00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "asterisk.h"
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 233694 $")
00036
00037 #include <vorbis/codec.h>
00038 #include <vorbis/vorbisenc.h>
00039
00040 #ifdef _WIN32
00041 #include <io.h>
00042 #endif
00043
00044 #include "asterisk/mod_format.h"
00045 #include "asterisk/module.h"
00046
00047
00048
00049
00050
00051 #define SAMPLES_MAX 160
00052 #define BUF_SIZE (2*SAMPLES_MAX)
00053
00054 #define BLOCK_SIZE 4096
00055
00056 struct vorbis_desc {
00057
00058 ogg_sync_state oy;
00059 ogg_stream_state os;
00060 ogg_page og;
00061 ogg_packet op;
00062
00063
00064 vorbis_info vi;
00065 vorbis_comment vc;
00066 vorbis_dsp_state vd;
00067 vorbis_block vb;
00068
00069
00070 int writing;
00071
00072
00073 int eos;
00074 };
00075
00076
00077
00078
00079
00080
00081 static int ogg_vorbis_open(struct ast_filestream *s)
00082 {
00083 int i;
00084 int bytes;
00085 int result;
00086 char **ptr;
00087 char *buffer;
00088 struct vorbis_desc *tmp = (struct vorbis_desc *)s->_private;
00089
00090 tmp->writing = 0;
00091
00092 ogg_sync_init(&tmp->oy);
00093
00094 buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE);
00095 bytes = fread(buffer, 1, BLOCK_SIZE, s->f);
00096 ogg_sync_wrote(&tmp->oy, bytes);
00097
00098 result = ogg_sync_pageout(&tmp->oy, &tmp->og);
00099 if (result != 1) {
00100 if(bytes < BLOCK_SIZE) {
00101 ast_log(LOG_ERROR, "Run out of data...\n");
00102 } else {
00103 ast_log(LOG_ERROR, "Input does not appear to be an Ogg bitstream.\n");
00104 }
00105 ogg_sync_clear(&tmp->oy);
00106 return -1;
00107 }
00108
00109 ogg_stream_init(&tmp->os, ogg_page_serialno(&tmp->og));
00110 vorbis_info_init(&tmp->vi);
00111 vorbis_comment_init(&tmp->vc);
00112
00113 if (ogg_stream_pagein(&tmp->os, &tmp->og) < 0) {
00114 ast_log(LOG_ERROR, "Error reading first page of Ogg bitstream data.\n");
00115 error:
00116 ogg_stream_clear(&tmp->os);
00117 vorbis_comment_clear(&tmp->vc);
00118 vorbis_info_clear(&tmp->vi);
00119 ogg_sync_clear(&tmp->oy);
00120 return -1;
00121 }
00122
00123 if (ogg_stream_packetout(&tmp->os, &tmp->op) != 1) {
00124 ast_log(LOG_ERROR, "Error reading initial header packet.\n");
00125 goto error;
00126 }
00127
00128 if (vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op) < 0) {
00129 ast_log(LOG_ERROR, "This Ogg bitstream does not contain Vorbis audio data.\n");
00130 goto error;
00131 }
00132
00133 for (i = 0; i < 2 ; ) {
00134 while (i < 2) {
00135 result = ogg_sync_pageout(&tmp->oy, &tmp->og);
00136 if (result == 0)
00137 break;
00138 if (result == 1) {
00139 ogg_stream_pagein(&tmp->os, &tmp->og);
00140 while(i < 2) {
00141 result = ogg_stream_packetout(&tmp->os,&tmp->op);
00142 if(result == 0)
00143 break;
00144 if(result < 0) {
00145 ast_log(LOG_ERROR, "Corrupt secondary header. Exiting.\n");
00146 goto error;
00147 }
00148 vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op);
00149 i++;
00150 }
00151 }
00152 }
00153
00154 buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE);
00155 bytes = fread(buffer, 1, BLOCK_SIZE, s->f);
00156 if (bytes == 0 && i < 2) {
00157 ast_log(LOG_ERROR, "End of file before finding all Vorbis headers!\n");
00158 goto error;
00159 }
00160 ogg_sync_wrote(&tmp->oy, bytes);
00161 }
00162
00163 for (ptr = tmp->vc.user_comments; *ptr; ptr++)
00164 ast_debug(1, "OGG/Vorbis comment: %s\n", *ptr);
00165 ast_debug(1, "OGG/Vorbis bitstream is %d channel, %ldHz\n", tmp->vi.channels, tmp->vi.rate);
00166 ast_debug(1, "OGG/Vorbis file encoded by: %s\n", tmp->vc.vendor);
00167
00168 if (tmp->vi.channels != 1) {
00169 ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n");
00170 goto error;
00171 }
00172
00173 if (tmp->vi.rate != DEFAULT_SAMPLE_RATE) {
00174 ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n");
00175 vorbis_block_clear(&tmp->vb);
00176 vorbis_dsp_clear(&tmp->vd);
00177 goto error;
00178 }
00179
00180 vorbis_synthesis_init(&tmp->vd, &tmp->vi);
00181 vorbis_block_init(&tmp->vd, &tmp->vb);
00182
00183 return 0;
00184 }
00185
00186
00187
00188
00189
00190
00191
00192 static int ogg_vorbis_rewrite(struct ast_filestream *s,
00193 const char *comment)
00194 {
00195 ogg_packet header;
00196 ogg_packet header_comm;
00197 ogg_packet header_code;
00198 struct vorbis_desc *tmp = (struct vorbis_desc *)s->_private;
00199
00200 tmp->writing = 1;
00201
00202 vorbis_info_init(&tmp->vi);
00203
00204 if (vorbis_encode_init_vbr(&tmp->vi, 1, DEFAULT_SAMPLE_RATE, 0.4)) {
00205 ast_log(LOG_ERROR, "Unable to initialize Vorbis encoder!\n");
00206 return -1;
00207 }
00208
00209 vorbis_comment_init(&tmp->vc);
00210 vorbis_comment_add_tag(&tmp->vc, "ENCODER", "Asterisk PBX");
00211 if (comment)
00212 vorbis_comment_add_tag(&tmp->vc, "COMMENT", (char *) comment);
00213
00214 vorbis_analysis_init(&tmp->vd, &tmp->vi);
00215 vorbis_block_init(&tmp->vd, &tmp->vb);
00216
00217 ogg_stream_init(&tmp->os, ast_random());
00218
00219 vorbis_analysis_headerout(&tmp->vd, &tmp->vc, &header, &header_comm,
00220 &header_code);
00221 ogg_stream_packetin(&tmp->os, &header);
00222 ogg_stream_packetin(&tmp->os, &header_comm);
00223 ogg_stream_packetin(&tmp->os, &header_code);
00224
00225 while (!tmp->eos) {
00226 if (ogg_stream_flush(&tmp->os, &tmp->og) == 0)
00227 break;
00228 if (!fwrite(tmp->og.header, 1, tmp->og.header_len, s->f)) {
00229 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00230 }
00231 if (!fwrite(tmp->og.body, 1, tmp->og.body_len, s->f)) {
00232 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00233 }
00234 if (ogg_page_eos(&tmp->og))
00235 tmp->eos = 1;
00236 }
00237
00238 return 0;
00239 }
00240
00241
00242
00243
00244
00245
00246 static void write_stream(struct vorbis_desc *s, FILE *f)
00247 {
00248 while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
00249 vorbis_analysis(&s->vb, NULL);
00250 vorbis_bitrate_addblock(&s->vb);
00251
00252 while (vorbis_bitrate_flushpacket(&s->vd, &s->op)) {
00253 ogg_stream_packetin(&s->os, &s->op);
00254 while (!s->eos) {
00255 if (ogg_stream_pageout(&s->os, &s->og) == 0) {
00256 break;
00257 }
00258 if (!fwrite(s->og.header, 1, s->og.header_len, f)) {
00259 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00260 }
00261 if (!fwrite(s->og.body, 1, s->og.body_len, f)) {
00262 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00263 }
00264 if (ogg_page_eos(&s->og)) {
00265 s->eos = 1;
00266 }
00267 }
00268 }
00269 }
00270 }
00271
00272
00273
00274
00275
00276
00277
00278 static int ogg_vorbis_write(struct ast_filestream *fs, struct ast_frame *f)
00279 {
00280 int i;
00281 float **buffer;
00282 short *data;
00283 struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00284
00285 if (!s->writing) {
00286 ast_log(LOG_ERROR, "This stream is not set up for writing!\n");
00287 return -1;
00288 }
00289
00290 if (f->frametype != AST_FRAME_VOICE) {
00291 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
00292 return -1;
00293 }
00294 if (f->subclass != AST_FORMAT_SLINEAR) {
00295 ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n",
00296 f->subclass);
00297 return -1;
00298 }
00299 if (!f->datalen)
00300 return -1;
00301
00302 data = (short *) f->data.ptr;
00303
00304 buffer = vorbis_analysis_buffer(&s->vd, f->samples);
00305
00306 for (i = 0; i < f->samples; i++)
00307 buffer[0][i] = (double)data[i] / 32768.0;
00308
00309 vorbis_analysis_wrote(&s->vd, f->samples);
00310
00311 write_stream(s, fs->f);
00312
00313 return 0;
00314 }
00315
00316
00317
00318
00319
00320 static void ogg_vorbis_close(struct ast_filestream *fs)
00321 {
00322 struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00323
00324 if (s->writing) {
00325
00326
00327 vorbis_analysis_wrote(&s->vd, 0);
00328 write_stream(s, fs->f);
00329 }
00330
00331 ogg_stream_clear(&s->os);
00332 vorbis_block_clear(&s->vb);
00333 vorbis_dsp_clear(&s->vd);
00334 vorbis_comment_clear(&s->vc);
00335 vorbis_info_clear(&s->vi);
00336
00337 if (s->writing) {
00338 ogg_sync_clear(&s->oy);
00339 }
00340 }
00341
00342
00343
00344
00345
00346
00347
00348 static int read_samples(struct ast_filestream *fs, float ***pcm)
00349 {
00350 int samples_in;
00351 int result;
00352 char *buffer;
00353 int bytes;
00354 struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00355
00356 while (1) {
00357 samples_in = vorbis_synthesis_pcmout(&s->vd, pcm);
00358 if (samples_in > 0) {
00359 return samples_in;
00360 }
00361
00362
00363
00364 result = ogg_stream_packetout(&s->os, &s->op);
00365 if (result > 0) {
00366
00367 if (vorbis_synthesis(&s->vb, &s->op) == 0) {
00368 vorbis_synthesis_blockin(&s->vd, &s->vb);
00369 }
00370
00371 continue;
00372 }
00373
00374 if (result < 0)
00375 ast_log(LOG_WARNING,
00376 "Corrupt or missing data at this page position; continuing...\n");
00377
00378
00379
00380 if (s->eos) {
00381
00382 return -1;
00383 }
00384
00385 while (!s->eos) {
00386
00387 result = ogg_sync_pageout(&s->oy, &s->og);
00388 if (result > 0) {
00389
00390
00391 result = ogg_stream_pagein(&s->os, &s->og);
00392 if (result == 0) {
00393
00394 if (ogg_page_eos(&s->og)) {
00395 s->eos = 1;
00396 }
00397 break;
00398 }
00399 ast_log(LOG_WARNING,
00400 "Invalid page in the bitstream; continuing...\n");
00401 }
00402
00403 if (result < 0)
00404 ast_log(LOG_WARNING,
00405 "Corrupt or missing data in bitstream; continuing...\n");
00406
00407
00408
00409 buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE);
00410
00411 bytes = fread(buffer, 1, BLOCK_SIZE, fs->f);
00412
00413 ogg_sync_wrote(&s->oy, bytes);
00414 if (bytes == 0) {
00415 s->eos = 1;
00416 }
00417 }
00418 }
00419 }
00420
00421
00422
00423
00424
00425
00426
00427 static struct ast_frame *ogg_vorbis_read(struct ast_filestream *fs,
00428 int *whennext)
00429 {
00430 int clipflag = 0;
00431 int i;
00432 int j;
00433 double accumulator[SAMPLES_MAX];
00434 int val;
00435 int samples_in;
00436 int samples_out = 0;
00437 struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00438 short *buf;
00439
00440 fs->fr.frametype = AST_FRAME_VOICE;
00441 fs->fr.subclass = AST_FORMAT_SLINEAR;
00442 fs->fr.mallocd = 0;
00443 AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
00444 buf = (short *)(fs->fr.data.ptr);
00445
00446 while (samples_out != SAMPLES_MAX) {
00447 float **pcm;
00448 int len = SAMPLES_MAX - samples_out;
00449
00450
00451 samples_in = read_samples(fs, &pcm);
00452 if (samples_in <= 0)
00453 break;
00454
00455
00456
00457
00458 clipflag = 0;
00459 if (samples_in > len)
00460 samples_in = len;
00461 for (j = 0; j < samples_in; j++)
00462 accumulator[j] = 0.0;
00463
00464 for (i = 0; i < s->vi.channels; i++) {
00465 float *mono = pcm[i];
00466 for (j = 0; j < samples_in; j++)
00467 accumulator[j] += mono[j];
00468 }
00469
00470 for (j = 0; j < samples_in; j++) {
00471 val = accumulator[j] * 32767.0 / s->vi.channels;
00472 if (val > 32767) {
00473 val = 32767;
00474 clipflag = 1;
00475 } else if (val < -32768) {
00476 val = -32768;
00477 clipflag = 1;
00478 }
00479 buf[samples_out + j] = val;
00480 }
00481
00482 if (clipflag)
00483 ast_log(LOG_WARNING, "Clipping in frame %ld\n", (long) (s->vd.sequence));
00484
00485 vorbis_synthesis_read(&s->vd, samples_in);
00486 samples_out += samples_in;
00487 }
00488
00489 if (samples_out > 0) {
00490 fs->fr.datalen = samples_out * 2;
00491 fs->fr.samples = samples_out;
00492 *whennext = samples_out;
00493
00494 return &fs->fr;
00495 } else {
00496 return NULL;
00497 }
00498 }
00499
00500
00501
00502
00503
00504
00505
00506 static int ogg_vorbis_trunc(struct ast_filestream *s)
00507 {
00508 ast_log(LOG_WARNING, "Truncation is not supported on OGG/Vorbis streams!\n");
00509 return -1;
00510 }
00511
00512
00513
00514
00515
00516
00517
00518
00519 static int ogg_vorbis_seek(struct ast_filestream *s, off_t sample_offset, int whence)
00520 {
00521 ast_log(LOG_WARNING, "Seeking is not supported on OGG/Vorbis streams!\n");
00522 return -1;
00523 }
00524
00525 static off_t ogg_vorbis_tell(struct ast_filestream *s)
00526 {
00527 ast_log(LOG_WARNING, "Telling is not supported on OGG/Vorbis streams!\n");
00528 return -1;
00529 }
00530
00531 static const struct ast_format vorbis_f = {
00532 .name = "ogg_vorbis",
00533 .exts = "ogg",
00534 .format = AST_FORMAT_SLINEAR,
00535 .open = ogg_vorbis_open,
00536 .rewrite = ogg_vorbis_rewrite,
00537 .write = ogg_vorbis_write,
00538 .seek = ogg_vorbis_seek,
00539 .trunc = ogg_vorbis_trunc,
00540 .tell = ogg_vorbis_tell,
00541 .read = ogg_vorbis_read,
00542 .close = ogg_vorbis_close,
00543 .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
00544 .desc_size = sizeof(struct vorbis_desc),
00545 };
00546
00547 static int load_module(void)
00548 {
00549 if (ast_format_register(&vorbis_f))
00550 return AST_MODULE_LOAD_FAILURE;
00551 return AST_MODULE_LOAD_SUCCESS;
00552 }
00553
00554 static int unload_module(void)
00555 {
00556 return ast_format_unregister(vorbis_f.name);
00557 }
00558
00559 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Vorbis audio",
00560 .load = load_module,
00561 .unload = unload_module,
00562 .load_pri = 10,
00563 );