Sat Aug 6 00:39:28 2011

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

Generated on Sat Aug 6 00:39:28 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7