X-Git-Url: http://git.code-monkey.de/?a=blobdiff_plain;f=ext%2Fvcedit.c;h=cf63e7487217c7a36fcb479590328488928c7c63;hb=d167e476209adeab15e067425b941509b1ed90f4;hp=b6c7dfd8d0d0db18e361b0fd79b37fa74a1664cb;hpb=fcd91ff1a3cc4a4f889abfd2af89119cbe262ad1;p=ruby-vorbistagger.git diff --git a/ext/vcedit.c b/ext/vcedit.c index b6c7dfd..cf63e74 100644 --- a/ext/vcedit.c +++ b/ext/vcedit.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -30,128 +31,125 @@ #define CHUNKSIZE 4096 -static int vcedit_open (vcedit_state *state); - struct vcedit_state_St { int refcount; - ogg_sync_state *oy; - ogg_stream_state *os; - - vorbis_comment *vc; - vorbis_info *vi; + ogg_sync_state oy; + ogg_stream_state os; - char filename[PATH_MAX]; + vorbis_comment vc; + vorbis_info vi; 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; + + char filename[0]; }; +static void +vcedit_state_free (vcedit_state *s) +{ + free (s->mainbuf); + free (s->bookbuf); + free (s->vendor); + + if (s->in) { + fclose (s->in); + s->in = NULL; + } + + free (s); +} + +static bool +vcedit_state_init (vcedit_state *s, const char *filename) +{ + s->refcount = 1; + + strcpy (s->filename, filename); + + return true; +} + vcedit_state * vcedit_state_new (const char *filename) { - vcedit_state *state; + vcedit_state *s; + size_t len; - state = malloc (sizeof (vcedit_state)); - if (!state) + len = strlen (filename); + if (len > PATH_MAX) return NULL; - memset (state, 0, sizeof (vcedit_state)); - - state->refcount = 1; - - snprintf (state->filename, sizeof (state->filename), - "%s", filename); + s= malloc (sizeof (vcedit_state) + len + 1); + if (!s) + return NULL; - state->in = fopen (state->filename, "rb"); + memset (s, 0, sizeof (vcedit_state)); - if (vcedit_open (state) < 0) { - free (state); + if (!vcedit_state_init (s, filename)) { + vcedit_state_free (s); return NULL; } - return state; -} - -const char * -vcedit_error (vcedit_state *state) -{ - return state->lasterror; + return s; } vorbis_comment * -vcedit_comments (vcedit_state *state) +vcedit_comments (vcedit_state *s) { - return state->vc; + return s->opened ? &s->vc : NULL; } static void -vcedit_clear_internals (vcedit_state *state) +vcedit_clear_internals (vcedit_state *s) { - if (state->vc) { - vorbis_comment_clear (state->vc); - free (state->vc); - state->vc = NULL; - } - - if (state->os) { - ogg_stream_clear (state->os); - free (state->os); - state->os = NULL; - } + ogg_stream_clear (&s->os); + ogg_sync_clear (&s->oy); - if (state->oy) { - ogg_sync_clear (state->oy); - free (state->oy); - state->oy = NULL; - } - - free (state->vendor); - state->vendor = NULL; + vorbis_info_clear (&s->vi); + vorbis_comment_clear (&s->vc); - free (state->mainbuf); - state->mainbuf = NULL; - state->mainlen = 0; + free (s->vendor); + s->vendor = NULL; - free (state->bookbuf); - state->bookbuf = NULL; - state->booklen = 0; + free (s->mainbuf); + s->mainbuf = NULL; + s->mainlen = 0; - if (state->vi) { - vorbis_info_clear (state->vi); - free (state->vi); - state->vi = NULL; - } + free (s->bookbuf); + s->bookbuf = NULL; + s->booklen = 0; - state->serial = 0; - state->prevW = state->extrapage = state->eosin = 0; + s->serial = 0; + s->opened = false; } void -vcedit_state_ref (vcedit_state *state) +vcedit_state_ref (vcedit_state *s) { - state->refcount++; + s->refcount++; } void -vcedit_state_unref (vcedit_state *state) +vcedit_state_unref (vcedit_state *s) { - state->refcount--; + if (--s->refcount) + return; - if (!state->refcount) { - fclose (state->in); - vcedit_clear_internals (state); - free (state); - } + if (s->opened) + vcedit_clear_internals (s); + + vcedit_state_free (s); } /* Next two functions pulled straight from libvorbis, apart from one change @@ -160,14 +158,15 @@ vcedit_state_unref (vcedit_state *state) static void _v_writestring (oggpack_buffer *o, char *s, int len) { - while (len--) { + while (len--) oggpack_write (o, *s++, 8); - } } static int _commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op) { + int i; + oggpack_buffer opb; oggpack_writeinit (&opb); @@ -183,16 +182,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]); } } @@ -214,13 +210,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; @@ -233,20 +228,19 @@ _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page) char *buffer; int result, bytes; - result = ogg_stream_packetout (s->os, p); - - if (result > 0) + result = ogg_stream_packetout (&s->os, p); + if (result == 1) return 1; if (s->eosin) return 0; - while (ogg_sync_pageout (s->oy, page) <= 0) { - buffer = ogg_sync_buffer (s->oy, CHUNKSIZE); + while (ogg_sync_pageout (&s->oy, page) != 1) { + buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE); bytes = fread (buffer, 1, CHUNKSIZE, s->in); - ogg_sync_wrote (s->oy, bytes); + ogg_sync_wrote (&s->oy, bytes); - if (!bytes) + if (!bytes && (feof (s->in) || ferror (s->in))) return 0; } @@ -258,137 +252,150 @@ _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); } -static int -vcedit_open (vcedit_state *state) +vcedit_error +vcedit_open (vcedit_state *s) { - char *buffer; - int bytes, i; - int chunks = 0; + vcedit_error ret; ogg_packet *header; ogg_packet header_main, header_comments, header_codebooks; ogg_page og; + char *buffer; + size_t bytes, total = 0; + int i = 0; - state->oy = malloc (sizeof (ogg_sync_state)); - ogg_sync_init (state->oy); - - while (1) { - buffer = ogg_sync_buffer (state->oy, CHUNKSIZE); - bytes = fread (buffer, 1, CHUNKSIZE, state->in); - - ogg_sync_wrote (state->oy, bytes); + s->in = fopen (s->filename, "rb"); + if (!s->in) + return VCEDIT_ERR_OPEN; - if (ogg_sync_pageout (state->oy, &og) == 1) - break; + ogg_sync_init (&s->oy); + do { /* 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."; + if (feof (s->in) || ferror (s->in) || total >= (CHUNKSIZE * 10)) { + ogg_sync_clear (&s->oy); - goto err; + return VCEDIT_ERR_INVAL; } - } - state->serial = ogg_page_serialno (&og); + buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE); + + bytes = fread (buffer, 1, CHUNKSIZE, s->in); + total += bytes; - state->os = malloc (sizeof (ogg_stream_state)); - ogg_stream_init (state->os, state->serial); + ogg_sync_wrote (&s->oy, bytes); + } while (ogg_sync_pageout (&s->oy, &og) != 1); - state->vi = malloc (sizeof (vorbis_info)); - vorbis_info_init (state->vi); + s->serial = ogg_page_serialno (&og); - state->vc = malloc (sizeof (vorbis_comment)); - vorbis_comment_init (state->vc); + ogg_stream_init (&s->os, s->serial); + vorbis_info_init (&s->vi); + vorbis_comment_init (&s->vc); - if (ogg_stream_pagein (state->os, &og) < 0) { - state->lasterror = "Error reading first page of Ogg bitstream."; + if (ogg_stream_pagein (&s->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 (&s->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 (&s->vi, &s->vc, &header_main) < 0) { + ret = VCEDIT_ERR_INVAL; goto err; } - state->mainlen = header_main.bytes; - state->mainbuf = malloc (state->mainlen); - memcpy (state->mainbuf, header_main.packet, header_main.bytes); + s->mainlen = header_main.bytes; + s->mainbuf = malloc (s->mainlen); + memcpy (s->mainbuf, header_main.packet, header_main.bytes); - i = 0; header = &header_comments; while (i < 2) { + if (feof (s->in) || ferror (s->in)) { + ret = VCEDIT_ERR_INVAL; + goto err; + } + while (i < 2) { - int result = ogg_sync_pageout (state->oy, &og); + int result; + result = ogg_sync_pageout (&s->oy, &og); if (!result) break; /* Too little data so far */ - if (result == 1) { - ogg_stream_pagein (state->os, &og); + if (result != 1) + continue; - while (i < 2) { - result = ogg_stream_packetout (state->os, header); + ogg_stream_pagein (&s->os, &og); - if (!result) - break; + while (i < 2) { + result = ogg_stream_packetout (&s->os, header); + if (!result) + break; - if (result == -1) { - state->lasterror = "Corrupt secondary header."; - goto err; - } - - vorbis_synthesis_headerin (state->vi, state->vc, header); + if (result != 1) { + ret = VCEDIT_ERR_INVAL; + goto err; + } - if (i == 1) { - state->booklen = header->bytes; - state->bookbuf = malloc (state->booklen); - memcpy (state->bookbuf, header->packet, header->bytes); - } + vorbis_synthesis_headerin (&s->vi, &s->vc, header); - i++; - header = &header_codebooks; + if (i++ == 1) { + s->booklen = header->bytes; + s->bookbuf = malloc (s->booklen); + memcpy (s->bookbuf, header->packet, header->bytes); } + + header = &header_codebooks; } } - buffer = ogg_sync_buffer (state->oy, CHUNKSIZE); - bytes = fread (buffer, 1, CHUNKSIZE, state->in); + buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE); - if (bytes == 0 && i < 2) { - state->lasterror = "EOF before end of vorbis headers."; - goto err; - } - - ogg_sync_wrote (state->oy, bytes); + bytes = fread (buffer, 1, CHUNKSIZE, s->in); + ogg_sync_wrote (&s->oy, bytes); } /* Copy the vendor tag */ - state->vendor = strdup (state->vc->vendor); + s->vendor = strdup (s->vc.vendor); /* Headers are done! */ - return 0; + s->opened = true; + + return VCEDIT_ERR_SUCCESS; err: - vcedit_clear_internals (state); + vcedit_clear_internals (s); - return -1; + return ret; } -int -vcedit_write (vcedit_state *state) +static int +write_data (const void *buf, size_t size, size_t nmemb, FILE *stream) +{ + while (nmemb > 0) { + size_t w; + + w = fwrite (buf, size, nmemb, stream); + if (!w && ferror (stream)) + return 0; + + nmemb -= w; + buf += size * w; + } + + return 1; +} + +vcedit_error +vcedit_write (vcedit_state *s) { ogg_stream_state streamout; ogg_packet header_main, header_comments, header_codebooks, op; @@ -396,85 +403,76 @@ vcedit_write (vcedit_state *state) ogg_int64_t granpos = 0; FILE *out; char *buffer, tmpfile[PATH_MAX]; - int s, result, bytes, needflush = 0, needout = 0; - size_t tmp; + int fd, result, bytes, needflush = 0, needout = 0; + + if (!s->opened) + return VCEDIT_ERR_INVAL; - strcpy (tmpfile, state->filename); + strcpy (tmpfile, s->filename); strcat (tmpfile, ".XXXXXX"); - s = mkstemp (tmpfile); - if (s == -1) { - state->lasterror = "Error writing stream to output. " - "Cannot open temporary file."; - return -1; - } + fd = mkstemp (tmpfile); + if (fd == -1) + return VCEDIT_ERR_TMPFILE; - out = fdopen (s, "wb"); + out = fdopen (fd, "wb"); if (!out) { unlink (tmpfile); - close (s); - state->lasterror = "Error writing stream to output. " - "Cannot open temporary file."; - return -1; + close (fd); + + return VCEDIT_ERR_TMPFILE; } - state->eosin = 0; - state->extrapage = 0; + s->prevW = s->extrapage = s->eosin = 0; - header_main.bytes = state->mainlen; - header_main.packet = state->mainbuf; + header_main.bytes = s->mainlen; + header_main.packet = s->mainbuf; header_main.b_o_s = 1; header_main.e_o_s = 0; header_main.granulepos = 0; - header_codebooks.bytes = state->booklen; - header_codebooks.packet = state->bookbuf; + header_codebooks.bytes = s->booklen; + header_codebooks.packet = s->bookbuf; header_codebooks.b_o_s = 0; header_codebooks.e_o_s = 0; header_codebooks.granulepos = 0; - ogg_stream_init (&streamout, state->serial); + ogg_stream_init (&streamout, s->serial); - _commentheader_out (state->vc, state->vendor, &header_comments); + _commentheader_out (&s->vc, s->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 = fwrite (ogout.header, 1, ogout.header_len, out); - if (tmp != (size_t) ogout.header_len) + if (!write_data (ogout.header, 1, ogout.header_len, out)) goto cleanup; - tmp = fwrite (ogout.body, 1, ogout.body_len, out); - if (tmp != (size_t) ogout.body_len) + if (!write_data (ogout.body, 1, ogout.body_len, out)) goto cleanup; } - while (_fetch_next_packet (state, &op, &ogin)) { + while (_fetch_next_packet (s, &op, &ogin)) { int size; - size = _blocksize (state, &op); + size = _blocksize (s, &op); granpos += size; if (needflush) { if (ogg_stream_flush (&streamout, &ogout)) { - tmp = fwrite (ogout.header, 1, ogout.header_len, out); - if (tmp != (size_t) ogout.header_len) + if (!write_data (ogout.header, 1, ogout.header_len, out)) goto cleanup; - tmp = fwrite (ogout.body, 1, ogout.body_len, out); - if (tmp != (size_t) ogout.body_len) + if (!write_data (ogout.body, 1, ogout.body_len, out)) goto cleanup; } } else if (needout) { if (ogg_stream_pageout (&streamout, &ogout)) { - tmp = fwrite (ogout.header, 1, ogout.header_len, out); - if (tmp != (size_t) ogout.header_len) + if (!write_data (ogout.header, 1, ogout.header_len, out)) goto cleanup; - tmp = fwrite (ogout.body, 1, ogout.body_len, out); - if (tmp != (size_t) ogout.body_len) + if (!write_data (ogout.body, 1, ogout.body_len, out)) goto cleanup; } } @@ -502,69 +500,57 @@ vcedit_write (vcedit_state *state) streamout.e_o_s = 1; while (ogg_stream_flush (&streamout, &ogout)) { - tmp = fwrite (ogout.header, 1, ogout.header_len, out); - if (tmp != (size_t) ogout.header_len) + if (!write_data (ogout.header, 1, ogout.header_len, out)) goto cleanup; - tmp = fwrite (ogout.body, 1, ogout.body_len, out); - if (tmp != (size_t) ogout.body_len) + if (!write_data (ogout.body, 1, ogout.body_len, out)) goto cleanup; } - if (state->extrapage) { - tmp = fwrite (ogin.header, 1, ogin.header_len, out); - if (tmp != (size_t) ogin.header_len) + if (s->extrapage) { + if (!write_data (ogin.header, 1, ogin.header_len, out)) goto cleanup; - tmp = fwrite (ogin.body, 1, ogin.body_len, out); - if (tmp != (size_t) ogin.body_len) + if (!write_data (ogin.body, 1, ogin.body_len, out)) goto cleanup; } /* clear it, because not all paths to here do */ - state->eosin = 0; + s->eosin = 0; - while (!state->eosin) { /* We reached eos, not eof */ + do { /* We copy the rest of the stream (other logical streams) * through, a page at a time. */ - while (1) { - result = ogg_sync_pageout (state->oy, &ogout); + while ((result = ogg_sync_pageout (&s->oy, &ogout))) { + if (result != 1) + continue; - if (!result) - break; - - if (result < 0) - state->lasterror = "Corrupt or missing data, continuing..."; - else { - /* Don't bother going through the rest, we can just - * write the page out now - */ - tmp = fwrite (ogout.header, 1, ogout.header_len, out); - if (tmp != (size_t) ogout.header_len) - goto cleanup; + /* Don't bother going through the rest, we can just + * write the page out now + */ + if (!write_data (ogout.header, 1, ogout.header_len, out)) + goto cleanup; - tmp = fwrite (ogout.body, 1, ogout.body_len, out); - if (tmp != (size_t) ogout.body_len) - goto cleanup; - } + if (!write_data (ogout.body, 1, ogout.body_len, out)) + goto cleanup; } - buffer = ogg_sync_buffer (state->oy, CHUNKSIZE); - bytes = fread (buffer, 1, CHUNKSIZE, state->in); - ogg_sync_wrote (state->oy, bytes); + buffer = ogg_sync_buffer (&s->oy, CHUNKSIZE); + bytes = fread (buffer, 1, CHUNKSIZE, s->in); + ogg_sync_wrote (&s->oy, bytes); - if (!bytes) { - state->eosin = 1; - break; - } - } + if (ferror (s->in)) + goto cleanup; + + s->eosin = !bytes && feof (s->in); + } while (!s->eosin); fclose (out); - fclose (state->in); + fclose (s->in); - unlink (state->filename); - rename (tmpfile, state->filename); + unlink (s->filename); + rename (tmpfile, s->filename); cleanup: ogg_stream_clear (&streamout); @@ -574,21 +560,16 @@ cleanup: */ _ogg_free (header_comments.packet); - free (state->mainbuf); - free (state->bookbuf); + free (s->mainbuf); + free (s->bookbuf); - state->mainbuf = state->bookbuf = NULL; + s->mainbuf = s->bookbuf = NULL; - if (!state->eosin) { - state->lasterror = "Error writing stream to output. " - "Output stream may be corrupted or truncated."; - return -1; - } + if (!s->eosin) + return VCEDIT_ERR_INVAL; - vcedit_clear_internals (state); + vcedit_clear_internals (s); - state->in = fopen (state->filename, "rb"); - vcedit_open (state); - - return 0; + return (vcedit_open (s) == VCEDIT_ERR_SUCCESS) ? + VCEDIT_ERR_SUCCESS : VCEDIT_ERR_REOPEN; }