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
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 361471 $")
00037
00038 #include <vorbis/codec.h>
00039 #include <vorbis/vorbisenc.h>
00040 #include <vorbis/vorbisfile.h>
00041
00042 #ifdef _WIN32
00043 #include <io.h>
00044 #endif
00045
00046 #include "asterisk/mod_format.h"
00047 #include "asterisk/module.h"
00048
00049
00050
00051
00052
00053 #define SAMPLES_MAX 512
00054 #define BUF_SIZE (2*SAMPLES_MAX)
00055
00056 #define BLOCK_SIZE 4096
00057
00058 struct ogg_vorbis_desc {
00059
00060 OggVorbis_File ov_f;
00061
00062
00063 ogg_stream_state os;
00064 ogg_page og;
00065 ogg_packet op;
00066
00067
00068 vorbis_info vi;
00069 vorbis_comment vc;
00070 vorbis_dsp_state vd;
00071 vorbis_block vb;
00072
00073
00074 int writing;
00075
00076
00077 off_t writing_pcm_pos;
00078
00079
00080 int eos;
00081 };
00082
00083 #if !defined(HAVE_VORBIS_OPEN_CALLBACKS)
00084
00085
00086
00087
00088 static int _ov_header_fseek_wrap(FILE *f, ogg_int64_t off, int whence)
00089 {
00090 if (f == NULL) {
00091 return -1;
00092 }
00093 return fseek(f, off, whence);
00094 }
00095
00096 static ov_callbacks OV_CALLBACKS_NOCLOSE = {
00097 (size_t (*)(void *, size_t, size_t, void *)) fread,
00098 (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap,
00099 (int (*)(void *)) NULL,
00100 (long (*)(void *)) ftell
00101 };
00102 #endif
00103
00104
00105
00106
00107
00108
00109 static int ogg_vorbis_open(struct ast_filestream *s)
00110 {
00111 int result;
00112 struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) s->_private;
00113
00114
00115 memset(desc, 0, sizeof(struct ogg_vorbis_desc));
00116 desc->writing = 0;
00117
00118
00119 result = ov_open_callbacks(s->f, &desc->ov_f, NULL, 0, OV_CALLBACKS_NOCLOSE);
00120 if (result != 0) {
00121 ast_log(LOG_ERROR, "Error opening Ogg/Vorbis file stream.\n");
00122 return -1;
00123 }
00124
00125
00126 if (desc->ov_f.vi->channels != 1) {
00127 ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n");
00128 ov_clear(&desc->ov_f);
00129 return -1;
00130 }
00131
00132 if (desc->ov_f.vi->rate != DEFAULT_SAMPLE_RATE) {
00133 ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n");
00134 ov_clear(&desc->ov_f);
00135 return -1;
00136 }
00137
00138 return 0;
00139 }
00140
00141
00142
00143
00144
00145
00146
00147 static int ogg_vorbis_rewrite(struct ast_filestream *s,
00148 const char *comment)
00149 {
00150 ogg_packet header;
00151 ogg_packet header_comm;
00152 ogg_packet header_code;
00153 struct ogg_vorbis_desc *tmp = (struct ogg_vorbis_desc *) s->_private;
00154
00155 tmp->writing = 1;
00156 tmp->writing_pcm_pos = 0;
00157
00158 vorbis_info_init(&tmp->vi);
00159
00160 if (vorbis_encode_init_vbr(&tmp->vi, 1, DEFAULT_SAMPLE_RATE, 0.4)) {
00161 ast_log(LOG_ERROR, "Unable to initialize Vorbis encoder!\n");
00162 return -1;
00163 }
00164
00165 vorbis_comment_init(&tmp->vc);
00166 vorbis_comment_add_tag(&tmp->vc, "ENCODER", "Asterisk PBX");
00167 if (comment)
00168 vorbis_comment_add_tag(&tmp->vc, "COMMENT", (char *) comment);
00169
00170 vorbis_analysis_init(&tmp->vd, &tmp->vi);
00171 vorbis_block_init(&tmp->vd, &tmp->vb);
00172
00173 ogg_stream_init(&tmp->os, ast_random());
00174
00175 vorbis_analysis_headerout(&tmp->vd, &tmp->vc, &header, &header_comm,
00176 &header_code);
00177 ogg_stream_packetin(&tmp->os, &header);
00178 ogg_stream_packetin(&tmp->os, &header_comm);
00179 ogg_stream_packetin(&tmp->os, &header_code);
00180
00181 while (!tmp->eos) {
00182 if (ogg_stream_flush(&tmp->os, &tmp->og) == 0)
00183 break;
00184 if (!fwrite(tmp->og.header, 1, tmp->og.header_len, s->f)) {
00185 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00186 }
00187 if (!fwrite(tmp->og.body, 1, tmp->og.body_len, s->f)) {
00188 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00189 }
00190 if (ogg_page_eos(&tmp->og))
00191 tmp->eos = 1;
00192 }
00193
00194 return 0;
00195 }
00196
00197
00198
00199
00200
00201
00202 static void write_stream(struct ogg_vorbis_desc *s, FILE *f)
00203 {
00204 while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
00205 vorbis_analysis(&s->vb, NULL);
00206 vorbis_bitrate_addblock(&s->vb);
00207
00208 while (vorbis_bitrate_flushpacket(&s->vd, &s->op)) {
00209 ogg_stream_packetin(&s->os, &s->op);
00210 while (!s->eos) {
00211 if (ogg_stream_pageout(&s->os, &s->og) == 0) {
00212 break;
00213 }
00214 if (!fwrite(s->og.header, 1, s->og.header_len, f)) {
00215 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00216 }
00217 if (!fwrite(s->og.body, 1, s->og.body_len, f)) {
00218 ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00219 }
00220 if (ogg_page_eos(&s->og)) {
00221 s->eos = 1;
00222 }
00223 }
00224 }
00225 }
00226 }
00227
00228
00229
00230
00231
00232
00233
00234 static int ogg_vorbis_write(struct ast_filestream *fs, struct ast_frame *f)
00235 {
00236 int i;
00237 float **buffer;
00238 short *data;
00239 struct ogg_vorbis_desc *s = (struct ogg_vorbis_desc *) fs->_private;
00240
00241 if (!s->writing) {
00242 ast_log(LOG_ERROR, "This stream is not set up for writing!\n");
00243 return -1;
00244 }
00245
00246 if (f->frametype != AST_FRAME_VOICE) {
00247 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
00248 return -1;
00249 }
00250 if (f->subclass.codec != AST_FORMAT_SLINEAR) {
00251 ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%s)!\n",
00252 ast_getformatname(f->subclass.codec));
00253 return -1;
00254 }
00255 if (!f->datalen)
00256 return -1;
00257
00258 data = (short *) f->data.ptr;
00259
00260 buffer = vorbis_analysis_buffer(&s->vd, f->samples);
00261
00262 for (i = 0; i < f->samples; i++)
00263 buffer[0][i] = (double)data[i] / 32768.0;
00264
00265 vorbis_analysis_wrote(&s->vd, f->samples);
00266
00267 write_stream(s, fs->f);
00268
00269 s->writing_pcm_pos += f->samples;
00270
00271 return 0;
00272 }
00273
00274
00275
00276
00277
00278 static void ogg_vorbis_close(struct ast_filestream *fs)
00279 {
00280 struct ogg_vorbis_desc *s = (struct ogg_vorbis_desc *) fs->_private;
00281
00282 if (s->writing) {
00283
00284
00285 vorbis_analysis_wrote(&s->vd, 0);
00286 write_stream(s, fs->f);
00287 } else {
00288
00289 ov_clear(&s->ov_f);
00290 }
00291 }
00292
00293
00294
00295
00296
00297
00298
00299 static struct ast_frame *ogg_vorbis_read(struct ast_filestream *fs,
00300 int *whennext)
00301 {
00302 struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) fs->_private;
00303 int current_bitstream = -10;
00304 char *out_buf;
00305 long bytes_read;
00306
00307 if (desc->writing) {
00308 ast_log(LOG_WARNING, "Reading is not suport on OGG/Vorbis on write files.\n");
00309 return NULL;
00310 }
00311
00312
00313 fs->fr.frametype = AST_FRAME_VOICE;
00314 fs->fr.subclass.codec = AST_FORMAT_SLINEAR;
00315 fs->fr.mallocd = 0;
00316 AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
00317 out_buf = (char *) (fs->fr.data.ptr);
00318
00319
00320 bytes_read = ov_read(
00321 &desc->ov_f,
00322 out_buf,
00323 BUF_SIZE,
00324 (__BYTE_ORDER == __BIG_ENDIAN),
00325 2,
00326 1,
00327 ¤t_bitstream
00328 );
00329
00330
00331 if (bytes_read <= 0) {
00332
00333 return NULL;
00334 }
00335
00336
00337 fs->fr.datalen = bytes_read;
00338 fs->fr.samples = bytes_read / 2;
00339 *whennext = fs->fr.samples;
00340 return &fs->fr;
00341 }
00342
00343
00344
00345
00346
00347
00348
00349 static int ogg_vorbis_trunc(struct ast_filestream *fs)
00350 {
00351 ast_log(LOG_WARNING, "Truncation is not supported on OGG/Vorbis streams!\n");
00352 return -1;
00353 }
00354
00355
00356
00357
00358
00359
00360 static off_t ogg_vorbis_tell(struct ast_filestream *fs)
00361 {
00362 off_t pos;
00363 struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) fs->_private;
00364
00365 if (desc->writing) {
00366 return desc->writing_pcm_pos;
00367 }
00368
00369 if ((pos = ov_pcm_tell(&desc->ov_f)) < 0) {
00370 return -1;
00371 }
00372 return pos;
00373 }
00374
00375
00376
00377
00378
00379
00380
00381
00382 static int ogg_vorbis_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
00383 {
00384 int seek_result = -1;
00385 off_t relative_pcm_pos;
00386 struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) fs->_private;
00387
00388 if (desc->writing) {
00389 ast_log(LOG_WARNING, "Seeking is not supported on OGG/Vorbis streams in writing mode!\n");
00390 return -1;
00391 }
00392
00393
00394 switch (whence) {
00395 case SEEK_SET:
00396 seek_result = ov_pcm_seek(&desc->ov_f, sample_offset);
00397 break;
00398 case SEEK_CUR:
00399 if ((relative_pcm_pos = ogg_vorbis_tell(fs)) < 0) {
00400 seek_result = -1;
00401 break;
00402 }
00403 seek_result = ov_pcm_seek(&desc->ov_f, relative_pcm_pos + sample_offset);
00404 break;
00405 case SEEK_END:
00406 if ((relative_pcm_pos = ov_pcm_total(&desc->ov_f, -1)) < 0) {
00407 seek_result = -1;
00408 break;
00409 }
00410 seek_result = ov_pcm_seek(&desc->ov_f, relative_pcm_pos - sample_offset);
00411 break;
00412 default:
00413 ast_log(LOG_WARNING, "Unknown *whence* to seek on OGG/Vorbis streams!\n");
00414 break;
00415 }
00416
00417
00418 return (seek_result == 0) ? 0 : -1;
00419 }
00420
00421 static const struct ast_format vorbis_f = {
00422 .name = "ogg_vorbis",
00423 .exts = "ogg",
00424 .format = AST_FORMAT_SLINEAR,
00425 .open = ogg_vorbis_open,
00426 .rewrite = ogg_vorbis_rewrite,
00427 .write = ogg_vorbis_write,
00428 .seek = ogg_vorbis_seek,
00429 .trunc = ogg_vorbis_trunc,
00430 .tell = ogg_vorbis_tell,
00431 .read = ogg_vorbis_read,
00432 .close = ogg_vorbis_close,
00433 .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
00434 .desc_size = sizeof(struct ogg_vorbis_desc),
00435 };
00436
00437 static int load_module(void)
00438 {
00439 if (ast_format_register(&vorbis_f))
00440 return AST_MODULE_LOAD_FAILURE;
00441 return AST_MODULE_LOAD_SUCCESS;
00442 }
00443
00444 static int unload_module(void)
00445 {
00446 return ast_format_unregister(vorbis_f.name);
00447 }
00448
00449 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Vorbis audio",
00450 .load = load_module,
00451 .unload = unload_module,
00452 .load_pri = AST_MODPRI_APP_DEPEND
00453 );