From 4f4e19480a1b254d1d049cffffb11f13ae54b53a Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Wed, 26 Oct 2011 11:02:12 -0400 Subject: binkaudio: only decode one block at a time. This prevents truncating output due to an output buffer that is too small for all blocks. There is no limit on the number of blocks in a packet. --- libavcodec/binkaudio.c | 55 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 17 deletions(-) (limited to 'libavcodec/binkaudio.c') diff --git a/libavcodec/binkaudio.c b/libavcodec/binkaudio.c index 815fbb7f77..742682f1c5 100644 --- a/libavcodec/binkaudio.c +++ b/libavcodec/binkaudio.c @@ -62,6 +62,7 @@ typedef struct { DECLARE_ALIGNED(16, int16_t, current)[BINK_BLOCK_MAX_SIZE / 16]; float *coeffs_ptr[MAX_CHANNELS]; ///< pointers to the coeffs arrays for float_to_int16_interleave float *prev_ptr[MAX_CHANNELS]; ///< pointers to the overlap points in the coeffs array + uint8_t *packet_buffer; union { RDFTContext rdft; DCTContext dct; @@ -287,6 +288,7 @@ static av_cold int decode_end(AVCodecContext *avctx) { BinkAudioContext * s = avctx->priv_data; av_freep(&s->bands); + av_freep(&s->packet_buffer); if (CONFIG_BINKAUDIO_RDFT_DECODER && avctx->codec->id == CODEC_ID_BINKAUDIO_RDFT) ff_rdft_end(&s->trans.rdft); else if (CONFIG_BINKAUDIO_DCT_DECODER) @@ -305,30 +307,47 @@ static int decode_frame(AVCodecContext *avctx, AVPacket *avpkt) { BinkAudioContext *s = avctx->priv_data; - const uint8_t *buf = avpkt->data; - int buf_size = avpkt->size; short *samples = data; - short *samples_end = (short*)((uint8_t*)data + *data_size); - int reported_size; GetBitContext *gb = &s->gb; - - if (buf_size < 4) { - av_log(avctx, AV_LOG_ERROR, "Packet is too small\n"); - return AVERROR_INVALIDDATA; + int out_size, consumed = 0; + + if (!get_bits_left(gb)) { + uint8_t *buf; + /* handle end-of-stream */ + if (!avpkt->size) { + *data_size = 0; + return 0; + } + if (avpkt->size < 4) { + av_log(avctx, AV_LOG_ERROR, "Packet is too small\n"); + return AVERROR_INVALIDDATA; + } + buf = av_realloc(s->packet_buffer, avpkt->size + FF_INPUT_BUFFER_PADDING_SIZE); + if (!buf) + return AVERROR(ENOMEM); + s->packet_buffer = buf; + memcpy(s->packet_buffer, avpkt->data, avpkt->size); + init_get_bits(gb, s->packet_buffer, avpkt->size * 8); + consumed = avpkt->size; + + /* skip reported size */ + skip_bits_long(gb, 32); } - init_get_bits(gb, buf, buf_size * 8); + out_size = s->block_size * av_get_bytes_per_sample(avctx->sample_fmt); + if (*data_size < out_size) { + av_log(avctx, AV_LOG_ERROR, "Output buffer is too small\n"); + return AVERROR(EINVAL); + } - reported_size = get_bits_long(gb, 32); - while (samples + s->block_size <= samples_end) { - if (decode_block(s, samples, avctx->codec->id == CODEC_ID_BINKAUDIO_DCT)) - break; - samples += s->block_size; - get_bits_align32(gb); + if (decode_block(s, samples, avctx->codec->id == CODEC_ID_BINKAUDIO_DCT)) { + av_log(avctx, AV_LOG_ERROR, "Incomplete packet\n"); + return AVERROR_INVALIDDATA; } + get_bits_align32(gb); - *data_size = FFMIN(reported_size, (uint8_t*)samples - (uint8_t*)data); - return buf_size; + *data_size = out_size; + return consumed; } AVCodec ff_binkaudio_rdft_decoder = { @@ -339,6 +358,7 @@ AVCodec ff_binkaudio_rdft_decoder = { .init = decode_init, .close = decode_end, .decode = decode_frame, + .capabilities = CODEC_CAP_DELAY, .long_name = NULL_IF_CONFIG_SMALL("Bink Audio (RDFT)") }; @@ -350,5 +370,6 @@ AVCodec ff_binkaudio_dct_decoder = { .init = decode_init, .close = decode_end, .decode = decode_frame, + .capabilities = CODEC_CAP_DELAY, .long_name = NULL_IF_CONFIG_SMALL("Bink Audio (DCT)") }; -- cgit v1.2.3