1 /* This program is licensed under the GNU Library General Public License, version 2,
2 * a copy of which is included with this program (LICENCE.LGPL).
3 *
4 * (c) 2000-2001 Michael Smith <msmith@xiph.org>
5 *
6 *
7 * Comment editing backend, suitable for use by nice frontend interfaces.
8 *
9 * last modified: $Id$
10 */
11
12 /* Handle muxed streams and the Vorbis renormalization without having
13 * to understand remuxing:
14 * Linked list of buffers (buffer_chain). Start a link and whenever
15 * you encounter an unknown page from the current stream (ie we found
16 * its bos in the bos section) push it onto the current buffer. Whenever
17 * you encounter the stream being renormalized create a new link in the
18 * chain.
19 * On writing, write the contents of the first link before every Vorbis
20 * page written, and move to the next link. Assuming the Vorbis pages
21 * in match vorbis pages out, the order of pages from different logical
22 * streams will be unchanged.
23 * Special case: header. After writing the vorbis headers, and before
24 * starting renormalization, flush accumulated links (takes care of
25 * situations where number of secondary vorbis header pages changes due
26 * to remuxing. Similarly flush links at the end of renormalization
27 * and before the start of the next chain is written.
28 *
29 */
30
31 /* This file is taken from vorbis-tools SVN
32 * (http://svn.xiph.org/trunk/vorbis-tools/vorbiscomment) Rev. 16716 2009-11-22,
33 * modifications are marked with kid3. */
34 /* kid3 */
35 #include "oggflacconfig.h"
36 #ifdef HAVE_VORBIS
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <ogg/ogg.h>
47 #include <vorbis/codec.h>
48
49 #include "vcedit.h"
50 /* kid3 */
51 /*#include "vceditaux.h"*/
52 typedef struct vcedit_page_buffer {
53 char *data;
54 size_t data_len;
55 } vcedit_page_buffer;
56
57 typedef struct vcedit_buffer_chain {
58 struct vcedit_buffer_chain *next;
59 struct vcedit_page_buffer buffer;
60 } vcedit_buffer_chain;
61 /* kid3 */
62 /*#include "i18n.h"*/
63 #define _(str) ((char*)(str))
64
65
66 #define CHUNKSIZE 4096
67 #define BUFFERCHUNK CHUNKSIZE
68
69 /* Helper function, shouldn't need to call directly */
page_buffer_push(vcedit_buffer_chain * bufferlink,ogg_page * og)70 static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) {
71 int result=0;
72 char *tmp;
73 vcedit_page_buffer *buffer;
74
75 buffer = &bufferlink->buffer;
76 tmp = realloc(buffer->data,
77 buffer->data_len + og->header_len + og->body_len);
78 if(tmp) {
79 buffer->data = tmp;
80 memcpy(buffer->data + buffer->data_len, og->header,
81 og->header_len);
82 buffer->data_len += og->header_len;
83 memcpy(buffer->data + buffer->data_len, og->body,
84 og->body_len);
85 result = 1;
86 buffer->data_len += og->body_len;
87 } else {
88 result = -1;
89 }
90
91 return result;
92 }
93
94 /* Write and free the first link using callbacks */
buffer_chain_writelink(vcedit_state * state,void * out)95 static int buffer_chain_writelink(vcedit_state *state, void *out) {
96 int result = 0;
97 vcedit_buffer_chain *tmpchain;
98 vcedit_page_buffer *tmpbuffer;
99
100 tmpchain = state->sidebuf;
101 tmpbuffer = &tmpchain->buffer;
102 if(tmpbuffer->data_len)
103 {
104 if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) !=
105 (size_t) tmpbuffer->data_len)
106 result = -1;
107 else
108 result = 1;
109 }
110
111 free(tmpbuffer->data);
112 state->sidebuf = tmpchain->next;
113 free(tmpchain);
114 return result;
115 }
116
117
buffer_chain_newlink(vcedit_state * state)118 static int buffer_chain_newlink(vcedit_state *state) {
119 int result = 1;
120 vcedit_buffer_chain *bufferlink;
121
122 if(!state->sidebuf) {
123 state->sidebuf = malloc (sizeof *state->sidebuf);
124 if(state->sidebuf) {
125 bufferlink = state->sidebuf;
126 } else {
127 result = -1;
128 }
129 } else {
130 bufferlink=state->sidebuf;
131 while(bufferlink->next) {
132 bufferlink = bufferlink->next;
133 }
134 bufferlink->next = malloc (sizeof *bufferlink->next);
135 if(bufferlink->next) {
136 bufferlink = bufferlink->next;
137 } else {
138 result = -1;
139 }
140 }
141
142 if(result > 0 ) {
143 bufferlink->next = 0;
144 bufferlink->buffer.data = 0;
145 bufferlink->buffer.data_len = 0;
146 }
147 else
148 state->lasterror =
149 _("Couldn't get enough memory for input buffering.");
150
151 return result;
152 }
153
154
155 /* Push page onto the end of the buffer chain */
buffer_chain_push(vcedit_state * state,ogg_page * og)156 static int buffer_chain_push(vcedit_state *state, ogg_page *og) {
157 /* If there is no sidebuffer yet we need to create one, otherwise
158 * traverse to the last buffer and push the new page onto it. */
159 int result=1;
160 vcedit_buffer_chain *bufferlink;
161 if(!state->sidebuf) {
162 result = buffer_chain_newlink(state);
163 }
164
165 if(result > 0) {
166 bufferlink = state->sidebuf;
167 while(bufferlink->next) {
168 bufferlink = bufferlink->next;
169 }
170 result = page_buffer_push(bufferlink, og);
171 }
172
173 if(result < 0)
174 state->lasterror =
175 _("Couldn't get enough memory for input buffering.");
176
177 return result;
178 }
179
180
181
vcedit_supported_stream(vcedit_state * state,ogg_page * og)182 static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) {
183 ogg_stream_state os;
184 vorbis_info vi;
185 vorbis_comment vc;
186 ogg_packet header;
187 int result = 0;
188
189 ogg_stream_init(&os, ogg_page_serialno(og));
190 vorbis_info_init(&vi);
191 vorbis_comment_init(&vc);
192
193 if( !ogg_page_bos(og) )
194 result = -1;
195
196 if(result >= 0 && ogg_stream_pagein(&os, og) < 0)
197 {
198 state->lasterror =
199 _("Error reading first page of Ogg bitstream.");
200 result = -1;
201 }
202
203 if(result >= 0 && ogg_stream_packetout(&os, &header) != 1)
204 {
205 state->lasterror = _("Error reading initial header packet.");
206 result = -1;
207 }
208
209 if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0)
210 {
211 result = 1;
212 } else {
213 /* Not vorbis, may eventually become a chain of checks (Speex,
214 * Theora), but for the moment return 0, bos scan will push
215 * the current page onto the buffer.
216 */
217 }
218
219 ogg_stream_clear(&os);
220 vorbis_info_clear(&vi);
221 vorbis_comment_clear(&vc);
222 return result;
223 }
224
225
vcedit_contains_serial(vcedit_state * state,int serialno)226 static int vcedit_contains_serial (vcedit_state *state, int serialno) {
227 int result = 0;
228 size_t count;
229 for( count=0; count < state->serials.streams_len; count++ ) {
230 if ( *(state->serials.streams + count ) == serialno )
231 result = 1;
232 }
233
234 return result;
235 }
236
237
vcedit_add_serial(vcedit_state * state,long serial)238 static int vcedit_add_serial (vcedit_state *state, long serial) {
239 int result = 0;
240 long *tmp;
241
242
243 if( vcedit_contains_serial(state, serial) )
244 {
245 result = 1;
246 } else {
247 tmp = realloc(state->serials.streams,
248 (state->serials.streams_len + 1) * sizeof *tmp);
249 if(tmp) {
250 state->serials.streams = tmp;
251 *(state->serials.streams +
252 state->serials.streams_len) = serial;
253 state->serials.streams_len += 1;
254 result = 1;
255 } else {
256 state->lasterror =
257 _("Couldn't get enough memory to register new stream serial number.");
258 result = -1;
259 }
260 }
261 return result;
262 }
263
264
265 /* For the benefit of the secondary header read only. Quietly creates
266 * newlinks and pushes pages onto the buffer in the right way */
vcedit_target_pageout(vcedit_state * state,ogg_page * og)267 static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) {
268 int result = 0;
269 int pageout_result;
270 pageout_result = ogg_sync_pageout(state->oy, og);
271 if(pageout_result > 0)
272 {
273 if(state->serial == ogg_page_serialno(og))
274 result = buffer_chain_newlink(state);
275 else
276 result = buffer_chain_push(state, og);
277 } else if (pageout_result < 0) {
278 /* Vorbis comment traditionally ignores the not-synced
279 * error from pageout, so give it a different code. */
280 result = -2;
281 }
282 return result;
283 }
284
285
286 /* (I'm paranoid about memset(x,0,len) not giving null pointers */
vcedit_new_state(void)287 vcedit_state *vcedit_new_state(void) {
288 vcedit_state *state = malloc(sizeof(vcedit_state));
289 if(state) {
290 memset(state, 0, sizeof(vcedit_state));
291 state->sidebuf = 0;
292 state->serials.streams = 0;
293 state->serials.streams_len = 0;
294 }
295 return state;
296 }
297
vcedit_error(vcedit_state * state)298 char *vcedit_error(vcedit_state *state) {
299 return state->lasterror;
300 }
301
vcedit_comments(vcedit_state * state)302 vorbis_comment *vcedit_comments(vcedit_state *state) {
303 return state->vc;
304 }
305
vcedit_clear_internals(vcedit_state * state)306 static void vcedit_clear_internals(vcedit_state *state) {
307 char *tmp;
308 if(state->vc) {
309 vorbis_comment_clear(state->vc);
310 free(state->vc);
311 }
312 if(state->os) {
313 ogg_stream_clear(state->os);
314 free(state->os);
315 }
316 if(state->oy) {
317 ogg_sync_clear(state->oy);
318 free(state->oy);
319 }
320 if(state->serials.streams_len) {
321 free(state->serials.streams);
322 state->serials.streams_len = 0;
323 state->serials.streams = 0;
324 }
325 while(state->sidebuf) {
326 vcedit_buffer_chain *tmpbuffer;
327 tmpbuffer = state->sidebuf;
328 state->sidebuf = tmpbuffer->next;
329 free(tmpbuffer->buffer.data);
330 free(tmpbuffer);
331 }
332 if(state->vendor)
333 free(state->vendor);
334 if(state->mainbuf)
335 free(state->mainbuf);
336 if(state->bookbuf)
337 free(state->bookbuf);
338 if(state->vi) {
339 vorbis_info_clear(state->vi);
340 free(state->vi);
341 }
342
343 tmp = state->lasterror;
344 memset(state, 0, sizeof(*state));
345 state->lasterror = tmp;
346 }
347
vcedit_clear(vcedit_state * state)348 void vcedit_clear(vcedit_state *state)
349 {
350 if(state)
351 {
352 vcedit_clear_internals(state);
353 free(state);
354 }
355 }
356
357 /* Next two functions pulled straight from libvorbis, apart from one change
358 * - we don't want to overwrite the vendor string.
359 */
360 /* kid3: changed type from char* to const char* */
_v_writestring(oggpack_buffer * o,const char * s,int len)361 static void _v_writestring(oggpack_buffer *o, const char *s, int len)
362 {
363 while(len--)
364 {
365 oggpack_write(o,*s++,8);
366 }
367 }
368
_commentheader_out(vorbis_comment * vc,char * vendor,ogg_packet * op)369 static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
370 {
371 oggpack_buffer opb;
372
373 oggpack_writeinit(&opb);
374
375 /* preamble */
376 oggpack_write(&opb,0x03,8);
377 _v_writestring(&opb,"vorbis", 6);
378
379 /* vendor */
380 oggpack_write(&opb,strlen(vendor),32);
381 _v_writestring(&opb,vendor, strlen(vendor));
382
383 /* comments */
384 oggpack_write(&opb,vc->comments,32);
385 if(vc->comments){
386 int i;
387 for(i=0;i<vc->comments;i++){
388 if(vc->user_comments[i]){
389 oggpack_write(&opb,vc->comment_lengths[i],32);
390 _v_writestring(&opb,vc->user_comments[i],
391 vc->comment_lengths[i]);
392 }else{
393 oggpack_write(&opb,0,32);
394 }
395 }
396 }
397 oggpack_write(&opb,1,1);
398
399 op->packet = malloc(oggpack_bytes(&opb));
400 memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
401
402 op->bytes=oggpack_bytes(&opb);
403 op->b_o_s=0;
404 op->e_o_s=0;
405 op->granulepos=0;
406
407 oggpack_writeclear(&opb);
408 return 0;
409 }
410
_blocksize(vcedit_state * s,ogg_packet * p)411 static int _blocksize(vcedit_state *s, ogg_packet *p)
412 {
413 int this = vorbis_packet_blocksize(s->vi, p);
414 int ret = (this + s->prevW)/4;
415
416 if(!s->prevW)
417 {
418 s->prevW = this;
419 return 0;
420 }
421
422 s->prevW = this;
423 return ret;
424 }
425
_fetch_next_packet(vcedit_state * s,ogg_packet * p,ogg_page * page)426 static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
427 {
428 int result;
429 char *buffer;
430 int bytes;
431 int serialno;
432
433 result = ogg_stream_packetout(s->os, p);
434
435 if(result > 0)
436 return 1;
437 else {
438 while(1) {
439 if(s->eosin)
440 return 0;
441
442 while(ogg_sync_pageout(s->oy, page) <= 0)
443 {
444 buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
445 bytes = s->read(buffer,1, CHUNKSIZE, s->in);
446 ogg_sync_wrote(s->oy, bytes);
447 if(bytes == 0)
448 return 0;
449 }
450
451 serialno = ogg_page_serialno(page);
452 if(ogg_page_serialno(page) != s->serial)
453 {
454 if(vcedit_contains_serial(s, serialno)) {
455 result = buffer_chain_push(s, page);
456 if(result < 0)
457 return result;
458 }
459 else
460 {
461 s->eosin = 1;
462 s->extrapage = 1;
463 return 0;
464 }
465 }
466 else
467 {
468 ogg_stream_pagein(s->os, page);
469 result = buffer_chain_newlink(s);
470 if (result < 0)
471 return result;
472
473 if(ogg_page_eos(page))
474 s->eosin = 1;
475 }
476 result = ogg_stream_packetout(s->os, p);
477 if(result > 0)
478 return 1;
479 }
480 /* Here == trouble */
481 return 0;
482 }
483 }
484
485 /* kid3 vcedit_open() removed */
486
vcedit_open_callbacks(vcedit_state * state,void * in,vcedit_read_func read_func,vcedit_write_func write_func)487 int vcedit_open_callbacks(vcedit_state *state, void *in,
488 vcedit_read_func read_func, vcedit_write_func write_func)
489 {
490
491 char *buffer;
492 int bytes,i;
493 int chunks = 0;
494 int read_bos, test_supported, page_pending;
495 int have_vorbis;
496 size_t vendor_size;
497 ogg_packet *header;
498 ogg_packet header_main;
499 ogg_packet header_comments;
500 ogg_packet header_codebooks;
501 ogg_page og;
502
503 /* kid3, to fix LLVM scan-build error "Assigned value is garbage or undefined"
504 in line "state->mainlen = header_main.bytes;" */
505 memset(&header_main, 0, sizeof(header_main));
506
507 state->in = in;
508 state->read = read_func;
509 state->write = write_func;
510
511 state->oy = malloc(sizeof(ogg_sync_state));
512 ogg_sync_init(state->oy);
513
514 while(1)
515 {
516 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
517 bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
518
519 ogg_sync_wrote(state->oy, bytes);
520
521 if(ogg_sync_pageout(state->oy, &og) == 1)
522 break;
523
524 if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */
525 {
526 if(bytes<CHUNKSIZE)
527 state->lasterror = _("Input truncated or empty.");
528 else
529 state->lasterror = _("Input is not an Ogg bitstream.");
530 goto err;
531 }
532 }
533
534 /* BOS loop, starting with a loaded ogg page. */
535 if(buffer_chain_newlink(state) < 0)
536 goto err;
537
538 for( read_bos = 1, have_vorbis = 0 ; read_bos; )
539 {
540 test_supported = vcedit_supported_stream(state, &og);
541 if(test_supported < 0)
542 {
543 goto err;
544 }
545 else if (test_supported == 0 || have_vorbis )
546 {
547 if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
548 goto err;
549 if( buffer_chain_push(state, &og) < 0)
550 goto err;
551 }
552 else if (test_supported > 0)
553 {
554 if(buffer_chain_newlink(state) < 0)
555 goto err;
556 state->serial = ogg_page_serialno(&og);
557 if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0)
558 goto err;
559
560 state->os = malloc(sizeof(ogg_stream_state));
561 ogg_stream_init(state->os, state->serial);
562
563 state->vi = malloc(sizeof(vorbis_info));
564 vorbis_info_init(state->vi);
565
566 state->vc = malloc(sizeof(vorbis_comment));
567 vorbis_comment_init(state->vc);
568
569 if(ogg_stream_pagein(state->os, &og) < 0)
570 {
571 state->lasterror =
572 _("Error reading first page of Ogg bitstream.");
573 goto err;
574 }
575
576 if(ogg_stream_packetout(state->os, &header_main) != 1)
577 {
578 state->lasterror =
579 _("Error reading initial header packet.");
580 goto err;
581 }
582
583 if(vorbis_synthesis_headerin(state->vi, state->vc,
584 &header_main) < 0)
585 {
586 state->lasterror =
587 _("Ogg bitstream does not contain Vorbis data.");
588 goto err;
589 }
590 have_vorbis = 1;
591 }
592 while(1)
593 {
594 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
595 bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
596
597 if(bytes == 0)
598 {
599 state->lasterror =
600 _("EOF before recognised stream.");
601 goto err;
602 }
603
604 ogg_sync_wrote(state->oy, bytes);
605
606 if(ogg_sync_pageout(state->oy, &og) == 1)
607 break;
608 }
609 if(!ogg_page_bos(&og)) {
610 read_bos = 0;
611 page_pending = 1;
612 }
613 }
614
615 if(!state->os) {
616 state->lasterror = _("Ogg bitstream does not contain a supported data-type.");
617 goto err;
618 }
619
620 state->mainlen = header_main.bytes;
621 state->mainbuf = malloc(state->mainlen);
622 memcpy(state->mainbuf, header_main.packet, header_main.bytes);
623
624 if(ogg_page_serialno(&og) == state->serial)
625 {
626 if(buffer_chain_newlink(state) < 0)
627 goto err;
628 }
629
630 else
631 {
632 if(buffer_chain_push(state, &og) < 0)
633 goto err;
634 page_pending = 0;
635 }
636
637 i = 0;
638 header = &header_comments;
639 while(i<2) {
640 while(i<2) {
641 int result;
642 if(!page_pending)
643 result = vcedit_target_pageout(state, &og);
644 else
645 {
646 result = 1;
647 page_pending = 0;
648 }
649 if(result == 0 || result == -2) break; /* Too little data so far */
650 else if(result == -1) goto err;
651 else if(result == 1)
652 {
653 ogg_stream_pagein(state->os, &og);
654 while(i<2)
655 {
656 result = ogg_stream_packetout(state->os, header);
657 if(result == 0) break;
658 if(result == -1)
659 {
660 state->lasterror = _("Corrupt secondary header.");
661 goto err;
662 }
663 vorbis_synthesis_headerin(state->vi, state->vc, header);
664 if(i==1)
665 {
666 state->booklen = header->bytes;
667 state->bookbuf = malloc(state->booklen);
668 memcpy(state->bookbuf, header->packet,
669 header->bytes);
670 }
671 i++;
672 header = &header_codebooks;
673 }
674 }
675 }
676
677 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
678 bytes = state->read(buffer, 1, CHUNKSIZE, state->in);
679 if(bytes == 0 && i < 2)
680 {
681 state->lasterror = _("EOF before end of Vorbis headers.");
682 goto err;
683 }
684 ogg_sync_wrote(state->oy, bytes);
685 }
686
687 /* Copy the vendor tag */
688 /* kid3 */
689 vendor_size = strlen(state->vc->vendor) +1;
690 state->vendor = malloc(vendor_size);
691 memcpy(state->vendor, state->vc->vendor, vendor_size);
692
693 /* Headers are done! */
694 return 0;
695
696 err:
697 vcedit_clear_internals(state);
698 return -1;
699 }
700
vcedit_write(vcedit_state * state,void * out)701 int vcedit_write(vcedit_state *state, void *out)
702 {
703 ogg_stream_state streamout;
704 ogg_packet header_main;
705 ogg_packet header_comments;
706 ogg_packet header_codebooks;
707
708 ogg_page ogout, ogin;
709 ogg_packet op;
710 ogg_int64_t granpos = 0;
711 int result;
712 char *buffer;
713 int bytes;
714 int needflush=0, needout=0;
715
716 state->eosin = 0;
717 state->extrapage = 0;
718
719 header_main.bytes = state->mainlen;
720 header_main.packet = state->mainbuf;
721 header_main.b_o_s = 1;
722 header_main.e_o_s = 0;
723 header_main.granulepos = 0;
724
725 header_codebooks.bytes = state->booklen;
726 header_codebooks.packet = state->bookbuf;
727 header_codebooks.b_o_s = 0;
728 header_codebooks.e_o_s = 0;
729 header_codebooks.granulepos = 0;
730
731 ogg_stream_init(&streamout, state->serial);
732
733 _commentheader_out(state->vc, state->vendor, &header_comments);
734
735 ogg_stream_packetin(&streamout, &header_main);
736 ogg_stream_packetin(&streamout, &header_comments);
737 ogg_stream_packetin(&streamout, &header_codebooks);
738
739 while((result = ogg_stream_flush(&streamout, &ogout)))
740 {
741 if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
742 goto cleanup;
743 if(state->write(ogout.header,1,ogout.header_len, out) !=
744 (size_t) ogout.header_len)
745 goto cleanup;
746 if(state->write(ogout.body,1,ogout.body_len, out) !=
747 (size_t) ogout.body_len)
748 goto cleanup;
749 }
750
751 while(state->sidebuf) {
752 if(buffer_chain_writelink(state, out) < 0)
753 goto cleanup;
754 }
755 if(buffer_chain_newlink(state) < 0)
756 goto cleanup;
757
758 while(_fetch_next_packet(state, &op, &ogin))
759 {
760 int size;
761 size = _blocksize(state, &op);
762 granpos += size;
763
764 if(needflush)
765 {
766 if(ogg_stream_flush(&streamout, &ogout))
767 {
768 if(state->sidebuf &&
769 buffer_chain_writelink(state, out) < 0)
770 goto cleanup;
771 if(state->write(ogout.header,1,ogout.header_len,
772 out) != (size_t) ogout.header_len)
773 goto cleanup;
774 if(state->write(ogout.body,1,ogout.body_len,
775 out) != (size_t) ogout.body_len)
776 goto cleanup;
777 }
778 }
779 else if(needout)
780 {
781 if(ogg_stream_pageout(&streamout, &ogout))
782 {
783 if(state->sidebuf &&
784 buffer_chain_writelink(state, out) < 0)
785 goto cleanup;
786 if(state->write(ogout.header,1,ogout.header_len,
787 out) != (size_t) ogout.header_len)
788 goto cleanup;
789 if(state->write(ogout.body,1,ogout.body_len,
790 out) != (size_t) ogout.body_len)
791 goto cleanup;
792 }
793 }
794
795 needflush=needout=0;
796
797 if(op.granulepos == -1)
798 {
799 op.granulepos = granpos;
800 ogg_stream_packetin(&streamout, &op);
801 }
802 else /* granulepos is set, validly. Use it, and force a flush to
803 account for shortened blocks (vcut) when appropriate */
804 {
805 if(granpos > op.granulepos)
806 {
807 granpos = op.granulepos;
808 ogg_stream_packetin(&streamout, &op);
809 needflush=1;
810 }
811 else
812 {
813 ogg_stream_packetin(&streamout, &op);
814 needout=1;
815 }
816 }
817 }
818
819 streamout.e_o_s = 1;
820 while(ogg_stream_flush(&streamout, &ogout))
821 {
822 if(state->sidebuf && buffer_chain_writelink(state, out) < 0)
823 goto cleanup;
824 if(state->write(ogout.header,1,ogout.header_len,
825 out) != (size_t) ogout.header_len)
826 goto cleanup;
827 if(state->write(ogout.body,1,ogout.body_len,
828 out) != (size_t) ogout.body_len)
829 goto cleanup;
830 }
831
832 if (state->extrapage)
833 {
834 /* This is the first page of a new chain, get rid of the
835 * sidebuffer */
836 while(state->sidebuf)
837 if(buffer_chain_writelink(state, out) < 0)
838 goto cleanup;
839 if(state->write(ogin.header,1,ogin.header_len,
840 out) != (size_t) ogin.header_len)
841 goto cleanup;
842 if (state->write(ogin.body,1,ogin.body_len, out) !=
843 (size_t) ogin.body_len)
844 goto cleanup;
845 }
846
847 state->eosin=0; /* clear it, because not all paths to here do */
848 while(!state->eosin) /* We reached eos, not eof */
849 {
850 /* We copy the rest of the stream (other logical streams)
851 * through, a page at a time. */
852 while(1)
853 {
854 result = ogg_sync_pageout(state->oy, &ogout);
855 if(result==0)
856 break;
857 if(result<0)
858 state->lasterror = _("Corrupt or missing data, continuing...");
859 else
860 {
861 /* Don't bother going through the rest, we can just
862 * write the page out now */
863 if(state->write(ogout.header,1,ogout.header_len,
864 out) != (size_t) ogout.header_len) {
865 goto cleanup;
866 }
867 if(state->write(ogout.body,1,ogout.body_len, out) !=
868 (size_t) ogout.body_len) {
869 goto cleanup;
870 }
871 }
872 }
873 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
874 bytes = state->read(buffer,1, CHUNKSIZE, state->in);
875 ogg_sync_wrote(state->oy, bytes);
876 if(bytes == 0)
877 {
878 state->eosin = 1;
879 break;
880 }
881 }
882
883
884 cleanup:
885 ogg_stream_clear(&streamout);
886
887 /* We don't ogg_packet_clear() this, because the memory was allocated in
888 _commentheader_out(), so we mirror that here */
889 _ogg_free(header_comments.packet);
890
891 free(state->mainbuf);
892 free(state->bookbuf);
893 state->mainbuf = state->bookbuf = NULL;
894
895 if(!state->eosin)
896 {
897 state->lasterror =
898 _("Error writing stream to output. "
899 "Output stream may be corrupted or truncated.");
900 return -1;
901 }
902
903 return 0;
904 }
905
906 /* kid3 */
907 #endif /* HAVE_VORBIS */
908