Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvoggvorbis.cc

Go to the documentation of this file.
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * Provides a WvEncoder abstraction for Ogg vorbis files. 00006 */ 00007 #include "wvoggvorbis.h" 00008 #include <ogg/ogg.h> 00009 #include <vorbis/codec.h> 00010 #include <vorbis/vorbisenc.h> 00011 #include <unistd.h> 00012 00013 #define OGG_VORBIS_ENCODER_BUF_SIZE 1024 // 1k samples for Vorbis 00014 #define OGG_VORBIS_DECODER_BUF_SIZE 16384 // at least 8k for Vorbis 00015 00016 /***** WvOggVorbisEncoder *****/ 00017 00018 WvOggVorbisEncoder::WvOggVorbisEncoder( 00019 const BitrateSpec &bitratespec, 00020 int samplingrate, int channels, long serialno) : 00021 oggstream(NULL), ovinfo(NULL), ovcomment(NULL), 00022 ovdsp(NULL), ovblock(NULL), wrote_header(false) 00023 { 00024 assert(channels == 1 || ! "stereo not supported yet"); 00025 00026 // pick a serial number 00027 if (serialno == RANDOM_SERIALNO) 00028 { 00029 serialno = rand(); 00030 } 00031 00032 // init ogg bitstream layer 00033 int retval; 00034 oggstream = new ogg_stream_state; 00035 if ((retval = ogg_stream_init(oggstream, serialno)) != 0) 00036 { 00037 seterror("error %s during ogg_stream_init", retval); 00038 return; 00039 } 00040 00041 // init vorbis codec layer 00042 ovinfo = new vorbis_info; 00043 vorbis_info_init(ovinfo); 00044 00045 ovcomment = new vorbis_comment; 00046 vorbis_comment_init(ovcomment); 00047 00048 // init vorbis bitrate management 00049 switch (bitratespec.mode) 00050 { 00051 case BitrateSpec::VBR_QUALITY: 00052 if ((retval = vorbis_encode_init_vbr(ovinfo, channels, 00053 samplingrate, bitratespec.quality_index)) < 0) 00054 { 00055 seterror("error %s during vorbis_encode_init_vbr", retval); 00056 return; 00057 } 00058 break; 00059 00060 case BitrateSpec::VBR_BITRATE: 00061 if ((retval = vorbis_encode_init(ovinfo, channels, samplingrate, 00062 bitratespec.max_bitrate, bitratespec.nominal_bitrate, 00063 bitratespec.min_bitrate)) < 0) 00064 { 00065 seterror("error %s during vorbis_encode_init", retval); 00066 return; 00067 } 00068 break; 00069 } 00070 00071 // init vorbis dsp layer 00072 ovdsp = new vorbis_dsp_state; 00073 if ((retval = vorbis_analysis_init(ovdsp, ovinfo)) != 0) 00074 { 00075 seterror("error %s during vorbis_analysis_init", retval); 00076 return; 00077 } 00078 ovblock = new vorbis_block; 00079 if ((retval = vorbis_block_init(ovdsp, ovblock)) != 0) 00080 { 00081 seterror("error %s during vorbis_block_init", retval); 00082 return; 00083 } 00084 } 00085 00086 00087 WvOggVorbisEncoder::~WvOggVorbisEncoder() 00088 { 00089 // destroy vorbis dsp layer 00090 if (ovblock) 00091 { 00092 vorbis_block_clear(ovblock); 00093 delete ovblock; 00094 } 00095 if (ovdsp) 00096 { 00097 vorbis_dsp_clear(ovdsp); 00098 delete ovdsp; 00099 } 00100 00101 // destroy vorbis codec layer 00102 if (ovcomment) 00103 { 00104 vorbis_comment_clear(ovcomment); 00105 delete ovcomment; 00106 } 00107 if (ovinfo) 00108 { 00109 vorbis_info_clear(ovinfo); 00110 delete ovinfo; 00111 } 00112 00113 // destroy ogg bitstream layer 00114 if (oggstream) 00115 { 00116 ogg_stream_clear(oggstream); 00117 delete oggstream; 00118 } 00119 } 00120 00121 void WvOggVorbisEncoder::add_comment(WvStringParm comment) 00122 { 00123 if (! comment) return; 00124 // Ogg Vorbis missing const qualifier in function prototype! 00125 char *str = const_cast<char *>(comment.cstr()); 00126 vorbis_comment_add(ovcomment, str); 00127 } 00128 00129 00130 void WvOggVorbisEncoder::add_tag(WvStringParm tag, WvStringParm value) 00131 { 00132 if (! tag || ! value) return; 00133 // Ogg Vorbis missing const qualifier in function prototype! 00134 char *tagstr = const_cast<char *>(tag.cstr()); 00135 char *valuestr = const_cast<char *>(value.cstr()); 00136 vorbis_comment_add_tag(ovcomment, tagstr, valuestr); 00137 } 00138 00139 00140 bool WvOggVorbisEncoder::_typedencode(IBuffer &inbuf, OBuffer &outbuf, 00141 bool flush) 00142 { 00143 // write header pages if needed 00144 if (! wrote_header) 00145 { 00146 if (! write_header(outbuf)) 00147 return false; 00148 wrote_header = true; 00149 } 00150 00151 // write compressed audio pages 00152 for (;;) 00153 { 00154 // read in more data 00155 size_t ovsamples = inbuf.used(); 00156 if (ovsamples == 0) 00157 { 00158 // no more data 00159 if (flush) 00160 if (! write_stream(outbuf, true)) 00161 return false; 00162 return true; 00163 } 00164 if (ovsamples > OGG_VORBIS_ENCODER_BUF_SIZE) 00165 ovsamples = OGG_VORBIS_ENCODER_BUF_SIZE; 00166 00167 float **ovbuf = vorbis_analysis_buffer(ovdsp, ovsamples); 00168 if (ovbuf == NULL) 00169 { 00170 seterror("error allocating vorbis analysis buffer"); 00171 return false; 00172 } 00173 inbuf.move(ovbuf[0], ovsamples); 00174 vorbis_analysis_wrote(ovdsp, ovsamples); 00175 process_audio(outbuf); 00176 } 00177 } 00178 00179 00180 bool WvOggVorbisEncoder::_typedfinish(OBuffer &outbuf) 00181 { 00182 // write header pages if needed 00183 if (! wrote_header) 00184 { 00185 if (! write_header(outbuf)) 00186 return false; 00187 wrote_header = true; 00188 } 00189 00190 // write EOF mark 00191 vorbis_analysis_wrote(ovdsp, 0); 00192 process_audio(outbuf); 00193 return true; 00194 } 00195 00196 00197 bool WvOggVorbisEncoder::write_header(OBuffer &outbuf) 00198 { 00199 // generate headers 00200 ogg_packet headers[3]; 00201 int retval; 00202 if ((retval = vorbis_analysis_headerout(ovdsp, ovcomment, 00203 & headers[0], & headers[1], & headers[2])) != 0) 00204 { 00205 seterror("error %s during vorbis_analysis_headerout", retval); 00206 return false; 00207 } 00208 00209 // push headers to ogg stream 00210 for (int i = 0; i < 3; ++i) 00211 ogg_stream_packetin(oggstream, & headers[i]); // always succeeds 00212 00213 // flush to ensure next data packet is in its own page 00214 return write_stream(outbuf, true /*flush*/); 00215 } 00216 00217 00218 bool WvOggVorbisEncoder::write_stream(OBuffer &outbuf, bool flush) 00219 { 00220 ogg_page oggpage; 00221 for (;;) 00222 { 00223 if (flush) 00224 { 00225 int retval = ogg_stream_flush(oggstream, & oggpage); 00226 if (retval == 0) 00227 break; // no remaining data 00228 else if (retval < 0) 00229 { 00230 seterror("error %s during ogg_stream_flush", retval); 00231 return false; 00232 } 00233 } 00234 else 00235 { 00236 int retval = ogg_stream_pageout(oggstream, & oggpage); 00237 if (retval == 0) 00238 break; // not enough data 00239 else if (retval < 0) 00240 { 00241 seterror("error %s during ogg_stream_pageout", retval); 00242 return false; 00243 } 00244 } 00245 outbuf.put(oggpage.header, oggpage.header_len); 00246 outbuf.put(oggpage.body, oggpage.body_len); 00247 } 00248 return true; 00249 } 00250 00251 00252 bool WvOggVorbisEncoder::process_audio(OBuffer &outbuf) 00253 { 00254 while (vorbis_analysis_blockout(ovdsp, ovblock) == 1) 00255 { 00256 // we got a block! 00257 int retval = vorbis_analysis(ovblock, NULL); 00258 if (retval < 0) 00259 { 00260 seterror("error %s during vorbis_analysis", retval); 00261 return false; 00262 } 00263 retval = vorbis_bitrate_addblock(ovblock); 00264 if (retval < 0) 00265 { 00266 seterror("error %s during vorbis_bitrate_addblock", retval); 00267 return false; 00268 } 00269 00270 // write out packets 00271 ogg_packet oggpacket; 00272 while (vorbis_bitrate_flushpacket(ovdsp, & oggpacket) > 0) 00273 { 00274 ogg_stream_packetin(oggstream, & oggpacket); // always succeeds 00275 if (! write_stream(outbuf, false)) 00276 return false; 00277 } 00278 } 00279 return true; 00280 } 00281 00282 00283 00284 /***** WvOggVorbisDecoder *****/ 00285 00286 WvOggVorbisDecoder::WvOggVorbisDecoder() : 00287 oggsync(NULL), oggstream(NULL), ovinfo(NULL), ovcomment(NULL), 00288 ovdsp(NULL), ovblock(NULL), need_serialno(true), need_headers(3) 00289 { 00290 int retval; 00291 00292 // init ogg sync layer 00293 oggsync = new ogg_sync_state; 00294 if ((retval = ogg_sync_init(oggsync)) != 0) 00295 { 00296 seterror("error %s during ogg_sync_init", retval); 00297 return; 00298 } 00299 oggpage = new ogg_page; 00300 } 00301 00302 00303 WvOggVorbisDecoder::~WvOggVorbisDecoder() 00304 { 00305 // destroy vorbis dsp layer 00306 if (ovblock) 00307 { 00308 vorbis_block_clear(ovblock); 00309 delete ovblock; 00310 } 00311 if (ovdsp) 00312 { 00313 vorbis_dsp_clear(ovdsp); 00314 delete ovdsp; 00315 } 00316 00317 // destroy vorbis codec layer 00318 if (ovcomment) 00319 { 00320 vorbis_comment_clear(ovcomment); 00321 delete ovcomment; 00322 } 00323 if (ovinfo) 00324 { 00325 vorbis_info_clear(ovinfo); 00326 delete ovinfo; 00327 } 00328 00329 // destroy ogg bitstream layer 00330 if (oggstream) 00331 { 00332 ogg_stream_clear(oggstream); 00333 delete oggstream; 00334 } 00335 00336 // destroy ogg sync layer 00337 delete oggpage; 00338 ogg_sync_clear(oggsync); 00339 delete oggsync; 00340 } 00341 00342 00343 bool WvOggVorbisDecoder::isheaderok() const 00344 { 00345 return need_headers == 0; 00346 } 00347 00348 00349 bool WvOggVorbisDecoder::_typedencode(IBuffer &inbuf, OBuffer &outbuf, 00350 bool flush) 00351 { 00352 bool checkheaderok = ! isheaderok() && ! flush; 00353 for (;;) 00354 { 00355 if (oggstream) 00356 { 00357 // extract packets from the bitstream 00358 ogg_packet oggpacket; 00359 while (ogg_stream_packetout(oggstream, & oggpacket) > 0) 00360 { 00361 if (! process_packet(& oggpacket, outbuf)) 00362 return false; 00363 } 00364 00365 // detect end of stream 00366 if (oggstream->e_o_s) 00367 { 00368 setfinished(); 00369 return true; 00370 } 00371 } 00372 00373 // get more pages 00374 while (ogg_sync_pageseek(oggsync, oggpage) <= 0) 00375 { 00376 // read in more data 00377 size_t oggbufsize = inbuf.used(); 00378 if (oggbufsize == 0) 00379 { 00380 // no more data 00381 if (flush && oggsync->fill != 0) 00382 return false; 00383 return true; 00384 } 00385 if (oggbufsize > OGG_VORBIS_DECODER_BUF_SIZE) 00386 oggbufsize = OGG_VORBIS_DECODER_BUF_SIZE; 00387 00388 char *oggbuf = ogg_sync_buffer(oggsync, oggbufsize); 00389 if (oggbuf == NULL) 00390 { 00391 seterror("error allocating ogg sync buffer"); 00392 return false; 00393 } 00394 inbuf.move(oggbuf, oggbufsize); 00395 ogg_sync_wrote(oggsync, oggbufsize); 00396 } 00397 // we got a page! 00398 if (! process_page(oggpage, outbuf)) 00399 return false; 00400 00401 // return immediately after we see the header if not flushing 00402 // guarantee no data has been decoded yet since Ogg Vorbis 00403 // spec says that the audio data must begin on a fresh page 00404 // following the headers 00405 if (checkheaderok && isheaderok()) 00406 return true; 00407 } 00408 } 00409 00410 00411 bool WvOggVorbisDecoder::_typedfinish(OBuffer &outbuf) 00412 { 00413 if (! isheaderok()) 00414 { 00415 seterror("failed to detect an Ogg Vorbis stream"); 00416 return false; 00417 } 00418 return true; 00419 } 00420 00421 00422 bool WvOggVorbisDecoder::process_page(ogg_page *oggpage, 00423 OBuffer &outbuf) 00424 { 00425 if (need_serialno) 00426 { 00427 // attach to the first bitstream we find 00428 long serialno = ogg_page_serialno(oggpage); 00429 if (! prepare_stream(serialno)) 00430 return false; 00431 need_serialno = false; 00432 } 00433 // submit the page to the bitstream 00434 if (ogg_stream_pagein(oggstream, oggpage) != 0) 00435 { 00436 // this page was bad, or did not match the stream's 00437 // serial number exactly, skip it 00438 return true; 00439 } 00440 return true; 00441 } 00442 00443 00444 bool WvOggVorbisDecoder::process_packet(ogg_packet *oggpacket, 00445 OBuffer &outbuf) 00446 { 00447 if (need_headers > 0) 00448 { 00449 // read headers 00450 int result = vorbis_synthesis_headerin(ovinfo, 00451 ovcomment, oggpacket); 00452 if (result != 0) 00453 { 00454 seterror("error %s reading vorbis headers " 00455 "(not a vorbis stream?)", result); 00456 return false; 00457 } 00458 if (--need_headers == 0) 00459 return prepare_dsp(); 00460 } 00461 else 00462 { 00463 // process a block of Vorbis data 00464 if (vorbis_synthesis(ovblock, oggpacket) != 0) 00465 { 00466 // bad data? skip it! 00467 return true; 00468 } 00469 vorbis_synthesis_blockin(ovdsp, ovblock); 00470 00471 // synthesize PCM audio 00472 for (;;) { 00473 float **pcm; 00474 long samples = vorbis_synthesis_pcmout(ovdsp, &pcm); 00475 if (samples == 0) break; 00476 00477 outbuf.put(pcm[0], samples); 00478 vorbis_synthesis_read(ovdsp, samples); 00479 } 00480 } 00481 return true; 00482 } 00483 00484 00485 bool WvOggVorbisDecoder::prepare_dsp() 00486 { 00487 // extract comments 00488 for (int i = 0; i < ovcomment->comments; ++i) 00489 { 00490 char *c = ovcomment->user_comments[i]; 00491 if (c) 00492 comment_list.append(new WvString(c), true); 00493 } 00494 00495 // prepare OggVorbis dsp state 00496 int retval; 00497 ovdsp = new vorbis_dsp_state; 00498 if ((retval = vorbis_synthesis_init(ovdsp, ovinfo)) != 0) 00499 { 00500 seterror("error %s during vorbis_synthesis_init", retval); 00501 return false; 00502 } 00503 ovblock = new vorbis_block; 00504 if ((retval = vorbis_block_init(ovdsp, ovblock)) != 0) 00505 { 00506 seterror("error %s during vorbis_block_init", retval); 00507 return false; 00508 } 00509 return true; 00510 } 00511 00512 00513 bool WvOggVorbisDecoder::prepare_stream(long serialno) 00514 { 00515 // init ogg bitstream layer 00516 oggstream = new ogg_stream_state; 00517 int retval; 00518 if ((retval = ogg_stream_init(oggstream, serialno)) != 0) 00519 { 00520 seterror("error %s during ogg_stream_init", retval); 00521 return false; 00522 } 00523 00524 // init vorbis codec layer 00525 ovinfo = new vorbis_info; 00526 vorbis_info_init(ovinfo); // cannot fail 00527 ovcomment = new vorbis_comment; 00528 vorbis_comment_init(ovcomment); // cannot fail 00529 return true; 00530 }

Generated on Tue Oct 5 01:09:20 2004 for WvStreams by doxygen 1.3.7