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: 419592 $")
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 #include "asterisk/format_cache.h"
00049 
00050 /*
00051  * this is the number of samples we deal with. Samples are converted
00052  * to SLINEAR so each one uses 2 bytes in the buffer.
00053  */
00054 #define SAMPLES_MAX 512
00055 #define  BUF_SIZE (2*SAMPLES_MAX)
00056 
00057 #define BLOCK_SIZE 4096    /* used internally in the vorbis routines */
00058 
00059 struct ogg_vorbis_desc {   /* format specific parameters */
00060    /* OggVorbis_File structure for libvorbisfile interface */
00061    OggVorbis_File ov_f;
00062 
00063    /* structures for handling the Ogg container */
00064    ogg_stream_state os;
00065    ogg_page og;
00066    ogg_packet op;
00067    
00068    /* structures for handling Vorbis audio data */
00069    vorbis_info vi;
00070    vorbis_comment vc;
00071    vorbis_dsp_state vd;
00072    vorbis_block vb;
00073    
00074    /*! \brief Indicates whether this filestream is set up for reading or writing. */
00075    int writing;
00076 
00077    /*! \brief Stores the current pcm position to support tell() on writing mode. */
00078    off_t writing_pcm_pos;
00079 
00080    /*! \brief Indicates whether an End of Stream condition has been detected. */
00081    int eos;
00082 };
00083 
00084 #if !defined(HAVE_VORBIS_OPEN_CALLBACKS)
00085 /*
00086  * Declared for backward compatibility with vorbisfile v1.1.2.
00087  * Code taken from vorbisfile.h v1.2.0.
00088  */
00089 static int _ov_header_fseek_wrap(FILE *f, ogg_int64_t off, int whence)
00090 {
00091    if (f == NULL) {
00092       return -1;
00093    }
00094    return fseek(f, off, whence);
00095 }
00096 
00097 static ov_callbacks OV_CALLBACKS_NOCLOSE = {
00098   (size_t (*)(void *, size_t, size_t, void *))  fread,
00099   (int (*)(void *, ogg_int64_t, int))           _ov_header_fseek_wrap,
00100   (int (*)(void *))                             NULL,
00101   (long (*)(void *))                            ftell
00102 };
00103 #endif   /* !defined(HAVE_VORBIS_OPEN_CALLBACKS) */
00104 
00105 /*!
00106  * \brief Create a new OGG/Vorbis filestream and set it up for reading.
00107  * \param s File that points to on disk storage of the OGG/Vorbis data.
00108  * \return The new filestream.
00109  */
00110 static int ogg_vorbis_open(struct ast_filestream *s)
00111 {
00112    int result;
00113    struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) s->_private;
00114 
00115    /* initialize private description block */
00116    memset(desc, 0, sizeof(struct ogg_vorbis_desc));
00117    desc->writing = 0;
00118 
00119    /* actually open file */
00120    result = ov_open_callbacks(s->f, &desc->ov_f, NULL, 0, OV_CALLBACKS_NOCLOSE);
00121    if (result != 0) {
00122       ast_log(LOG_ERROR, "Error opening Ogg/Vorbis file stream.\n");
00123       return -1;
00124    }
00125 
00126    /* check stream(s) type */
00127    if (desc->ov_f.vi->channels != 1) {
00128       ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n");
00129       ov_clear(&desc->ov_f);
00130       return -1;
00131    }
00132 
00133    if (desc->ov_f.vi->rate != DEFAULT_SAMPLE_RATE) {
00134       ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n");
00135       ov_clear(&desc->ov_f);
00136       return -1;
00137    }
00138 
00139    return 0;
00140 }
00141 
00142 /*!
00143  * \brief Create a new OGG/Vorbis filestream and set it up for writing.
00144  * \param s File pointer that points to on-disk storage.
00145  * \param comment Comment that should be embedded in the OGG/Vorbis file.
00146  * \return A new filestream.
00147  */
00148 static int ogg_vorbis_rewrite(struct ast_filestream *s,
00149                    const char *comment)
00150 {
00151    ogg_packet header;
00152    ogg_packet header_comm;
00153    ogg_packet header_code;
00154    struct ogg_vorbis_desc *tmp = (struct ogg_vorbis_desc *) s->_private;
00155 
00156    tmp->writing = 1;
00157    tmp->writing_pcm_pos = 0;
00158 
00159    vorbis_info_init(&tmp->vi);
00160 
00161    if (vorbis_encode_init_vbr(&tmp->vi, 1, DEFAULT_SAMPLE_RATE, 0.4)) {
00162       ast_log(LOG_ERROR, "Unable to initialize Vorbis encoder!\n");
00163       return -1;
00164    }
00165 
00166    vorbis_comment_init(&tmp->vc);
00167    vorbis_comment_add_tag(&tmp->vc, "ENCODER", "Asterisk PBX");
00168    if (comment)
00169       vorbis_comment_add_tag(&tmp->vc, "COMMENT", (char *) comment);
00170 
00171    vorbis_analysis_init(&tmp->vd, &tmp->vi);
00172    vorbis_block_init(&tmp->vd, &tmp->vb);
00173 
00174    ogg_stream_init(&tmp->os, ast_random());
00175 
00176    vorbis_analysis_headerout(&tmp->vd, &tmp->vc, &header, &header_comm,
00177               &header_code);
00178    ogg_stream_packetin(&tmp->os, &header);
00179    ogg_stream_packetin(&tmp->os, &header_comm);
00180    ogg_stream_packetin(&tmp->os, &header_code);
00181 
00182    while (!tmp->eos) {
00183       if (ogg_stream_flush(&tmp->os, &tmp->og) == 0)
00184          break;
00185       if (!fwrite(tmp->og.header, 1, tmp->og.header_len, s->f)) {
00186          ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00187       }
00188       if (!fwrite(tmp->og.body, 1, tmp->og.body_len, s->f)) {
00189          ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00190       }
00191       if (ogg_page_eos(&tmp->og))
00192          tmp->eos = 1;
00193    }
00194 
00195    return 0;
00196 }
00197 
00198 /*!
00199  * \brief Write out any pending encoded data.
00200  * \param s An OGG/Vorbis filestream.
00201  * \param f The file to write to.
00202  */
00203 static void write_stream(struct ogg_vorbis_desc *s, FILE *f)
00204 {
00205    while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
00206       vorbis_analysis(&s->vb, NULL);
00207       vorbis_bitrate_addblock(&s->vb);
00208 
00209       while (vorbis_bitrate_flushpacket(&s->vd, &s->op)) {
00210          ogg_stream_packetin(&s->os, &s->op);
00211          while (!s->eos) {
00212             if (ogg_stream_pageout(&s->os, &s->og) == 0) {
00213                break;
00214             }
00215             if (!fwrite(s->og.header, 1, s->og.header_len, f)) {
00216             ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00217             }
00218             if (!fwrite(s->og.body, 1, s->og.body_len, f)) {
00219                ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00220             }
00221             if (ogg_page_eos(&s->og)) {
00222                s->eos = 1;
00223             }
00224          }
00225       }
00226    }
00227 }
00228 
00229 /*!
00230  * \brief Write audio data from a frame to an OGG/Vorbis filestream.
00231  * \param fs An OGG/Vorbis filestream.
00232  * \param f A frame containing audio to be written to the filestream.
00233  * \return -1 if there was an error, 0 on success.
00234  */
00235 static int ogg_vorbis_write(struct ast_filestream *fs, struct ast_frame *f)
00236 {
00237    int i;
00238    float **buffer;
00239    short *data;
00240    struct ogg_vorbis_desc *s = (struct ogg_vorbis_desc *) fs->_private;
00241 
00242    if (!s->writing) {
00243       ast_log(LOG_ERROR, "This stream is not set up for writing!\n");
00244       return -1;
00245    }
00246    if (!f->datalen)
00247       return -1;
00248 
00249    data = (short *) f->data.ptr;
00250 
00251    buffer = vorbis_analysis_buffer(&s->vd, f->samples);
00252 
00253    for (i = 0; i < f->samples; i++)
00254       buffer[0][i] = (double)data[i] / 32768.0;
00255 
00256    vorbis_analysis_wrote(&s->vd, f->samples);
00257 
00258    write_stream(s, fs->f);
00259 
00260    s->writing_pcm_pos +=  f->samples;
00261 
00262    return 0;
00263 }
00264 
00265 /*!
00266  * \brief Close a OGG/Vorbis filestream.
00267  * \param fs A OGG/Vorbis filestream.
00268  */
00269 static void ogg_vorbis_close(struct ast_filestream *fs)
00270 {
00271    struct ogg_vorbis_desc *s = (struct ogg_vorbis_desc *) fs->_private;
00272 
00273    if (s->writing) {
00274       /* Tell the Vorbis encoder that the stream is finished
00275        * and write out the rest of the data */
00276       vorbis_analysis_wrote(&s->vd, 0);
00277       write_stream(s, fs->f);
00278    } else {
00279       /* clear OggVorbis_File handle */
00280       ov_clear(&s->ov_f);
00281    }
00282 }
00283 
00284 /*!
00285  * \brief Read a frame full of audio data from the filestream.
00286  * \param fs The filestream.
00287  * \param whennext Number of sample times to schedule the next call.
00288  * \return A pointer to a frame containing audio data or NULL ifthere is no more audio data.
00289  */
00290 static struct ast_frame *ogg_vorbis_read(struct ast_filestream *fs,
00291                 int *whennext)
00292 {
00293    struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) fs->_private;
00294    int current_bitstream = -10;
00295    char *out_buf;
00296    long bytes_read;
00297 
00298    if (desc->writing) {
00299       ast_log(LOG_WARNING, "Reading is not supported on OGG/Vorbis on write files.\n");
00300       return NULL;
00301    }
00302 
00303    /* initialize frame */
00304    AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
00305    out_buf = (char *) (fs->fr.data.ptr);  /* SLIN data buffer */
00306 
00307    /* read samples from OV interface */
00308    bytes_read = ov_read(
00309       &desc->ov_f,
00310       out_buf,                /* Buffer to write data */
00311       BUF_SIZE,                  /* Size of buffer */
00312       (__BYTE_ORDER == __BIG_ENDIAN),  /* Endianes (0 for little) */
00313       2,                      /* 1 = 8bit, 2 = 16bit */
00314       1,                      /* 0 = unsigned, 1 = signed */
00315       &current_bitstream            /* Returns the current bitstream section */
00316    );
00317 
00318    /* check returned data */
00319    if (bytes_read <= 0) {
00320       /* End of stream */
00321       return NULL;
00322    }
00323 
00324    /* Return decoded bytes */
00325    fs->fr.datalen = bytes_read;
00326    fs->fr.samples = bytes_read / 2;
00327    *whennext = fs->fr.samples;
00328    return &fs->fr;
00329 }
00330 
00331 /*!
00332  * \brief Trucate an OGG/Vorbis filestream.
00333  * \param fs The filestream to truncate.
00334  * \return 0 on success, -1 on failure.
00335  */
00336 
00337 static int ogg_vorbis_trunc(struct ast_filestream *fs)
00338 {
00339    ast_log(LOG_WARNING, "Truncation is not supported on OGG/Vorbis streams!\n");
00340    return -1;
00341 }
00342 
00343 /*!
00344  * \brief Tell the current position in OGG/Vorbis filestream measured in pcms.
00345  * \param fs The filestream to take action on.
00346  * \return 0 or greater with the position measured in samples, or -1 for false.
00347  */
00348 static off_t ogg_vorbis_tell(struct ast_filestream *fs)
00349 {
00350    off_t pos;
00351    struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) fs->_private;
00352 
00353    if (desc->writing) {
00354       return desc->writing_pcm_pos;
00355    }
00356 
00357    if ((pos = ov_pcm_tell(&desc->ov_f)) < 0) {
00358       return -1;
00359    }
00360    return pos;
00361 }
00362 
00363 /*!
00364  * \brief Seek to a specific position in an OGG/Vorbis filestream.
00365  * \param fs The filestream to take action on.
00366  * \param sample_offset New position for the filestream, measured in 8KHz samples.
00367  * \param whence Location to measure 
00368  * \return 0 on success, -1 on failure.
00369  */
00370 static int ogg_vorbis_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
00371 {
00372    int seek_result = -1;
00373    off_t relative_pcm_pos;
00374    struct ogg_vorbis_desc *desc = (struct ogg_vorbis_desc *) fs->_private;
00375 
00376    if (desc->writing) {
00377       ast_log(LOG_WARNING, "Seeking is not supported on OGG/Vorbis streams in writing mode!\n");
00378       return -1;
00379    }
00380 
00381    /* ov_pcm_seek support seeking only from begining (SEEK_SET), the rest must be emulated */
00382    switch (whence) {
00383    case SEEK_SET:
00384       seek_result = ov_pcm_seek(&desc->ov_f, sample_offset);
00385       break;
00386    case SEEK_CUR:
00387       if ((relative_pcm_pos = ogg_vorbis_tell(fs)) < 0) {
00388          seek_result = -1;
00389          break;
00390       }
00391       seek_result = ov_pcm_seek(&desc->ov_f, relative_pcm_pos + sample_offset);
00392       break;
00393    case SEEK_END:
00394       if ((relative_pcm_pos = ov_pcm_total(&desc->ov_f, -1)) < 0) {
00395          seek_result = -1;
00396          break;
00397       }
00398       seek_result = ov_pcm_seek(&desc->ov_f, relative_pcm_pos - sample_offset);
00399       break;
00400    default:
00401       ast_log(LOG_WARNING, "Unknown *whence* to seek on OGG/Vorbis streams!\n");
00402       break;
00403    }
00404 
00405    /* normalize error value to -1,0 */
00406    return (seek_result == 0) ? 0 : -1;
00407 }
00408 
00409 static struct ast_format_def vorbis_f = {
00410    .name = "ogg_vorbis",
00411    .exts = "ogg",
00412    .open = ogg_vorbis_open,
00413    .rewrite = ogg_vorbis_rewrite,
00414    .write = ogg_vorbis_write,
00415    .seek =  ogg_vorbis_seek,
00416    .trunc = ogg_vorbis_trunc,
00417    .tell = ogg_vorbis_tell,
00418    .read = ogg_vorbis_read,
00419    .close = ogg_vorbis_close,
00420    .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
00421    .desc_size = sizeof(struct ogg_vorbis_desc),
00422 };
00423 
00424 static int load_module(void)
00425 {
00426    vorbis_f.format = ast_format_slin;
00427    if (ast_format_def_register(&vorbis_f))
00428       return AST_MODULE_LOAD_FAILURE;
00429    return AST_MODULE_LOAD_SUCCESS;
00430 }
00431 
00432 static int unload_module(void)
00433 {
00434    return ast_format_def_unregister(vorbis_f.name);
00435 }
00436 
00437 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Vorbis audio",
00438    .support_level = AST_MODULE_SUPPORT_CORE,
00439    .load = load_module,
00440    .unload = unload_module,
00441    .load_pri = AST_MODPRI_APP_DEPEND
00442 );

Generated on Thu Apr 16 06:27:34 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6