Mon Mar 19 11:30:27 2012

Asterisk developer's documentation


format_ogg_vorbis.c

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

Generated on Mon Mar 19 11:30:27 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7