diff --git a/libav/ogg.c b/libav/ogg.c index bd2547f4f9..7c7c96c61f 100644 --- a/libav/ogg.c +++ b/libav/ogg.c @@ -15,12 +15,18 @@ #include "avformat.h" #include "oggvorbis.h" +#define DECODER_BUFFER_SIZE 4096 + typedef struct OggContext { + /* output */ ogg_stream_state os ; - int header_written ; + int header_handled ; ogg_int64_t base_packet_no ; ogg_int64_t base_granule_pos ; + + /* input */ + ogg_sync_state oy ; } OggContext ; @@ -34,8 +40,6 @@ static int ogg_write_header(AVFormatContext *avfcontext) { ogg_packet header, header_comm, header_code ; int n ; - fprintf(stderr, "ogg_write_header\n") ; - if(!(context = malloc(sizeof(OggContext)))) return -1 ; avfcontext->priv_data = context ; @@ -75,7 +79,7 @@ static int ogg_write_header(AVFormatContext *avfcontext) { /* end of vorbis specific code */ - context->header_written = 0 ; + context->header_handled = 0 ; context->base_packet_no = 0 ; } @@ -94,13 +98,13 @@ static int ogg_write_packet(AVFormatContext *avfcontext, /* flush header packets so audio starts on a new page */ - if(!context->header_written) { + if(!context->header_handled) { while(ogg_stream_flush(&context->os, &og)) { put_buffer(&avfcontext->pb, og.header, og.header_len) ; put_buffer(&avfcontext->pb, og.body, og.body_len) ; put_flush_packet(&avfcontext->pb); } - context->header_written = 1 ; + context->header_handled = 1 ; } while(l < size) { @@ -135,8 +139,6 @@ static int ogg_write_trailer(AVFormatContext *avfcontext) { OggContext *context = avfcontext->priv_data ; ogg_page og ; - fprintf(stderr, "ogg_write_trailer\n") ; - while(ogg_stream_flush(&context->os, &og)) { put_buffer(&avfcontext->pb, og.header, og.header_len) ; put_buffer(&avfcontext->pb, og.body, og.body_len) ; @@ -159,10 +161,109 @@ static AVOutputFormat ogg_oformat = { ogg_write_header, ogg_write_packet, ogg_write_trailer, -}; +} ; + + +static int next_packet(AVFormatContext *avfcontext, ogg_packet *op) { + OggContext *context = avfcontext->priv_data ; + ogg_page og ; + char *buf ; + + while(ogg_stream_packetout(&context->os, op) != 1) { + + /* while no pages are available, read in more data to the sync */ + while(ogg_sync_pageout(&context->oy, &og) != 1) { + buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ; + if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0) + return 1 ; + ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ; + } + + /* got a page. Feed it into the stream and get the packet */ + if(ogg_stream_pagein(&context->os, &og) != 0) + return 1 ; + } + + return 0 ; +} + + +static int ogg_read_header(AVFormatContext *avfcontext, AVFormatParameters *ap) +{ + OggContext *context ; + char *buf ; + ogg_page og ; + AVStream *ast ; + + if(!(context = malloc(sizeof(OggContext)))) { + perror("malloc") ; + return -1 ; + } + avfcontext->priv_data = context ; + + ogg_sync_init(&context->oy) ; + buf = ogg_sync_buffer(&context->oy, DECODER_BUFFER_SIZE) ; + + if(get_buffer(&avfcontext->pb, buf, DECODER_BUFFER_SIZE) <= 0) + return -EIO ; + + ogg_sync_wrote(&context->oy, DECODER_BUFFER_SIZE) ; + ogg_sync_pageout(&context->oy, &og) ; + ogg_stream_init(&context->os, ogg_page_serialno(&og)) ; + ogg_stream_pagein(&context->os, &og) ; + + /* currently only one vorbis stream supported */ + + ast = av_new_stream(avfcontext, 0) ; + if(!ast) + return AVERROR_NOMEM ; + + ast->codec.codec_type = CODEC_TYPE_AUDIO ; + ast->codec.codec_id = CODEC_ID_VORBIS ; + + return 0 ; +} + + +static int ogg_read_packet(AVFormatContext *avfcontext, AVPacket *pkt) { + ogg_packet op ; + + if(next_packet(avfcontext, &op)) + return -EIO ; + if(av_new_packet(pkt, sizeof(ogg_packet) + op.bytes) < 0) + return -EIO ; + pkt->stream_index = 0 ; + memcpy(pkt->data, &op, sizeof(ogg_packet)) ; + memcpy(pkt->data + sizeof(ogg_packet), op.packet, op.bytes) ; + + return sizeof(ogg_packet) + op.bytes ; +} + + +static int ogg_read_close(AVFormatContext *avfcontext) { + OggContext *context = avfcontext->priv_data ; + + ogg_stream_clear(&context->os) ; + ogg_sync_clear(&context->oy) ; + + return 0 ; +} + + +static AVInputFormat ogg_iformat = { + "ogg", + "Ogg Vorbis", + sizeof(OggContext), + NULL, + ogg_read_header, + ogg_read_packet, + ogg_read_close, + .extensions = "ogg", +} ; int ogg_init(void) { av_register_output_format(&ogg_oformat) ; + av_register_input_format(&ogg_iformat); return 0 ; } diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ef3432c959..a5d2e41cfb 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -41,6 +41,7 @@ void avcodec_register_all(void) #endif #ifdef CONFIG_VORBIS register_avcodec(&oggvorbis_encoder); + register_avcodec(&oggvorbis_decoder); #endif register_avcodec(&mpeg1video_encoder); register_avcodec(&h263_encoder); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index b8a48870aa..963e5f100a 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -865,6 +865,7 @@ extern AVCodec mp3_decoder; extern AVCodec mace3_decoder; extern AVCodec mace6_decoder; extern AVCodec huffyuv_decoder; +extern AVCodec oggvorbis_decoder; /* pcm codecs */ #define PCM_CODEC(id, name) \ diff --git a/libavcodec/oggvorbis.c b/libavcodec/oggvorbis.c index 5b241ed4f1..1d046a3bb2 100644 --- a/libavcodec/oggvorbis.c +++ b/libavcodec/oggvorbis.c @@ -17,22 +17,16 @@ typedef struct OggVorbisContext { vorbis_info vi ; vorbis_dsp_state vd ; vorbis_block vb ; + + /* decoder */ + vorbis_comment vc ; } OggVorbisContext ; int oggvorbis_init_encoder(vorbis_info *vi, AVCodecContext *avccontext) { - if(avccontext->quality) { /* VBR requested */ - - fprintf(stderr, "init_encode: channels=%d quality=%d\n", - avccontext->channels, avccontext->quality) ; - + if(avccontext->quality) /* VBR requested */ return vorbis_encode_init_vbr(vi, avccontext->channels, avccontext->sample_rate, (float)avccontext->quality / 1000) ; - } - - fprintf(stderr, "init_encoder: channels=%d bitrate=%d tolerance=%d\n", - avccontext->channels, avccontext->bit_rate, - avccontext->bit_rate_tolerance) ; return vorbis_encode_init(vi, avccontext->channels, avccontext->sample_rate, -1, avccontext->bit_rate, -1) ; @@ -42,15 +36,11 @@ int oggvorbis_init_encoder(vorbis_info *vi, AVCodecContext *avccontext) { static int oggvorbis_encode_init(AVCodecContext *avccontext) { OggVorbisContext *context = avccontext->priv_data ; - fprintf(stderr, "oggvorbis_encode_init\n") ; - vorbis_info_init(&context->vi) ; - if(oggvorbis_init_encoder(&context->vi, avccontext) < 0) { fprintf(stderr, "oggvorbis_encode_init: init_encoder failed") ; return -1 ; } - vorbis_analysis_init(&context->vd, &context->vi) ; vorbis_block_init(&context->vd, &context->vb) ; @@ -60,14 +50,15 @@ static int oggvorbis_encode_init(AVCodecContext *avccontext) { } -int oggvorbis_encode_frame(AVCodecContext *avccontext, unsigned char *packets, +static int oggvorbis_encode_frame(AVCodecContext *avccontext, + unsigned char *packets, int buf_size, void *data) { OggVorbisContext *context = avccontext->priv_data ; float **buffer ; ogg_packet op ; signed char *audio = data ; - int l, samples = buf_size / 16 ; /* samples = OGGVORBIS_FRAME_SIZE */ ; + int l, samples = OGGVORBIS_FRAME_SIZE ; buffer = vorbis_analysis_buffer(&context->vd, samples) ; @@ -100,17 +91,17 @@ int oggvorbis_encode_frame(AVCodecContext *avccontext, unsigned char *packets, } -int oggvorbis_encode_close(AVCodecContext *avccontext) { +static int oggvorbis_encode_close(AVCodecContext *avccontext) { OggVorbisContext *context = avccontext->priv_data ; /* ogg_packet op ; */ - fprintf(stderr, "oggvorbis_encode_close\n") ; - vorbis_analysis_wrote(&context->vd, 0) ; /* notify vorbisenc this is EOF */ /* We need to write all the remaining packets into the stream * on closing */ + fprintf(stderr, "fixme: not all packets written on oggvorbis_encode_close()\n") ; + /* while(vorbis_bitrate_flushpacket(&context->vd, &op)) { memcpy(packets + l, &op, sizeof(ogg_packet)) ; @@ -135,6 +126,106 @@ AVCodec oggvorbis_encoder = { oggvorbis_encode_init, oggvorbis_encode_frame, oggvorbis_encode_close -}; +} ; +static int oggvorbis_decode_init(AVCodecContext *avccontext) { + OggVorbisContext *context = avccontext->priv_data ; + + vorbis_info_init(&context->vi) ; + vorbis_comment_init(&context->vc) ; + + return 0 ; +} + + +static inline int conv(int samples, float **pcm, char *buf, int channels) { + int i, j, val ; + 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++) { + + val = mono[j] * 32767.f; + + if(val > 32767) val = 32767 ; + if(val < -32768) val = -32768 ; + + *ptr = val ; + ptr += channels; + } + } + + return 0 ; +} + + +static int oggvorbis_decode_frame(AVCodecContext *avccontext, + void *data, int *data_size, + UINT8 *buf, int buf_size) +{ + OggVorbisContext *context = avccontext->priv_data ; + ogg_packet *op = (ogg_packet*)buf ; + float **pcm ; + int samples, total_samples, total_bytes ; + + op->packet = (char*)op + sizeof(ogg_packet) ; /* correct data pointer */ + + if(op->packetno < 3) { + vorbis_synthesis_headerin(&context->vi, &context->vc, op) ; + return buf_size ; + } + + if(op->packetno == 3) { + fprintf(stderr, "vorbis_decode: %d channel, %ldHz, encoder `%s'\n", + context->vi.channels, context->vi.rate, context->vc.vendor); + + avccontext->channels = context->vi.channels ; + avccontext->sample_rate = context->vi.rate ; + + vorbis_synthesis_init(&context->vd, &context->vi) ; + vorbis_block_init(&context->vd, &context->vb); + } + + 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*)data + total_bytes, context->vi.channels) ; + total_bytes += samples * 2 * context->vi.channels ; + total_samples += samples ; + vorbis_synthesis_read(&context->vd, samples) ; + } + + *data_size = total_bytes ; + return buf_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 oggvorbis_decoder = { + "vorbis", + CODEC_TYPE_AUDIO, + CODEC_ID_VORBIS, + sizeof(OggVorbisContext), + oggvorbis_decode_init, + NULL, + oggvorbis_decode_close, + oggvorbis_decode_frame, +} ; diff --git a/tests/regression.sh b/tests/regression.sh index b93f0631ff..6f29894728 100755 --- a/tests/regression.sh +++ b/tests/regression.sh @@ -36,7 +36,7 @@ fi # various files -ffmpeg="../ffmpeg" +ffmpeg="../ffmpeg_g" outfile="$datadir/a-" reffile="$2" benchfile="$datadir/ffmpeg.bench"