diff --git a/configure b/configure index d534ffd47f..4313bdd8db 100755 --- a/configure +++ b/configure @@ -198,7 +198,7 @@ External library support: --enable-libv4l2 enable libv4l2/v4l-utils [no] --enable-libvo-aacenc enable AAC encoding via libvo-aacenc [no] --enable-libvo-amrwbenc enable AMR-WB encoding via libvo-amrwbenc [no] - --enable-libvorbis enable Vorbis encoding via libvorbis, + --enable-libvorbis enable Vorbis en/decoding via libvorbis, native implementation exists [no] --enable-libvpx enable VP8 support via libvpx [no] --enable-libx264 enable H.264 encoding via x264 [no] @@ -1578,6 +1578,7 @@ libstagefright_h264_decoder_deps="libstagefright_h264" libtheora_encoder_deps="libtheora" libvo_aacenc_encoder_deps="libvo_aacenc" libvo_amrwbenc_encoder_deps="libvo_amrwbenc" +libvorbis_decoder_deps="libvorbis" libvorbis_encoder_deps="libvorbis" libvpx_decoder_deps="libvpx" libvpx_encoder_deps="libvpx" diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index b2d8ea5599..631802b221 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -420,7 +420,7 @@ void avcodec_register_all(void) REGISTER_ENCDEC (LIBUTVIDEO, libutvideo); REGISTER_ENCODER (LIBVO_AACENC, libvo_aacenc); REGISTER_ENCODER (LIBVO_AMRWBENC, libvo_amrwbenc); - REGISTER_ENCODER (LIBVORBIS, libvorbis); + REGISTER_ENCDEC (LIBVORBIS, libvorbis); REGISTER_ENCDEC (LIBVPX, libvpx); REGISTER_ENCODER (LIBX264, libx264); REGISTER_ENCODER (LIBX264RGB, libx264rgb); diff --git a/libavcodec/libvorbis.c b/libavcodec/libvorbis.c index db65d160a3..54e9f134ea 100644 --- a/libavcodec/libvorbis.c +++ b/libavcodec/libvorbis.c @@ -49,6 +49,7 @@ typedef struct OggVorbisContext { AVClass *av_class; /**< class for AVOptions */ + AVFrame frame; 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 */ @@ -390,3 +391,170 @@ AVCodec ff_libvorbis_encoder = { .priv_class = &class, .defaults = defaults, }; + +static int oggvorbis_decode_init(AVCodecContext *avccontext) { + OggVorbisContext *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++){ + hsizes[i] = bytestream_get_be16(&p); + 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; + 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) +{ + OggVorbisContext *context = avccontext->priv_data ; + 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; + } + + context->frame.nb_samples = 8192*4; + if ((ret = avccontext->get_buffer(avccontext, &context->frame)) < 0) { + av_log(avccontext, AV_LOG_ERROR, "get_buffer() failed\n"); + return ret; + } + output = (int16_t *)context->frame.data[0]; + + + 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; ibytes; 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) ; + } + + context->frame.nb_samples = total_samples; + *got_frame_ptr = 1; + *(AVFrame *)data = context->frame; + return avpkt->size; +} + + +static int oggvorbis_decode_close(AVCodecContext *avccontext) { + OggVorbisContext *context = avccontext->priv_data ; + + vorbis_info_clear(&context->vi) ; + vorbis_comment_clear(&context->vc) ; + + return 0 ; +} + + +AVCodec ff_libvorbis_decoder = { + .name = "libvorbis", + .type = AVMEDIA_TYPE_AUDIO, + .id = CODEC_ID_VORBIS, + .priv_data_size = sizeof(OggVorbisContext), + .init = oggvorbis_decode_init, + .decode = oggvorbis_decode_frame, + .close = oggvorbis_decode_close, + .capabilities = CODEC_CAP_DELAY, +} ; +