00001
00002
00003
00004
00005
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
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
00027
if (serialno ==
RANDOM_SERIALNO)
00028 {
00029 serialno = rand();
00030 }
00031
00032
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
00042 ovinfo =
new vorbis_info;
00043 vorbis_info_init(ovinfo);
00044
00045 ovcomment =
new vorbis_comment;
00046 vorbis_comment_init(ovcomment);
00047
00048
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
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
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
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
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
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
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
00144
if (! wrote_header)
00145 {
00146
if (! write_header(outbuf))
00147
return false;
00148 wrote_header =
true;
00149 }
00150
00151
00152
for (;;)
00153 {
00154
00155 size_t ovsamples = inbuf.used();
00156
if (ovsamples == 0)
00157 {
00158
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
00183
if (! wrote_header)
00184 {
00185
if (! write_header(outbuf))
00186
return false;
00187 wrote_header =
true;
00188 }
00189
00190
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
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
00210
for (
int i = 0; i < 3; ++i)
00211 ogg_stream_packetin(oggstream, & headers[i]);
00212
00213
00214
return write_stream(outbuf,
true );
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;
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;
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
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
00271 ogg_packet oggpacket;
00272
while (vorbis_bitrate_flushpacket(ovdsp, & oggpacket) > 0)
00273 {
00274 ogg_stream_packetin(oggstream, & oggpacket);
00275
if (! write_stream(outbuf,
false))
00276
return false;
00277 }
00278 }
00279
return true;
00280 }
00281
00282
00283
00284
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
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
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
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
00330
if (oggstream)
00331 {
00332 ogg_stream_clear(oggstream);
00333
delete oggstream;
00334 }
00335
00336
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
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
00366
if (oggstream->e_o_s)
00367 {
00368
setfinished();
00369
return true;
00370 }
00371 }
00372
00373
00374
while (ogg_sync_pageseek(oggsync, oggpage) <= 0)
00375 {
00376
00377 size_t oggbufsize = inbuf.used();
00378
if (oggbufsize == 0)
00379 {
00380
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
00398
if (! process_page(oggpage, outbuf))
00399
return false;
00400
00401
00402
00403
00404
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
00428
long serialno = ogg_page_serialno(oggpage);
00429
if (! prepare_stream(serialno))
00430
return false;
00431 need_serialno =
false;
00432 }
00433
00434
if (ogg_stream_pagein(oggstream, oggpage) != 0)
00435 {
00436
00437
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
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
00464
if (vorbis_synthesis(ovblock, oggpacket) != 0)
00465 {
00466
00467
return true;
00468 }
00469 vorbis_synthesis_blockin(ovdsp, ovblock);
00470
00471
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
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
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
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
00525 ovinfo =
new vorbis_info;
00526 vorbis_info_init(ovinfo);
00527 ovcomment =
new vorbis_comment;
00528 vorbis_comment_init(ovcomment);
00529
return true;
00530 }