libavcodec/libvorbisdec.c
c1d62489
 /*
  * Copyright (c) 2002 Mark Hills <mark@pogo.org.uk>
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #include <vorbis/vorbisenc.h>
 
 #include "avcodec.h"
 #include "bytestream.h"
874c5b02
 #include "internal.h"
c1d62489
 
 typedef struct OggVorbisDecContext {
     vorbis_info vi;                     /**< vorbis_info used during init   */
     vorbis_dsp_state vd;                /**< DSP state used for analysis    */
     vorbis_block vb;                    /**< vorbis_block used for analysis */
     vorbis_comment vc;                  /**< VorbisComment info             */
     ogg_packet op;                      /**< ogg packet                     */
 } OggVorbisDecContext;
 
 static int oggvorbis_decode_init(AVCodecContext *avccontext) {
     OggVorbisDecContext *context = avccontext->priv_data ;
     uint8_t *p= avccontext->extradata;
     int i, hsizes[3];
     unsigned char *headers[3], *extradata = avccontext->extradata;
 
     vorbis_info_init(&context->vi) ;
     vorbis_comment_init(&context->vc) ;
 
     if(! avccontext->extradata_size || ! p) {
         av_log(avccontext, AV_LOG_ERROR, "vorbis extradata absent\n");
         return -1;
     }
 
     if(p[0] == 0 && p[1] == 30) {
         for(i = 0; i < 3; i++){
6fc892d2
             hsizes[i] = bytestream_get_be16((const uint8_t **)&p);
c1d62489
             headers[i] = p;
             p += hsizes[i];
         }
     } else if(*p == 2) {
         unsigned int offset = 1;
         p++;
         for(i=0; i<2; i++) {
             hsizes[i] = 0;
             while((*p == 0xFF) && (offset < avccontext->extradata_size)) {
                 hsizes[i] += 0xFF;
                 offset++;
                 p++;
             }
             if(offset >= avccontext->extradata_size - 1) {
                 av_log(avccontext, AV_LOG_ERROR,
                        "vorbis header sizes damaged\n");
                 return -1;
             }
             hsizes[i] += *p;
             offset++;
             p++;
         }
         hsizes[2] = avccontext->extradata_size - hsizes[0]-hsizes[1]-offset;
 #if 0
         av_log(avccontext, AV_LOG_DEBUG,
                "vorbis header sizes: %d, %d, %d, / extradata_len is %d \n",
                hsizes[0], hsizes[1], hsizes[2], avccontext->extradata_size);
 #endif
         headers[0] = extradata + offset;
         headers[1] = extradata + offset + hsizes[0];
         headers[2] = extradata + offset + hsizes[0] + hsizes[1];
     } else {
         av_log(avccontext, AV_LOG_ERROR,
                "vorbis initial header len is wrong: %d\n", *p);
         return -1;
     }
 
     for(i=0; i<3; i++){
         context->op.b_o_s= i==0;
         context->op.bytes = hsizes[i];
         context->op.packet = headers[i];
         if(vorbis_synthesis_headerin(&context->vi, &context->vc, &context->op)<0){
             av_log(avccontext, AV_LOG_ERROR, "%d. vorbis header damaged\n", i+1);
             return -1;
         }
     }
 
     avccontext->channels = context->vi.channels;
     avccontext->sample_rate = context->vi.rate;
fe845772
     avccontext->sample_fmt = AV_SAMPLE_FMT_S16;
c1d62489
     avccontext->time_base= (AVRational){1, avccontext->sample_rate};
 
     vorbis_synthesis_init(&context->vd, &context->vi);
     vorbis_block_init(&context->vd, &context->vb);
 
     return 0 ;
 }
 
 
 static inline int conv(int samples, float **pcm, char *buf, int channels) {
     int i, j;
     ogg_int16_t *ptr, *data = (ogg_int16_t*)buf ;
     float *mono ;
 
     for(i = 0 ; i < channels ; i++){
         ptr = &data[i];
         mono = pcm[i] ;
 
         for(j = 0 ; j < samples ; j++) {
             *ptr = av_clip_int16(mono[j] * 32767.f);
             ptr += channels;
         }
     }
 
     return 0 ;
 }
 
 static int oggvorbis_decode_frame(AVCodecContext *avccontext, void *data,
                         int *got_frame_ptr, AVPacket *avpkt)
 {
     OggVorbisDecContext *context = avccontext->priv_data ;
324d9664
     AVFrame *frame = data;
c1d62489
     float **pcm ;
     ogg_packet *op= &context->op;
     int samples, total_samples, total_bytes;
     int ret;
     int16_t *output;
 
     if(!avpkt->size){
     //FIXME flush
         return 0;
     }
 
324d9664
     frame->nb_samples = 8192*4;
1ec94b0f
     if ((ret = ff_get_buffer(avccontext, frame, 0)) < 0)
c1d62489
         return ret;
324d9664
     output = (int16_t *)frame->data[0];
c1d62489
 
 
     op->packet = avpkt->data;
     op->bytes  = avpkt->size;
 
 //    av_log(avccontext, AV_LOG_DEBUG, "%d %d %d %"PRId64" %"PRId64" %d %d\n", op->bytes, op->b_o_s, op->e_o_s, op->granulepos, op->packetno, buf_size, context->vi.rate);
 
 /*    for(i=0; i<op->bytes; i++)
       av_log(avccontext, AV_LOG_DEBUG, "%02X ", op->packet[i]);
     av_log(avccontext, AV_LOG_DEBUG, "\n");*/
 
     if(vorbis_synthesis(&context->vb, op) == 0)
         vorbis_synthesis_blockin(&context->vd, &context->vb) ;
 
     total_samples = 0 ;
     total_bytes = 0 ;
 
     while((samples = vorbis_synthesis_pcmout(&context->vd, &pcm)) > 0) {
         conv(samples, pcm, (char*)output + total_bytes, context->vi.channels) ;
         total_bytes += samples * 2 * context->vi.channels ;
         total_samples += samples ;
         vorbis_synthesis_read(&context->vd, samples) ;
     }
 
324d9664
     frame->nb_samples = total_samples;
d7ccfe58
     *got_frame_ptr   = total_samples > 0;
c1d62489
     return avpkt->size;
 }
 
 
 static int oggvorbis_decode_close(AVCodecContext *avccontext) {
     OggVorbisDecContext *context = avccontext->priv_data ;
 
     vorbis_info_clear(&context->vi) ;
     vorbis_comment_clear(&context->vc) ;
 
     return 0 ;
 }
 
 
 AVCodec ff_libvorbis_decoder = {
     .name           = "libvorbis",
b46f1910
     .long_name      = NULL_IF_CONFIG_SMALL("libvorbis"),
c1d62489
     .type           = AVMEDIA_TYPE_AUDIO,
7a72695c
     .id             = AV_CODEC_ID_VORBIS,
c1d62489
     .priv_data_size = sizeof(OggVorbisDecContext),
     .init           = oggvorbis_decode_init,
     .decode         = oggvorbis_decode_frame,
     .close          = oggvorbis_decode_close,
     .capabilities   = CODEC_CAP_DELAY,
 };