2 * Copyright (C) 2000-2001 Michael Smith (msmith at xiph org)
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation, version 2.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 #include <vorbis/codec.h>
28 #define CHUNKSIZE 4096
30 struct vcedit_state_St {
39 vcedit_read_func read;
40 vcedit_write_func write;
44 unsigned char *mainbuf;
45 unsigned char *bookbuf;
48 const char *lasterror;
56 vcedit_state_new (void)
60 state = malloc (sizeof (vcedit_state));
64 memset (state, 0, sizeof (vcedit_state));
72 vcedit_error (vcedit_state *state)
74 return state->lasterror;
78 vcedit_comments (vcedit_state *state)
84 vcedit_clear_internals (vcedit_state *state)
89 vorbis_comment_clear (state->vc);
94 ogg_stream_clear (state->os);
99 ogg_sync_clear (state->oy);
103 free (state->vendor);
104 free (state->mainbuf);
105 free (state->bookbuf);
108 vorbis_info_clear (state->vi);
112 tmp = state->lasterror;
113 memset (state, 0, sizeof (vcedit_state));
114 state->lasterror = tmp;
118 vcedit_state_ref (vcedit_state *state)
124 vcedit_state_unref (vcedit_state *state)
128 if (!state->refcount) {
129 vcedit_clear_internals (state);
134 /* Next two functions pulled straight from libvorbis, apart from one change
135 * - we don't want to overwrite the vendor string.
138 _v_writestring (oggpack_buffer *o, char *s, int len)
141 oggpack_write (o, *s++, 8);
146 _commentheader_out (vorbis_comment *vc, char *vendor, ogg_packet *op)
150 oggpack_writeinit (&opb);
153 oggpack_write (&opb, 0x03, 8);
154 _v_writestring (&opb, "vorbis", 6);
157 oggpack_write (&opb, strlen (vendor), 32);
158 _v_writestring (&opb, vendor, strlen (vendor));
161 oggpack_write (&opb, vc->comments, 32);
166 for (i = 0; i < vc->comments; i++) {
167 if (vc->user_comments[i]) {
168 oggpack_write (&opb, vc->comment_lengths[i], 32);
169 _v_writestring (&opb, vc->user_comments[i],
170 vc->comment_lengths[i]);
172 oggpack_write (&opb, 0, 32);
176 oggpack_write (&opb, 1, 1);
178 op->packet = _ogg_malloc (oggpack_bytes (&opb));
179 memcpy (op->packet, opb.buffer, oggpack_bytes (&opb));
181 op->bytes = oggpack_bytes (&opb);
186 oggpack_writeclear (&opb);
192 _blocksize (vcedit_state *s, ogg_packet *p)
194 int this = vorbis_packet_blocksize (s->vi, p);
195 int ret = (this + s->prevW) / 4;
208 _fetch_next_packet (vcedit_state *s, ogg_packet *p, ogg_page *page)
213 result = ogg_stream_packetout (s->os, p);
221 while (ogg_sync_pageout (s->oy, page) <= 0) {
222 buffer = ogg_sync_buffer (s->oy, CHUNKSIZE);
223 bytes = s->read (buffer, 1, CHUNKSIZE, s->in);
224 ogg_sync_wrote (s->oy, bytes);
230 if (ogg_page_eos (page))
232 else if (ogg_page_serialno (page) != s->serial) {
238 ogg_stream_pagein (s->os, page);
240 return _fetch_next_packet (s, p, page);
244 vcedit_open_callbacks (vcedit_state *state, void *in,
245 vcedit_read_func read_func,
246 vcedit_write_func write_func)
252 ogg_packet header_main, header_comments, header_codebooks;
256 state->read = read_func;
257 state->write = write_func;
259 state->oy = malloc (sizeof (ogg_sync_state));
260 ogg_sync_init (state->oy);
263 buffer = ogg_sync_buffer (state->oy, CHUNKSIZE);
264 bytes = state->read (buffer, 1, CHUNKSIZE, state->in);
266 ogg_sync_wrote (state->oy, bytes);
268 if (ogg_sync_pageout (state->oy, &og) == 1)
271 /* Bail if we don't find data in the first 40 kB */
272 if (chunks++ >= 10) {
273 if (bytes < CHUNKSIZE)
274 state->lasterror = "Input truncated or empty.";
276 state->lasterror = "Input is not an Ogg bitstream.";
282 state->serial = ogg_page_serialno (&og);
284 state->os = malloc (sizeof (ogg_stream_state));
285 ogg_stream_init (state->os, state->serial);
287 state->vi = malloc (sizeof (vorbis_info));
288 vorbis_info_init (state->vi);
290 state->vc = malloc (sizeof (vorbis_comment));
291 vorbis_comment_init (state->vc);
293 if (ogg_stream_pagein (state->os, &og) < 0) {
294 state->lasterror = "Error reading first page of Ogg bitstream.";
298 if (ogg_stream_packetout (state->os, &header_main) != 1) {
299 state->lasterror = "Error reading initial header packet.";
303 if (vorbis_synthesis_headerin (state->vi, state->vc, &header_main) < 0) {
304 state->lasterror = "Ogg bitstream does not contain vorbis data.";
308 state->mainlen = header_main.bytes;
309 state->mainbuf = malloc (state->mainlen);
310 memcpy (state->mainbuf, header_main.packet, header_main.bytes);
313 header = &header_comments;
317 int result = ogg_sync_pageout (state->oy, &og);
320 break; /* Too little data so far */
323 ogg_stream_pagein (state->os, &og);
326 result = ogg_stream_packetout (state->os, header);
332 state->lasterror = "Corrupt secondary header.";
336 vorbis_synthesis_headerin (state->vi, state->vc, header);
339 state->booklen = header->bytes;
340 state->bookbuf = malloc (state->booklen);
341 memcpy (state->bookbuf, header->packet, header->bytes);
345 header = &header_codebooks;
350 buffer = ogg_sync_buffer (state->oy, CHUNKSIZE);
351 bytes = state->read (buffer, 1, CHUNKSIZE, state->in);
353 if (bytes == 0 && i < 2) {
354 state->lasterror = "EOF before end of vorbis headers.";
358 ogg_sync_wrote (state->oy, bytes);
361 /* Copy the vendor tag */
362 state->vendor = strdup (state->vc->vendor);
364 /* Headers are done! */
368 vcedit_clear_internals (state);
374 vcedit_write (vcedit_state *state, void *out)
376 ogg_stream_state streamout;
377 ogg_packet header_main, header_comments, header_codebooks, op;
378 ogg_page ogout, ogin;
379 ogg_int64_t granpos = 0;
380 int result, bytes, needflush = 0, needout = 0;
385 state->extrapage = 0;
387 header_main.bytes = state->mainlen;
388 header_main.packet = state->mainbuf;
389 header_main.b_o_s = 1;
390 header_main.e_o_s = 0;
391 header_main.granulepos = 0;
393 header_codebooks.bytes = state->booklen;
394 header_codebooks.packet = state->bookbuf;
395 header_codebooks.b_o_s = 0;
396 header_codebooks.e_o_s = 0;
397 header_codebooks.granulepos = 0;
399 ogg_stream_init (&streamout, state->serial);
401 _commentheader_out (state->vc, state->vendor, &header_comments);
403 ogg_stream_packetin (&streamout, &header_main);
404 ogg_stream_packetin (&streamout, &header_comments);
405 ogg_stream_packetin (&streamout, &header_codebooks);
407 while ((result = ogg_stream_flush (&streamout, &ogout))) {
408 tmp = state->write (ogout.header, 1, ogout.header_len, out);
409 if (tmp != (size_t) ogout.header_len)
412 tmp = state->write (ogout.body, 1, ogout.body_len, out);
413 if (tmp != (size_t) ogout.body_len)
417 while (_fetch_next_packet (state, &op, &ogin)) {
420 size = _blocksize (state, &op);
424 if (ogg_stream_flush (&streamout, &ogout)) {
425 tmp = state->write (ogout.header, 1, ogout.header_len, out);
426 if (tmp != (size_t) ogout.header_len)
429 tmp = state->write (ogout.body, 1, ogout.body_len, out);
430 if (tmp != (size_t) ogout.body_len)
433 } else if (needout) {
434 if (ogg_stream_pageout (&streamout, &ogout)) {
435 tmp = state->write (ogout.header, 1, ogout.header_len, out);
436 if (tmp != (size_t) ogout.header_len)
439 tmp = state->write (ogout.body, 1, ogout.body_len, out);
440 if (tmp != (size_t) ogout.body_len)
445 needflush = needout = 0;
447 if (op.granulepos == -1) {
448 op.granulepos = granpos;
449 ogg_stream_packetin (&streamout, &op);
451 /* granulepos is set, validly. Use it, and force a flush to
452 * account for shortened blocks (vcut) when appropriate
454 if (granpos > op.granulepos) {
455 granpos = op.granulepos;
456 ogg_stream_packetin (&streamout, &op);
459 ogg_stream_packetin (&streamout, &op);
467 while (ogg_stream_flush (&streamout, &ogout)) {
468 tmp = state->write (ogout.header, 1, ogout.header_len, out);
469 if (tmp != (size_t) ogout.header_len)
472 tmp = state->write (ogout.body, 1, ogout.body_len, out);
473 if (tmp != (size_t) ogout.body_len)
477 if (state->extrapage) {
478 tmp = state->write (ogin.header, 1, ogin.header_len, out);
479 if (tmp != (size_t) ogin.header_len)
482 tmp = state->write (ogin.body, 1, ogin.body_len, out);
483 if (tmp != (size_t) ogin.body_len)
487 /* clear it, because not all paths to here do */
490 while (!state->eosin) { /* We reached eos, not eof */
491 /* We copy the rest of the stream (other logical streams)
492 * through, a page at a time.
495 result = ogg_sync_pageout (state->oy, &ogout);
501 state->lasterror = "Corrupt or missing data, continuing...";
503 /* Don't bother going through the rest, we can just
504 * write the page out now
506 tmp = state->write (ogout.header,1,ogout.header_len, out);
507 if (tmp != (size_t) ogout.header_len)
510 tmp = state->write (ogout.body,1,ogout.body_len, out);
511 if (tmp != (size_t) ogout.body_len)
516 buffer = ogg_sync_buffer (state->oy, CHUNKSIZE);
517 bytes = state->read (buffer, 1, CHUNKSIZE, state->in);
518 ogg_sync_wrote (state->oy, bytes);
527 ogg_stream_clear (&streamout);
529 /* We don't ogg_packet_clear() this, because the memory was
530 * allocated in _commentheader_out(), so we mirror that here
532 _ogg_free (header_comments.packet);
534 free (state->mainbuf);
535 free (state->bookbuf);
537 state->mainbuf = state->bookbuf = NULL;
540 state->lasterror = "Error writing stream to output. "
541 "Output stream may be corrupted or truncated.";