File indexing completed on 2024-05-12 04:55:42
0001 /* This program is licensed under the GNU Library General Public License, version 2, 0002 * a copy of which is included with this program (LICENCE.LGPL). 0003 * 0004 * (c) 2000-2001 Michael Smith <msmith@xiph.org> 0005 * 0006 * 0007 * Comment editing backend, suitable for use by nice frontend interfaces. 0008 * 0009 * last modified: $Id$ 0010 */ 0011 0012 /* Handle muxed streams and the Vorbis renormalization without having 0013 * to understand remuxing: 0014 * Linked list of buffers (buffer_chain). Start a link and whenever 0015 * you encounter an unknown page from the current stream (ie we found 0016 * its bos in the bos section) push it onto the current buffer. Whenever 0017 * you encounter the stream being renormalized create a new link in the 0018 * chain. 0019 * On writing, write the contents of the first link before every Vorbis 0020 * page written, and move to the next link. Assuming the Vorbis pages 0021 * in match vorbis pages out, the order of pages from different logical 0022 * streams will be unchanged. 0023 * Special case: header. After writing the vorbis headers, and before 0024 * starting renormalization, flush accumulated links (takes care of 0025 * situations where number of secondary vorbis header pages changes due 0026 * to remuxing. Similarly flush links at the end of renormalization 0027 * and before the start of the next chain is written. 0028 * 0029 */ 0030 0031 /* This file is taken from vorbis-tools SVN 0032 * (http://svn.xiph.org/trunk/vorbis-tools/vorbiscomment) Rev. 16716 2009-11-22, 0033 * modifications are marked with kid3. */ 0034 /* kid3 */ 0035 #include "oggflacconfig.h" 0036 #ifdef HAVE_VORBIS 0037 0038 #ifdef HAVE_CONFIG_H 0039 #include <config.h> 0040 #endif 0041 0042 #include <stdio.h> 0043 #include <stdlib.h> 0044 #include <string.h> 0045 #include <errno.h> 0046 #include <ogg/ogg.h> 0047 #include <vorbis/codec.h> 0048 0049 #include "vcedit.h" 0050 /* kid3 */ 0051 /*#include "vceditaux.h"*/ 0052 typedef struct vcedit_page_buffer { 0053 char *data; 0054 size_t data_len; 0055 } vcedit_page_buffer; 0056 0057 typedef struct vcedit_buffer_chain { 0058 struct vcedit_buffer_chain *next; 0059 struct vcedit_page_buffer buffer; 0060 } vcedit_buffer_chain; 0061 /* kid3 */ 0062 /*#include "i18n.h"*/ 0063 #define _(str) ((char*)(str)) 0064 0065 0066 #define CHUNKSIZE 4096 0067 #define BUFFERCHUNK CHUNKSIZE 0068 0069 /* Helper function, shouldn't need to call directly */ 0070 static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) { 0071 int result=0; 0072 char *tmp; 0073 vcedit_page_buffer *buffer; 0074 0075 buffer = &bufferlink->buffer; 0076 tmp = realloc(buffer->data, 0077 buffer->data_len + og->header_len + og->body_len); 0078 if(tmp) { 0079 buffer->data = tmp; 0080 memcpy(buffer->data + buffer->data_len, og->header, 0081 og->header_len); 0082 buffer->data_len += og->header_len; 0083 memcpy(buffer->data + buffer->data_len, og->body, 0084 og->body_len); 0085 result = 1; 0086 buffer->data_len += og->body_len; 0087 } else { 0088 result = -1; 0089 } 0090 0091 return result; 0092 } 0093 0094 /* Write and free the first link using callbacks */ 0095 static int buffer_chain_writelink(vcedit_state *state, void *out) { 0096 int result = 0; 0097 vcedit_buffer_chain *tmpchain; 0098 vcedit_page_buffer *tmpbuffer; 0099 0100 tmpchain = state->sidebuf; 0101 tmpbuffer = &tmpchain->buffer; 0102 if(tmpbuffer->data_len) 0103 { 0104 if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) != 0105 (size_t) tmpbuffer->data_len) 0106 result = -1; 0107 else 0108 result = 1; 0109 } 0110 0111 free(tmpbuffer->data); 0112 state->sidebuf = tmpchain->next; 0113 free(tmpchain); 0114 return result; 0115 } 0116 0117 0118 static int buffer_chain_newlink(vcedit_state *state) { 0119 int result = 1; 0120 vcedit_buffer_chain *bufferlink; 0121 0122 if(!state->sidebuf) { 0123 state->sidebuf = malloc (sizeof *state->sidebuf); 0124 if(state->sidebuf) { 0125 bufferlink = state->sidebuf; 0126 } else { 0127 result = -1; 0128 } 0129 } else { 0130 bufferlink=state->sidebuf; 0131 while(bufferlink->next) { 0132 bufferlink = bufferlink->next; 0133 } 0134 bufferlink->next = malloc (sizeof *bufferlink->next); 0135 if(bufferlink->next) { 0136 bufferlink = bufferlink->next; 0137 } else { 0138 result = -1; 0139 } 0140 } 0141 0142 if(result > 0 ) { 0143 bufferlink->next = 0; 0144 bufferlink->buffer.data = 0; 0145 bufferlink->buffer.data_len = 0; 0146 } 0147 else 0148 state->lasterror = 0149 _("Couldn't get enough memory for input buffering."); 0150 0151 return result; 0152 } 0153 0154 0155 /* Push page onto the end of the buffer chain */ 0156 static int buffer_chain_push(vcedit_state *state, ogg_page *og) { 0157 /* If there is no sidebuffer yet we need to create one, otherwise 0158 * traverse to the last buffer and push the new page onto it. */ 0159 int result=1; 0160 vcedit_buffer_chain *bufferlink; 0161 if(!state->sidebuf) { 0162 result = buffer_chain_newlink(state); 0163 } 0164 0165 if(result > 0) { 0166 bufferlink = state->sidebuf; 0167 while(bufferlink->next) { 0168 bufferlink = bufferlink->next; 0169 } 0170 result = page_buffer_push(bufferlink, og); 0171 } 0172 0173 if(result < 0) 0174 state->lasterror = 0175 _("Couldn't get enough memory for input buffering."); 0176 0177 return result; 0178 } 0179 0180 0181 0182 static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) { 0183 ogg_stream_state os; 0184 vorbis_info vi; 0185 vorbis_comment vc; 0186 ogg_packet header; 0187 int result = 0; 0188 0189 ogg_stream_init(&os, ogg_page_serialno(og)); 0190 vorbis_info_init(&vi); 0191 vorbis_comment_init(&vc); 0192 0193 if( !ogg_page_bos(og) ) 0194 result = -1; 0195 0196 if(result >= 0 && ogg_stream_pagein(&os, og) < 0) 0197 { 0198 state->lasterror = 0199 _("Error reading first page of Ogg bitstream."); 0200 result = -1; 0201 } 0202 0203 if(result >= 0 && ogg_stream_packetout(&os, &header) != 1) 0204 { 0205 state->lasterror = _("Error reading initial header packet."); 0206 result = -1; 0207 } 0208 0209 if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0) 0210 { 0211 result = 1; 0212 } else { 0213 /* Not vorbis, may eventually become a chain of checks (Speex, 0214 * Theora), but for the moment return 0, bos scan will push 0215 * the current page onto the buffer. 0216 */ 0217 } 0218 0219 ogg_stream_clear(&os); 0220 vorbis_info_clear(&vi); 0221 vorbis_comment_clear(&vc); 0222 return result; 0223 } 0224 0225 0226 static int vcedit_contains_serial (vcedit_state *state, int serialno) { 0227 int result = 0; 0228 size_t count; 0229 for( count=0; count < state->serials.streams_len; count++ ) { 0230 if ( *(state->serials.streams + count ) == serialno ) 0231 result = 1; 0232 } 0233 0234 return result; 0235 } 0236 0237 0238 static int vcedit_add_serial (vcedit_state *state, long serial) { 0239 int result = 0; 0240 long *tmp; 0241 0242 0243 if( vcedit_contains_serial(state, serial) ) 0244 { 0245 result = 1; 0246 } else { 0247 tmp = realloc(state->serials.streams, 0248 (state->serials.streams_len + 1) * sizeof *tmp); 0249 if(tmp) { 0250 state->serials.streams = tmp; 0251 *(state->serials.streams + 0252 state->serials.streams_len) = serial; 0253 state->serials.streams_len += 1; 0254 result = 1; 0255 } else { 0256 state->lasterror = 0257 _("Couldn't get enough memory to register new stream serial number."); 0258 result = -1; 0259 } 0260 } 0261 return result; 0262 } 0263 0264 0265 /* For the benefit of the secondary header read only. Quietly creates 0266 * newlinks and pushes pages onto the buffer in the right way */ 0267 static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) { 0268 int result = 0; 0269 int pageout_result; 0270 pageout_result = ogg_sync_pageout(state->oy, og); 0271 if(pageout_result > 0) 0272 { 0273 if(state->serial == ogg_page_serialno(og)) 0274 result = buffer_chain_newlink(state); 0275 else 0276 result = buffer_chain_push(state, og); 0277 } else if (pageout_result < 0) { 0278 /* Vorbis comment traditionally ignores the not-synced 0279 * error from pageout, so give it a different code. */ 0280 result = -2; 0281 } 0282 return result; 0283 } 0284 0285 0286 /* (I'm paranoid about memset(x,0,len) not giving null pointers */ 0287 vcedit_state *vcedit_new_state(void) { 0288 vcedit_state *state = malloc(sizeof(vcedit_state)); 0289 if(state) { 0290 memset(state, 0, sizeof(vcedit_state)); 0291 state->sidebuf = 0; 0292 state->serials.streams = 0; 0293 state->serials.streams_len = 0; 0294 } 0295 return state; 0296 } 0297 0298 char *vcedit_error(vcedit_state *state) { 0299 return state->lasterror; 0300 } 0301 0302 vorbis_comment *vcedit_comments(vcedit_state *state) { 0303 return state->vc; 0304 } 0305 0306 static void vcedit_clear_internals(vcedit_state *state) { 0307 char *tmp; 0308 if(state->vc) { 0309 vorbis_comment_clear(state->vc); 0310 free(state->vc); 0311 } 0312 if(state->os) { 0313 ogg_stream_clear(state->os); 0314 free(state->os); 0315 } 0316 if(state->oy) { 0317 ogg_sync_clear(state->oy); 0318 free(state->oy); 0319 } 0320 if(state->serials.streams_len) { 0321 free(state->serials.streams); 0322 state->serials.streams_len = 0; 0323 state->serials.streams = 0; 0324 } 0325 while(state->sidebuf) { 0326 vcedit_buffer_chain *tmpbuffer; 0327 tmpbuffer = state->sidebuf; 0328 state->sidebuf = tmpbuffer->next; 0329 free(tmpbuffer->buffer.data); 0330 free(tmpbuffer); 0331 } 0332 if(state->vendor) 0333 free(state->vendor); 0334 if(state->mainbuf) 0335 free(state->mainbuf); 0336 if(state->bookbuf) 0337 free(state->bookbuf); 0338 if(state->vi) { 0339 vorbis_info_clear(state->vi); 0340 free(state->vi); 0341 } 0342 0343 tmp = state->lasterror; 0344 memset(state, 0, sizeof(*state)); 0345 state->lasterror = tmp; 0346 } 0347 0348 void vcedit_clear(vcedit_state *state) 0349 { 0350 if(state) 0351 { 0352 vcedit_clear_internals(state); 0353 free(state); 0354 } 0355 } 0356 0357 /* Next two functions pulled straight from libvorbis, apart from one change 0358 * - we don't want to overwrite the vendor string. 0359 */ 0360 /* kid3: changed type from char* to const char* */ 0361 static void _v_writestring(oggpack_buffer *o, const char *s, int len) 0362 { 0363 while(len--) 0364 { 0365 oggpack_write(o,*s++,8); 0366 } 0367 } 0368 0369 static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op) 0370 { 0371 oggpack_buffer opb; 0372 0373 oggpack_writeinit(&opb); 0374 0375 /* preamble */ 0376 oggpack_write(&opb,0x03,8); 0377 _v_writestring(&opb,"vorbis", 6); 0378 0379 /* vendor */ 0380 oggpack_write(&opb,strlen(vendor),32); 0381 _v_writestring(&opb,vendor, strlen(vendor)); 0382 0383 /* comments */ 0384 oggpack_write(&opb,vc->comments,32); 0385 if(vc->comments){ 0386 int i; 0387 for(i=0;i<vc->comments;i++){ 0388 if(vc->user_comments[i]){ 0389 oggpack_write(&opb,vc->comment_lengths[i],32); 0390 _v_writestring(&opb,vc->user_comments[i], 0391 vc->comment_lengths[i]); 0392 }else{ 0393 oggpack_write(&opb,0,32); 0394 } 0395 } 0396 } 0397 oggpack_write(&opb,1,1); 0398 0399 op->packet = malloc(oggpack_bytes(&opb)); 0400 memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); 0401 0402 op->bytes=oggpack_bytes(&opb); 0403 op->b_o_s=0; 0404 op->e_o_s=0; 0405 op->granulepos=0; 0406 0407 oggpack_writeclear(&opb); 0408 return 0; 0409 } 0410 0411 static int _blocksize(vcedit_state *s, ogg_packet *p) 0412 { 0413 int this = vorbis_packet_blocksize(s->vi, p); 0414 int ret = (this + s->prevW)/4; 0415 0416 if(!s->prevW) 0417 { 0418 s->prevW = this; 0419 return 0; 0420 } 0421 0422 s->prevW = this; 0423 return ret; 0424 } 0425 0426 static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page) 0427 { 0428 int result; 0429 char *buffer; 0430 int bytes; 0431 int serialno; 0432 0433 result = ogg_stream_packetout(s->os, p); 0434 0435 if(result > 0) 0436 return 1; 0437 else { 0438 while(1) { 0439 if(s->eosin) 0440 return 0; 0441 0442 while(ogg_sync_pageout(s->oy, page) <= 0) 0443 { 0444 buffer = ogg_sync_buffer(s->oy, CHUNKSIZE); 0445 bytes = s->read(buffer,1, CHUNKSIZE, s->in); 0446 ogg_sync_wrote(s->oy, bytes); 0447 if(bytes == 0) 0448 return 0; 0449 } 0450 0451 serialno = ogg_page_serialno(page); 0452 if(ogg_page_serialno(page) != s->serial) 0453 { 0454 if(vcedit_contains_serial(s, serialno)) { 0455 result = buffer_chain_push(s, page); 0456 if(result < 0) 0457 return result; 0458 } 0459 else 0460 { 0461 s->eosin = 1; 0462 s->extrapage = 1; 0463 return 0; 0464 } 0465 } 0466 else 0467 { 0468 ogg_stream_pagein(s->os, page); 0469 result = buffer_chain_newlink(s); 0470 if (result < 0) 0471 return result; 0472 0473 if(ogg_page_eos(page)) 0474 s->eosin = 1; 0475 } 0476 result = ogg_stream_packetout(s->os, p); 0477 if(result > 0) 0478 return 1; 0479 } 0480 /* Here == trouble */ 0481 return 0; 0482 } 0483 } 0484 0485 /* kid3 vcedit_open() removed */ 0486 0487 int vcedit_open_callbacks(vcedit_state *state, void *in, 0488 vcedit_read_func read_func, vcedit_write_func write_func) 0489 { 0490 0491 char *buffer; 0492 int bytes,i; 0493 int chunks = 0; 0494 int read_bos, test_supported, page_pending; 0495 int have_vorbis; 0496 size_t vendor_size; 0497 ogg_packet *header; 0498 ogg_packet header_main; 0499 ogg_packet header_comments; 0500 ogg_packet header_codebooks; 0501 ogg_page og; 0502 0503 /* kid3, to fix LLVM scan-build error "Assigned value is garbage or undefined" 0504 in line "state->mainlen = header_main.bytes;" */ 0505 memset(&header_main, 0, sizeof(header_main)); 0506 0507 state->in = in; 0508 state->read = read_func; 0509 state->write = write_func; 0510 0511 state->oy = malloc(sizeof(ogg_sync_state)); 0512 ogg_sync_init(state->oy); 0513 0514 while(1) 0515 { 0516 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); 0517 bytes = state->read(buffer, 1, CHUNKSIZE, state->in); 0518 0519 ogg_sync_wrote(state->oy, bytes); 0520 0521 if(ogg_sync_pageout(state->oy, &og) == 1) 0522 break; 0523 0524 if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */ 0525 { 0526 if(bytes<CHUNKSIZE) 0527 state->lasterror = _("Input truncated or empty."); 0528 else 0529 state->lasterror = _("Input is not an Ogg bitstream."); 0530 goto err; 0531 } 0532 } 0533 0534 /* BOS loop, starting with a loaded ogg page. */ 0535 if(buffer_chain_newlink(state) < 0) 0536 goto err; 0537 0538 for( read_bos = 1, have_vorbis = 0 ; read_bos; ) 0539 { 0540 test_supported = vcedit_supported_stream(state, &og); 0541 if(test_supported < 0) 0542 { 0543 goto err; 0544 } 0545 else if (test_supported == 0 || have_vorbis ) 0546 { 0547 if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) 0548 goto err; 0549 if( buffer_chain_push(state, &og) < 0) 0550 goto err; 0551 } 0552 else if (test_supported > 0) 0553 { 0554 if(buffer_chain_newlink(state) < 0) 0555 goto err; 0556 state->serial = ogg_page_serialno(&og); 0557 if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) 0558 goto err; 0559 0560 state->os = malloc(sizeof(ogg_stream_state)); 0561 ogg_stream_init(state->os, state->serial); 0562 0563 state->vi = malloc(sizeof(vorbis_info)); 0564 vorbis_info_init(state->vi); 0565 0566 state->vc = malloc(sizeof(vorbis_comment)); 0567 vorbis_comment_init(state->vc); 0568 0569 if(ogg_stream_pagein(state->os, &og) < 0) 0570 { 0571 state->lasterror = 0572 _("Error reading first page of Ogg bitstream."); 0573 goto err; 0574 } 0575 0576 if(ogg_stream_packetout(state->os, &header_main) != 1) 0577 { 0578 state->lasterror = 0579 _("Error reading initial header packet."); 0580 goto err; 0581 } 0582 0583 if(vorbis_synthesis_headerin(state->vi, state->vc, 0584 &header_main) < 0) 0585 { 0586 state->lasterror = 0587 _("Ogg bitstream does not contain Vorbis data."); 0588 goto err; 0589 } 0590 have_vorbis = 1; 0591 } 0592 while(1) 0593 { 0594 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); 0595 bytes = state->read(buffer, 1, CHUNKSIZE, state->in); 0596 0597 if(bytes == 0) 0598 { 0599 state->lasterror = 0600 _("EOF before recognised stream."); 0601 goto err; 0602 } 0603 0604 ogg_sync_wrote(state->oy, bytes); 0605 0606 if(ogg_sync_pageout(state->oy, &og) == 1) 0607 break; 0608 } 0609 if(!ogg_page_bos(&og)) { 0610 read_bos = 0; 0611 page_pending = 1; 0612 } 0613 } 0614 0615 if(!state->os) { 0616 state->lasterror = _("Ogg bitstream does not contain a supported data-type."); 0617 goto err; 0618 } 0619 0620 state->mainlen = header_main.bytes; 0621 state->mainbuf = malloc(state->mainlen); 0622 memcpy(state->mainbuf, header_main.packet, header_main.bytes); 0623 0624 if(ogg_page_serialno(&og) == state->serial) 0625 { 0626 if(buffer_chain_newlink(state) < 0) 0627 goto err; 0628 } 0629 0630 else 0631 { 0632 if(buffer_chain_push(state, &og) < 0) 0633 goto err; 0634 page_pending = 0; 0635 } 0636 0637 i = 0; 0638 header = &header_comments; 0639 while(i<2) { 0640 while(i<2) { 0641 int result; 0642 if(!page_pending) 0643 result = vcedit_target_pageout(state, &og); 0644 else 0645 { 0646 result = 1; 0647 page_pending = 0; 0648 } 0649 if(result == 0 || result == -2) break; /* Too little data so far */ 0650 else if(result == -1) goto err; 0651 else if(result == 1) 0652 { 0653 ogg_stream_pagein(state->os, &og); 0654 while(i<2) 0655 { 0656 result = ogg_stream_packetout(state->os, header); 0657 if(result == 0) break; 0658 if(result == -1) 0659 { 0660 state->lasterror = _("Corrupt secondary header."); 0661 goto err; 0662 } 0663 vorbis_synthesis_headerin(state->vi, state->vc, header); 0664 if(i==1) 0665 { 0666 state->booklen = header->bytes; 0667 state->bookbuf = malloc(state->booklen); 0668 memcpy(state->bookbuf, header->packet, 0669 header->bytes); 0670 } 0671 i++; 0672 header = &header_codebooks; 0673 } 0674 } 0675 } 0676 0677 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); 0678 bytes = state->read(buffer, 1, CHUNKSIZE, state->in); 0679 if(bytes == 0 && i < 2) 0680 { 0681 state->lasterror = _("EOF before end of Vorbis headers."); 0682 goto err; 0683 } 0684 ogg_sync_wrote(state->oy, bytes); 0685 } 0686 0687 /* Copy the vendor tag */ 0688 /* kid3 */ 0689 vendor_size = strlen(state->vc->vendor) +1; 0690 state->vendor = malloc(vendor_size); 0691 memcpy(state->vendor, state->vc->vendor, vendor_size); 0692 0693 /* Headers are done! */ 0694 return 0; 0695 0696 err: 0697 vcedit_clear_internals(state); 0698 return -1; 0699 } 0700 0701 int vcedit_write(vcedit_state *state, void *out) 0702 { 0703 ogg_stream_state streamout; 0704 ogg_packet header_main; 0705 ogg_packet header_comments; 0706 ogg_packet header_codebooks; 0707 0708 ogg_page ogout, ogin; 0709 ogg_packet op; 0710 ogg_int64_t granpos = 0; 0711 int result; 0712 char *buffer; 0713 int bytes; 0714 int needflush=0, needout=0; 0715 0716 state->eosin = 0; 0717 state->extrapage = 0; 0718 0719 header_main.bytes = state->mainlen; 0720 header_main.packet = state->mainbuf; 0721 header_main.b_o_s = 1; 0722 header_main.e_o_s = 0; 0723 header_main.granulepos = 0; 0724 0725 header_codebooks.bytes = state->booklen; 0726 header_codebooks.packet = state->bookbuf; 0727 header_codebooks.b_o_s = 0; 0728 header_codebooks.e_o_s = 0; 0729 header_codebooks.granulepos = 0; 0730 0731 ogg_stream_init(&streamout, state->serial); 0732 0733 _commentheader_out(state->vc, state->vendor, &header_comments); 0734 0735 ogg_stream_packetin(&streamout, &header_main); 0736 ogg_stream_packetin(&streamout, &header_comments); 0737 ogg_stream_packetin(&streamout, &header_codebooks); 0738 0739 while((result = ogg_stream_flush(&streamout, &ogout))) 0740 { 0741 if(state->sidebuf && buffer_chain_writelink(state, out) < 0) 0742 goto cleanup; 0743 if(state->write(ogout.header,1,ogout.header_len, out) != 0744 (size_t) ogout.header_len) 0745 goto cleanup; 0746 if(state->write(ogout.body,1,ogout.body_len, out) != 0747 (size_t) ogout.body_len) 0748 goto cleanup; 0749 } 0750 0751 while(state->sidebuf) { 0752 if(buffer_chain_writelink(state, out) < 0) 0753 goto cleanup; 0754 } 0755 if(buffer_chain_newlink(state) < 0) 0756 goto cleanup; 0757 0758 while(_fetch_next_packet(state, &op, &ogin)) 0759 { 0760 int size; 0761 size = _blocksize(state, &op); 0762 granpos += size; 0763 0764 if(needflush) 0765 { 0766 if(ogg_stream_flush(&streamout, &ogout)) 0767 { 0768 if(state->sidebuf && 0769 buffer_chain_writelink(state, out) < 0) 0770 goto cleanup; 0771 if(state->write(ogout.header,1,ogout.header_len, 0772 out) != (size_t) ogout.header_len) 0773 goto cleanup; 0774 if(state->write(ogout.body,1,ogout.body_len, 0775 out) != (size_t) ogout.body_len) 0776 goto cleanup; 0777 } 0778 } 0779 else if(needout) 0780 { 0781 if(ogg_stream_pageout(&streamout, &ogout)) 0782 { 0783 if(state->sidebuf && 0784 buffer_chain_writelink(state, out) < 0) 0785 goto cleanup; 0786 if(state->write(ogout.header,1,ogout.header_len, 0787 out) != (size_t) ogout.header_len) 0788 goto cleanup; 0789 if(state->write(ogout.body,1,ogout.body_len, 0790 out) != (size_t) ogout.body_len) 0791 goto cleanup; 0792 } 0793 } 0794 0795 needflush=needout=0; 0796 0797 if(op.granulepos == -1) 0798 { 0799 op.granulepos = granpos; 0800 ogg_stream_packetin(&streamout, &op); 0801 } 0802 else /* granulepos is set, validly. Use it, and force a flush to 0803 account for shortened blocks (vcut) when appropriate */ 0804 { 0805 if(granpos > op.granulepos) 0806 { 0807 granpos = op.granulepos; 0808 ogg_stream_packetin(&streamout, &op); 0809 needflush=1; 0810 } 0811 else 0812 { 0813 ogg_stream_packetin(&streamout, &op); 0814 needout=1; 0815 } 0816 } 0817 } 0818 0819 streamout.e_o_s = 1; 0820 while(ogg_stream_flush(&streamout, &ogout)) 0821 { 0822 if(state->sidebuf && buffer_chain_writelink(state, out) < 0) 0823 goto cleanup; 0824 if(state->write(ogout.header,1,ogout.header_len, 0825 out) != (size_t) ogout.header_len) 0826 goto cleanup; 0827 if(state->write(ogout.body,1,ogout.body_len, 0828 out) != (size_t) ogout.body_len) 0829 goto cleanup; 0830 } 0831 0832 if (state->extrapage) 0833 { 0834 /* This is the first page of a new chain, get rid of the 0835 * sidebuffer */ 0836 while(state->sidebuf) 0837 if(buffer_chain_writelink(state, out) < 0) 0838 goto cleanup; 0839 if(state->write(ogin.header,1,ogin.header_len, 0840 out) != (size_t) ogin.header_len) 0841 goto cleanup; 0842 if (state->write(ogin.body,1,ogin.body_len, out) != 0843 (size_t) ogin.body_len) 0844 goto cleanup; 0845 } 0846 0847 state->eosin=0; /* clear it, because not all paths to here do */ 0848 while(!state->eosin) /* We reached eos, not eof */ 0849 { 0850 /* We copy the rest of the stream (other logical streams) 0851 * through, a page at a time. */ 0852 while(1) 0853 { 0854 result = ogg_sync_pageout(state->oy, &ogout); 0855 if(result==0) 0856 break; 0857 if(result<0) 0858 state->lasterror = _("Corrupt or missing data, continuing..."); 0859 else 0860 { 0861 /* Don't bother going through the rest, we can just 0862 * write the page out now */ 0863 if(state->write(ogout.header,1,ogout.header_len, 0864 out) != (size_t) ogout.header_len) { 0865 goto cleanup; 0866 } 0867 if(state->write(ogout.body,1,ogout.body_len, out) != 0868 (size_t) ogout.body_len) { 0869 goto cleanup; 0870 } 0871 } 0872 } 0873 buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); 0874 bytes = state->read(buffer,1, CHUNKSIZE, state->in); 0875 ogg_sync_wrote(state->oy, bytes); 0876 if(bytes == 0) 0877 { 0878 state->eosin = 1; 0879 break; 0880 } 0881 } 0882 0883 0884 cleanup: 0885 ogg_stream_clear(&streamout); 0886 0887 /* We don't ogg_packet_clear() this, because the memory was allocated in 0888 _commentheader_out(), so we mirror that here */ 0889 _ogg_free(header_comments.packet); 0890 0891 free(state->mainbuf); 0892 free(state->bookbuf); 0893 state->mainbuf = state->bookbuf = NULL; 0894 0895 if(!state->eosin) 0896 { 0897 state->lasterror = 0898 _("Error writing stream to output. " 0899 "Output stream may be corrupted or truncated."); 0900 return -1; 0901 } 0902 0903 return 0; 0904 } 0905 0906 /* kid3 */ 0907 #endif /* HAVE_VORBIS */