summaryrefslogtreecommitdiff
path: root/libavcodec/binkaudio.c
diff options
context:
space:
mode:
authorJustin Ruggles <justin.ruggles@gmail.com>2011-10-26 11:02:12 -0400
committerJustin Ruggles <justin.ruggles@gmail.com>2011-10-29 15:16:54 -0400
commit4f4e19480a1b254d1d049cffffb11f13ae54b53a (patch)
tree93210f77c712220cf32ea7375229fc4ffef16de6 /libavcodec/binkaudio.c
parenteaddd29e00850110e64982451e606efff8422eb4 (diff)
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.
Diffstat (limited to 'libavcodec/binkaudio.c')
-rw-r--r--libavcodec/binkaudio.c55
1 files changed, 38 insertions, 17 deletions
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)")
};