X-Git-Url: http://git.code-monkey.de/?p=ruby-vorbistagger.git;a=blobdiff_plain;f=ext%2Fvcedit.c;h=832f417b2b30519cba6661b3dfb063c52a43f925;hp=c7bd8e75becb618d5ace62fd212f33db2001ca0b;hb=b1c729d53273c482052dfc99e197463b123e389e;hpb=310e91e26bebf168b6081e91da123b4a8cfb9b67 diff --git a/ext/vcedit.c b/ext/vcedit.c index c7bd8e7..832f417 100644 --- a/ext/vcedit.c +++ b/ext/vcedit.c @@ -17,11 +17,15 @@ */ #include +#include #include #include #include +#include +#include #include #include +#include #include "vcedit.h" @@ -30,30 +34,52 @@ struct vcedit_state_St { int refcount; - ogg_sync_state *oy; - ogg_stream_state *os; + ogg_sync_state oy; + ogg_stream_state os; - vorbis_comment *vc; - vorbis_info *vi; + vorbis_comment vc; + vorbis_info vi; - vcedit_read_func read; - vcedit_write_func write; + char filename[PATH_MAX]; - void *in; + FILE *in; + bool opened; long serial; unsigned char *mainbuf; unsigned char *bookbuf; int mainlen; int booklen; - const char *lasterror; char *vendor; int prevW; int extrapage; int eosin; }; +static void +vcedit_state_free (vcedit_state *state) +{ + free (state->mainbuf); + free (state->bookbuf); + free (state->vendor); + + if (state->in) { + fclose (state->in); + state->in = NULL; + } + + free (state); +} + +static bool +vcedit_state_init (vcedit_state *state) +{ + state->refcount = 1; + + return true; +} + vcedit_state * -vcedit_state_new (void) +vcedit_state_new (const char *filename) { vcedit_state *state; @@ -63,55 +89,45 @@ vcedit_state_new (void) memset (state, 0, sizeof (vcedit_state)); - state->refcount = 1; + if (!vcedit_state_init (state)) { + vcedit_state_free (state); + return NULL; + } - return state; -} + snprintf (state->filename, sizeof (state->filename), + "%s", filename); -const char * -vcedit_error (vcedit_state *state) -{ - return state->lasterror; + return state; } vorbis_comment * vcedit_comments (vcedit_state *state) { - return state->vc; + return state->opened ? &state->vc : NULL; } static void vcedit_clear_internals (vcedit_state *state) { - const char *tmp; + ogg_stream_clear (&state->os); + ogg_sync_clear (&state->oy); - if (state->vc) { - vorbis_comment_clear (state->vc); - free (state->vc); - } - - if (state->os) { - ogg_stream_clear (state->os); - free (state->os); - } - - if (state->oy) { - ogg_sync_clear (state->oy); - free (state->oy); - } + vorbis_info_clear (&state->vi); + vorbis_comment_clear (&state->vc); free (state->vendor); + state->vendor = NULL; + free (state->mainbuf); - free (state->bookbuf); + state->mainbuf = NULL; + state->mainlen = 0; - if (state->vi) { - vorbis_info_clear (state->vi); - free (state->vi); - } + free (state->bookbuf); + state->bookbuf = NULL; + state->booklen = 0; - tmp = state->lasterror; - memset (state, 0, sizeof (vcedit_state)); - state->lasterror = tmp; + state->serial = 0; + state->opened = false; } void @@ -123,12 +139,13 @@ vcedit_state_ref (vcedit_state *state) void vcedit_state_unref (vcedit_state *state) { - state->refcount--; + if (--state->refcount) + return; - if (!state->refcount) { + if (state->opened) vcedit_clear_internals (state); - free (state); - } + + vcedit_state_free (state); } /* Next two functions pulled straight from libvorbis, apart from one change @@ -145,6 +162,8 @@ _v_writestring (oggpack_buffer *o, char *s, int len) static int _commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op) { + int i; + oggpack_buffer opb; oggpack_writeinit (&opb); @@ -160,16 +179,13 @@ _commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op) /* comments */ oggpack_write (&opb, vc->comments, 32); - if (vc->comments) { - int i; - - for (i = 0; i < vc->comments; i++) { - if (vc->user_comments[i]) { - oggpack_write (&opb, vc->comment_lengths[i], 32); - _v_writestring (&opb, vc->user_comments[i], - vc->comment_lengths[i]); - } else - oggpack_write (&opb, 0, 32); + for (i = 0; i < vc->comments; i++) { + if (!vc->user_comments[i]) + oggpack_write (&opb, 0, 32); + else { + oggpack_write (&opb, vc->comment_lengths[i], 32); + _v_writestring (&opb, vc->user_comments[i], + vc->comment_lengths[i]); } } @@ -191,13 +207,12 @@ _commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op) static int _blocksize (vcedit_state *s, ogg_packet *p) { - int this = vorbis_packet_blocksize (s->vi, p); - int ret = (this + s->prevW) / 4; + int this, ret = 0; - if (!s->prevW) { - s->prevW = this; - return 0; - } + this = vorbis_packet_blocksize (&s->vi, p); + + if (s->prevW) + ret = (this + s->prevW) / 4; s->prevW = this; @@ -210,7 +225,7 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page) char *buffer; int result, bytes; - result = ogg_stream_packetout (s->os, p); + result = ogg_stream_packetout (&s->os, p); if (result > 0) return 1; @@ -218,10 +233,10 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page) if (s->eosin) return 0; - while (ogg_sync_pageout (s->oy, page) <= 0) { - buffer = ogg_sync_buffer (s->oy, CHUNKSIZE); - bytes = s->read (buffer, 1, CHUNKSIZE, s->in); - ogg_sync_wrote (s->oy, bytes); + while (ogg_sync_pageout (&s->oy, page) <= 0) { + buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE); + bytes = fread (buffer, 1, CHUNKSIZE, s->in); + ogg_sync_wrote (&s->oy, bytes); if (!bytes) return 0; @@ -235,16 +250,15 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page) return 0; } - ogg_stream_pagein (s->os, page); + ogg_stream_pagein (&s->os, page); return _fetch_next_packet (s, p, page); } -int -vcedit_open_callbacks (vcedit_state *state, void *in, - vcedit_read_func read_func, - vcedit_write_func write_func) +vcedit_error +vcedit_open (vcedit_state *state) { + vcedit_error ret; char *buffer; int bytes, i; int chunks = 0; @@ -252,56 +266,47 @@ vcedit_open_callbacks (vcedit_state *state, void *in, ogg_packet header_main, header_comments, header_codebooks; ogg_page og; - state->in = in; - state->read = read_func; - state->write = write_func; + state->in = fopen (state->filename, "rb"); + if (!state->in) + return VCEDIT_ERR_OPEN; - state->oy = malloc (sizeof (ogg_sync_state)); - ogg_sync_init (state->oy); + ogg_sync_init (&state->oy); while (1) { - buffer = ogg_sync_buffer (state->oy, CHUNKSIZE); - bytes = state->read (buffer, 1, CHUNKSIZE, state->in); + buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE); + bytes = fread (buffer, 1, CHUNKSIZE, state->in); - ogg_sync_wrote (state->oy, bytes); + ogg_sync_wrote (&state->oy, bytes); - if (ogg_sync_pageout (state->oy, &og) == 1) + if (ogg_sync_pageout (&state->oy, &og) == 1) break; /* Bail if we don't find data in the first 40 kB */ if (chunks++ >= 10) { - if (bytes < CHUNKSIZE) - state->lasterror = "Input truncated or empty."; - else - state->lasterror = "Input is not an Ogg bitstream."; + ogg_sync_clear (&state->oy); - goto err; + return VCEDIT_ERR_INVAL; } } state->serial = ogg_page_serialno (&og); - state->os = malloc (sizeof (ogg_stream_state)); - ogg_stream_init (state->os, state->serial); - - state->vi = malloc (sizeof (vorbis_info)); - vorbis_info_init (state->vi); - - state->vc = malloc (sizeof (vorbis_comment)); - vorbis_comment_init (state->vc); + ogg_stream_init (&state->os, state->serial); + vorbis_info_init (&state->vi); + vorbis_comment_init (&state->vc); - if (ogg_stream_pagein (state->os, &og) < 0) { - state->lasterror = "Error reading first page of Ogg bitstream."; + if (ogg_stream_pagein (&state->os, &og) < 0) { + ret = VCEDIT_ERR_INVAL; goto err; } - if (ogg_stream_packetout (state->os, &header_main) != 1) { - state->lasterror = "Error reading initial header packet."; + if (ogg_stream_packetout (&state->os, &header_main) != 1) { + ret = VCEDIT_ERR_INVAL; goto err; } - if (vorbis_synthesis_headerin (state->vi, state->vc, &header_main) < 0) { - state->lasterror = "Ogg bitstream does not contain vorbis data."; + if (vorbis_synthesis_headerin (&state->vi, &state->vc, &header_main) < 0) { + ret = VCEDIT_ERR_INVAL; goto err; } @@ -314,26 +319,26 @@ vcedit_open_callbacks (vcedit_state *state, void *in, while (i < 2) { while (i < 2) { - int result = ogg_sync_pageout (state->oy, &og); + int result = ogg_sync_pageout (&state->oy, &og); if (!result) break; /* Too little data so far */ if (result == 1) { - ogg_stream_pagein (state->os, &og); + ogg_stream_pagein (&state->os, &og); while (i < 2) { - result = ogg_stream_packetout (state->os, header); + result = ogg_stream_packetout (&state->os, header); if (!result) break; if (result == -1) { - state->lasterror = "Corrupt secondary header."; + ret = VCEDIT_ERR_INVAL; goto err; } - vorbis_synthesis_headerin (state->vi, state->vc, header); + vorbis_synthesis_headerin (&state->vi, &state->vc, header); if (i == 1) { state->booklen = header->bytes; @@ -347,42 +352,62 @@ vcedit_open_callbacks (vcedit_state *state, void *in, } } - buffer = ogg_sync_buffer (state->oy, CHUNKSIZE); - bytes = state->read (buffer, 1, CHUNKSIZE, state->in); + buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE); + bytes = fread (buffer, 1, CHUNKSIZE, state->in); if (bytes == 0 && i < 2) { - state->lasterror = "EOF before end of vorbis headers."; + ret = VCEDIT_ERR_INVAL; goto err; } - ogg_sync_wrote (state->oy, bytes); + ogg_sync_wrote (&state->oy, bytes); } /* Copy the vendor tag */ - state->vendor = strdup (state->vc->vendor); + state->vendor = strdup (state->vc.vendor); /* Headers are done! */ - return 0; + state->opened = true; + + return VCEDIT_ERR_SUCCESS; err: vcedit_clear_internals (state); - return -1; + return ret; } -int -vcedit_write (vcedit_state *state, void *out) +vcedit_error +vcedit_write (vcedit_state *state) { ogg_stream_state streamout; ogg_packet header_main, header_comments, header_codebooks, op; ogg_page ogout, ogin; ogg_int64_t granpos = 0; - int result, bytes, needflush = 0, needout = 0; - char *buffer; + FILE *out; + char *buffer, tmpfile[PATH_MAX]; + int s, result, bytes, needflush = 0, needout = 0; size_t tmp; - state->eosin = 0; - state->extrapage = 0; + if (!state->opened) + return VCEDIT_ERR_INVAL; + + strcpy (tmpfile, state->filename); + strcat (tmpfile, ".XXXXXX"); + + s = mkstemp (tmpfile); + if (s == -1) + return VCEDIT_ERR_TMPFILE; + + out = fdopen (s, "wb"); + if (!out) { + unlink (tmpfile); + close (s); + + return VCEDIT_ERR_TMPFILE; + } + + state->prevW = state->extrapage = state->eosin = 0; header_main.bytes = state->mainlen; header_main.packet = state->mainbuf; @@ -398,18 +423,18 @@ vcedit_write (vcedit_state *state, void *out) ogg_stream_init (&streamout, state->serial); - _commentheader_out (state->vc, state->vendor, &header_comments); + _commentheader_out (&state->vc, state->vendor, &header_comments); ogg_stream_packetin (&streamout, &header_main); ogg_stream_packetin (&streamout, &header_comments); ogg_stream_packetin (&streamout, &header_codebooks); while ((result = ogg_stream_flush (&streamout, &ogout))) { - tmp = state->write (ogout.header, 1, ogout.header_len, out); + tmp = fwrite (ogout.header, 1, ogout.header_len, out); if (tmp != (size_t) ogout.header_len) goto cleanup; - tmp = state->write (ogout.body, 1, ogout.body_len, out); + tmp = fwrite (ogout.body, 1, ogout.body_len, out); if (tmp != (size_t) ogout.body_len) goto cleanup; } @@ -422,21 +447,21 @@ vcedit_write (vcedit_state *state, void *out) if (needflush) { if (ogg_stream_flush (&streamout, &ogout)) { - tmp = state->write (ogout.header, 1, ogout.header_len, out); + tmp = fwrite (ogout.header, 1, ogout.header_len, out); if (tmp != (size_t) ogout.header_len) goto cleanup; - tmp = state->write (ogout.body, 1, ogout.body_len, out); + tmp = fwrite (ogout.body, 1, ogout.body_len, out); if (tmp != (size_t) ogout.body_len) goto cleanup; } } else if (needout) { if (ogg_stream_pageout (&streamout, &ogout)) { - tmp = state->write (ogout.header, 1, ogout.header_len, out); + tmp = fwrite (ogout.header, 1, ogout.header_len, out); if (tmp != (size_t) ogout.header_len) goto cleanup; - tmp = state->write (ogout.body, 1, ogout.body_len, out); + tmp = fwrite (ogout.body, 1, ogout.body_len, out); if (tmp != (size_t) ogout.body_len) goto cleanup; } @@ -465,21 +490,21 @@ vcedit_write (vcedit_state *state, void *out) streamout.e_o_s = 1; while (ogg_stream_flush (&streamout, &ogout)) { - tmp = state->write (ogout.header, 1, ogout.header_len, out); + tmp = fwrite (ogout.header, 1, ogout.header_len, out); if (tmp != (size_t) ogout.header_len) goto cleanup; - tmp = state->write (ogout.body, 1, ogout.body_len, out); + tmp = fwrite (ogout.body, 1, ogout.body_len, out); if (tmp != (size_t) ogout.body_len) goto cleanup; } if (state->extrapage) { - tmp = state->write (ogin.header, 1, ogin.header_len, out); + tmp = fwrite (ogin.header, 1, ogin.header_len, out); if (tmp != (size_t) ogin.header_len) goto cleanup; - tmp = state->write (ogin.body, 1, ogin.body_len, out); + tmp = fwrite (ogin.body, 1, ogin.body_len, out); if (tmp != (size_t) ogin.body_len) goto cleanup; } @@ -492,30 +517,28 @@ vcedit_write (vcedit_state *state, void *out) * through, a page at a time. */ while (1) { - result = ogg_sync_pageout (state->oy, &ogout); + result = ogg_sync_pageout (&state->oy, &ogout); if (!result) break; - if (result < 0) - state->lasterror = "Corrupt or missing data, continuing..."; - else { + if (result >= 0) { /* Don't bother going through the rest, we can just * write the page out now */ - tmp = state->write (ogout.header,1,ogout.header_len, out); + tmp = fwrite (ogout.header, 1, ogout.header_len, out); if (tmp != (size_t) ogout.header_len) goto cleanup; - tmp = state->write (ogout.body,1,ogout.body_len, out); + tmp = fwrite (ogout.body, 1, ogout.body_len, out); if (tmp != (size_t) ogout.body_len) goto cleanup; } } - buffer = ogg_sync_buffer (state->oy, CHUNKSIZE); - bytes = state->read (buffer, 1, CHUNKSIZE, state->in); - ogg_sync_wrote (state->oy, bytes); + buffer = ogg_sync_buffer (&state->oy, CHUNKSIZE); + bytes = fread (buffer, 1, CHUNKSIZE, state->in); + ogg_sync_wrote (&state->oy, bytes); if (!bytes) { state->eosin = 1; @@ -523,6 +546,12 @@ vcedit_write (vcedit_state *state, void *out) } } + fclose (out); + fclose (state->in); + + unlink (state->filename); + rename (tmpfile, state->filename); + cleanup: ogg_stream_clear (&streamout); @@ -536,11 +565,11 @@ cleanup: state->mainbuf = state->bookbuf = NULL; - if (!state->eosin) { - state->lasterror = "Error writing stream to output. " - "Output stream may be corrupted or truncated."; - return -1; - } + if (!state->eosin) + return VCEDIT_ERR_INVAL; - return 0; + vcedit_clear_internals (state); + + return (vcedit_open (state) == VCEDIT_ERR_SUCCESS) ? + VCEDIT_ERR_SUCCESS : VCEDIT_ERR_REOPEN; }