Sat Apr 12 07:12:24 2008

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: 90155 $")
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       fwrite(tmp->og.header, 1, tmp->og.header_len, s->f);
00243       fwrite(tmp->og.body, 1, tmp->og.body_len, s->f);
00244       if (ogg_page_eos(&tmp->og))
00245          tmp->eos = 1;
00246    }
00247 
00248    return 0;
00249 }
00250 
00251 /*!
00252  * \brief Write out any pending encoded data.
00253  * \param s An OGG/Vorbis filestream.
00254  * \param f The file to write to.
00255  */
00256 static void write_stream(struct vorbis_desc *s, FILE *f)
00257 {
00258    while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
00259       vorbis_analysis(&s->vb, NULL);
00260       vorbis_bitrate_addblock(&s->vb);
00261 
00262       while (vorbis_bitrate_flushpacket(&s->vd, &s->op)) {
00263          ogg_stream_packetin(&s->os, &s->op);
00264          while (!s->eos) {
00265             if (ogg_stream_pageout(&s->os, &s->og) == 0) {
00266                break;
00267             }
00268             fwrite(s->og.header, 1, s->og.header_len, f);
00269             fwrite(s->og.body, 1, s->og.body_len, f);
00270             if (ogg_page_eos(&s->og)) {
00271                s->eos = 1;
00272             }
00273          }
00274       }
00275    }
00276 }
00277 
00278 /*!
00279  * \brief Write audio data from a frame to an OGG/Vorbis filestream.
00280  * \param fs An OGG/Vorbis filestream.
00281  * \param f A frame containing audio to be written to the filestream.
00282  * \return -1 if there was an error, 0 on success.
00283  */
00284 static int ogg_vorbis_write(struct ast_filestream *fs, struct ast_frame *f)
00285 {
00286    int i;
00287    float **buffer;
00288    short *data;
00289    struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00290 
00291    if (!s->writing) {
00292       ast_log(LOG_ERROR, "This stream is not set up for writing!\n");
00293       return -1;
00294    }
00295 
00296    if (f->frametype != AST_FRAME_VOICE) {
00297       ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
00298       return -1;
00299    }
00300    if (f->subclass != AST_FORMAT_SLINEAR) {
00301       ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n",
00302             f->subclass);
00303       return -1;
00304    }
00305    if (!f->datalen)
00306       return -1;
00307 
00308    data = (short *) f->data;
00309 
00310    buffer = vorbis_analysis_buffer(&s->vd, f->samples);
00311 
00312    for (i = 0; i < f->samples; i++)
00313       buffer[0][i] = (double)data[i] / 32768.0;
00314 
00315    vorbis_analysis_wrote(&s->vd, f->samples);
00316 
00317    write_stream(s, fs->f);
00318 
00319    return 0;
00320 }
00321 
00322 /*!
00323  * \brief Close a OGG/Vorbis filestream.
00324  * \param fs A OGG/Vorbis filestream.
00325  */
00326 static void ogg_vorbis_close(struct ast_filestream *fs)
00327 {
00328    struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00329 
00330    if (s->writing) {
00331       /* Tell the Vorbis encoder that the stream is finished
00332        * and write out the rest of the data */
00333       vorbis_analysis_wrote(&s->vd, 0);
00334       write_stream(s, fs->f);
00335    }
00336 
00337    ogg_stream_clear(&s->os);
00338    vorbis_block_clear(&s->vb);
00339    vorbis_dsp_clear(&s->vd);
00340    vorbis_comment_clear(&s->vc);
00341    vorbis_info_clear(&s->vi);
00342 
00343    if (s->writing) {
00344       ogg_sync_clear(&s->oy);
00345    }
00346 }
00347 
00348 /*!
00349  * \brief Get audio data.
00350  * \param fs An OGG/Vorbis filestream.
00351  * \param pcm Pointer to a buffere to store audio data in.
00352  */
00353 
00354 static int read_samples(struct ast_filestream *fs, float ***pcm)
00355 {
00356    int samples_in;
00357    int result;
00358    char *buffer;
00359    int bytes;
00360    struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00361 
00362    while (1) {
00363       samples_in = vorbis_synthesis_pcmout(&s->vd, pcm);
00364       if (samples_in > 0) {
00365          return samples_in;
00366       }
00367 
00368       /* The Vorbis decoder needs more data... */
00369       /* See ifOGG has any packets in the current page for the Vorbis decoder. */
00370       result = ogg_stream_packetout(&s->os, &s->op);
00371       if (result > 0) {
00372          /* Yes OGG had some more packets for the Vorbis decoder. */
00373          if (vorbis_synthesis(&s->vb, &s->op) == 0) {
00374             vorbis_synthesis_blockin(&s->vd, &s->vb);
00375          }
00376 
00377          continue;
00378       }
00379 
00380       if (result < 0)
00381          ast_log(LOG_WARNING,
00382                "Corrupt or missing data at this page position; continuing...\n");
00383 
00384       /* No more packets left in the current page... */
00385 
00386       if (s->eos) {
00387          /* No more pages left in the stream */
00388          return -1;
00389       }
00390 
00391       while (!s->eos) {
00392          /* See ifOGG has any pages in it's internal buffers */
00393          result = ogg_sync_pageout(&s->oy, &s->og);
00394          if (result > 0) {
00395             /* Yes, OGG has more pages in it's internal buffers,
00396                add the page to the stream state */
00397             result = ogg_stream_pagein(&s->os, &s->og);
00398             if (result == 0) {
00399                /* Yes, got a new,valid page */
00400                if (ogg_page_eos(&s->og)) {
00401                   s->eos = 1;
00402                }
00403                break;
00404             }
00405             ast_log(LOG_WARNING,
00406                   "Invalid page in the bitstream; continuing...\n");
00407          }
00408 
00409          if (result < 0)
00410             ast_log(LOG_WARNING,
00411                   "Corrupt or missing data in bitstream; continuing...\n");
00412 
00413          /* No, we need to read more data from the file descrptor */
00414          /* get a buffer from OGG to read the data into */
00415          buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE);
00416          /* read more data from the file descriptor */
00417          bytes = fread(buffer, 1, BLOCK_SIZE, fs->f);
00418          /* Tell OGG how many bytes we actually read into the buffer */
00419          ogg_sync_wrote(&s->oy, bytes);
00420          if (bytes == 0) {
00421             s->eos = 1;
00422          }
00423       }
00424    }
00425 }
00426 
00427 /*!
00428  * \brief Read a frame full of audio data from the filestream.
00429  * \param fs The filestream.
00430  * \param whennext Number of sample times to schedule the next call.
00431  * \return A pointer to a frame containing audio data or NULL ifthere is no more audio data.
00432  */
00433 static struct ast_frame *ogg_vorbis_read(struct ast_filestream *fs,
00434                 int *whennext)
00435 {
00436    int clipflag = 0;
00437    int i;
00438    int j;
00439    double accumulator[SAMPLES_MAX];
00440    int val;
00441    int samples_in;
00442    int samples_out = 0;
00443    struct vorbis_desc *s = (struct vorbis_desc *)fs->_private;
00444    short *buf; /* SLIN data buffer */
00445 
00446    fs->fr.frametype = AST_FRAME_VOICE;
00447    fs->fr.subclass = AST_FORMAT_SLINEAR;
00448    fs->fr.mallocd = 0;
00449    AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
00450    buf = (short *)(fs->fr.data); /* SLIN data buffer */
00451 
00452    while (samples_out != SAMPLES_MAX) {
00453       float **pcm;
00454       int len = SAMPLES_MAX - samples_out;
00455 
00456       /* See ifVorbis decoder has some audio data for us ... */
00457       samples_in = read_samples(fs, &pcm);
00458       if (samples_in <= 0)
00459          break;
00460 
00461       /* Got some audio data from Vorbis... */
00462       /* Convert the float audio data to 16-bit signed linear */
00463 
00464       clipflag = 0;
00465       if (samples_in > len)
00466          samples_in = len;
00467       for (j = 0; j < samples_in; j++)
00468          accumulator[j] = 0.0;
00469 
00470       for (i = 0; i < s->vi.channels; i++) {
00471          float *mono = pcm[i];
00472          for (j = 0; j < samples_in; j++)
00473             accumulator[j] += mono[j];
00474       }
00475 
00476       for (j = 0; j < samples_in; j++) {
00477          val = accumulator[j] * 32767.0 / s->vi.channels;
00478          if (val > 32767) {
00479             val = 32767;
00480             clipflag = 1;
00481          } else if (val < -32768) {
00482             val = -32768;
00483             clipflag = 1;
00484          }
00485          buf[samples_out + j] = val;
00486       }
00487 
00488       if (clipflag)
00489          ast_log(LOG_WARNING, "Clipping in frame %ld\n", (long) (s->vd.sequence));
00490       /* Tell the Vorbis decoder how many samples we actually used. */
00491       vorbis_synthesis_read(&s->vd, samples_in);
00492       samples_out += samples_in;
00493    }
00494 
00495    if (samples_out > 0) {
00496       fs->fr.datalen = samples_out * 2;
00497       fs->fr.samples = samples_out;
00498       *whennext = samples_out;
00499 
00500       return &fs->fr;
00501    } else {
00502       return NULL;
00503    }
00504 }
00505 
00506 /*!
00507  * \brief Trucate an OGG/Vorbis filestream.
00508  * \param s The filestream to truncate.
00509  * \return 0 on success, -1 on failure.
00510  */
00511 
00512 static int ogg_vorbis_trunc(struct ast_filestream *s)
00513 {
00514    ast_log(LOG_WARNING, "Truncation is not supported on OGG/Vorbis streams!\n");
00515    return -1;
00516 }
00517 
00518 /*!
00519  * \brief Seek to a specific position in an OGG/Vorbis filestream.
00520  * \param s The filestream to truncate.
00521  * \param sample_offset New position for the filestream, measured in 8KHz samples.
00522  * \param whence Location to measure 
00523  * \return 0 on success, -1 on failure.
00524  */
00525 static int ogg_vorbis_seek(struct ast_filestream *s, off_t sample_offset, int whence)
00526 {
00527    ast_log(LOG_WARNING, "Seeking is not supported on OGG/Vorbis streams!\n");
00528    return -1;
00529 }
00530 
00531 static off_t ogg_vorbis_tell(struct ast_filestream *s)
00532 {
00533    ast_log(LOG_WARNING, "Telling is not supported on OGG/Vorbis streams!\n");
00534    return -1;
00535 }
00536 
00537 static const struct ast_format vorbis_f = {
00538    .name = "ogg_vorbis",
00539    .exts = "ogg",
00540    .format = AST_FORMAT_SLINEAR,
00541    .open = ogg_vorbis_open,
00542    .rewrite = ogg_vorbis_rewrite,
00543    .write = ogg_vorbis_write,
00544    .seek =  ogg_vorbis_seek,
00545    .trunc = ogg_vorbis_trunc,
00546    .tell = ogg_vorbis_tell,
00547    .read = ogg_vorbis_read,
00548    .close = ogg_vorbis_close,
00549    .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
00550    .desc_size = sizeof(struct vorbis_desc),
00551 };
00552 
00553 static int load_module(void)
00554 {
00555    return ast_format_register(&vorbis_f);
00556 }
00557 
00558 static int unload_module(void)
00559 {
00560    return ast_format_unregister(vorbis_f.name);
00561 }
00562 
00563 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OGG/Vorbis audio");
00564 

Generated on Sat Apr 12 07:12:24 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5