From cf57c78b774a71e8379d73e892880a2df1bf93ad Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Wed, 22 Feb 2012 16:20:49 -0500 Subject: roqaudioenc: use AVCodecContext.frame_size correctly. It is not allowed to change mid-stream like it does currently. Instead we need to buffer the first 8 frames before returning them as a single packet, then only return single frame packets after that. --- libavcodec/roqaudioenc.c | 91 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 26 deletions(-) (limited to 'libavcodec/roqaudioenc.c') diff --git a/libavcodec/roqaudioenc.c b/libavcodec/roqaudioenc.c index 1562417a7e..9137b02035 100644 --- a/libavcodec/roqaudioenc.c +++ b/libavcodec/roqaudioenc.c @@ -25,9 +25,8 @@ #include "avcodec.h" #include "bytestream.h" -#define ROQ_FIRST_FRAME_SIZE (735*8) #define ROQ_FRAME_SIZE 735 - +#define ROQ_HEADER_SIZE 8 #define MAX_DPCM (127*127) @@ -35,11 +34,26 @@ typedef struct { short lastSample[2]; + int input_frames; + int buffered_samples; + int16_t *frame_buffer; } ROQDPCMContext; + +static av_cold int roq_dpcm_encode_close(AVCodecContext *avctx) +{ + ROQDPCMContext *context = avctx->priv_data; + + av_freep(&avctx->coded_frame); + av_freep(&context->frame_buffer); + + return 0; +} + static av_cold int roq_dpcm_encode_init(AVCodecContext *avctx) { ROQDPCMContext *context = avctx->priv_data; + int ret; if (avctx->channels > 2) { av_log(avctx, AV_LOG_ERROR, "Audio must be mono or stereo\n"); @@ -50,15 +64,27 @@ static av_cold int roq_dpcm_encode_init(AVCodecContext *avctx) return -1; } - avctx->frame_size = ROQ_FIRST_FRAME_SIZE; + avctx->frame_size = ROQ_FRAME_SIZE; + + context->frame_buffer = av_malloc(8 * ROQ_FRAME_SIZE * avctx->channels * + sizeof(*context->frame_buffer)); + if (!context->frame_buffer) { + ret = AVERROR(ENOMEM); + goto error; + } context->lastSample[0] = context->lastSample[1] = 0; avctx->coded_frame= avcodec_alloc_frame(); - if (!avctx->coded_frame) - return AVERROR(ENOMEM); + if (!avctx->coded_frame) { + ret = AVERROR(ENOMEM); + goto error; + } return 0; +error: + roq_dpcm_encode_close(avctx); + return ret; } static unsigned char dpcm_predict(short *previous, short current) @@ -104,25 +130,45 @@ static unsigned char dpcm_predict(short *previous, short current) static int roq_dpcm_encode_frame(AVCodecContext *avctx, unsigned char *frame, int buf_size, void *data) { - int i, samples, stereo, ch; - const short *in; - unsigned char *out; - + int i, stereo, data_size; + const int16_t *in = data; + uint8_t *out = frame; ROQDPCMContext *context = avctx->priv_data; stereo = (avctx->channels == 2); + if (!data && context->input_frames >= 8) + return 0; + + if (data && context->input_frames < 8) { + memcpy(&context->frame_buffer[context->buffered_samples * avctx->channels], + in, avctx->frame_size * avctx->channels * sizeof(*in)); + context->buffered_samples += avctx->frame_size; + if (context->input_frames < 7) { + context->input_frames++; + return 0; + } + in = context->frame_buffer; + } + if (stereo) { context->lastSample[0] &= 0xFF00; context->lastSample[1] &= 0xFF00; } - out = frame; - in = data; + if (context->input_frames == 7 || !data) + data_size = avctx->channels * context->buffered_samples; + else + data_size = avctx->channels * avctx->frame_size; + + if (buf_size < ROQ_HEADER_SIZE + data_size) { + av_log(avctx, AV_LOG_ERROR, "output buffer is too small\n"); + return AVERROR(EINVAL); + } bytestream_put_byte(&out, stereo ? 0x21 : 0x20); bytestream_put_byte(&out, 0x10); - bytestream_put_le32(&out, avctx->frame_size*avctx->channels); + bytestream_put_le32(&out, data_size); if (stereo) { bytestream_put_byte(&out, (context->lastSample[1])>>8); @@ -131,23 +177,15 @@ static int roq_dpcm_encode_frame(AVCodecContext *avctx, bytestream_put_le16(&out, context->lastSample[0]); /* Write the actual samples */ - samples = avctx->frame_size; - for (i=0; ichannels; ch++) - *out++ = dpcm_predict(&context->lastSample[ch], *in++); + for (i = 0; i < data_size; i++) + *out++ = dpcm_predict(&context->lastSample[i & 1], *in++); - /* Use smaller frames from now on */ - avctx->frame_size = ROQ_FRAME_SIZE; + context->input_frames++; + if (!data) + context->input_frames = FFMAX(context->input_frames, 8); /* Return the result size */ - return out - frame; -} - -static av_cold int roq_dpcm_encode_close(AVCodecContext *avctx) -{ - av_freep(&avctx->coded_frame); - - return 0; + return ROQ_HEADER_SIZE + data_size; } AVCodec ff_roq_dpcm_encoder = { @@ -158,6 +196,7 @@ AVCodec ff_roq_dpcm_encoder = { .init = roq_dpcm_encode_init, .encode = roq_dpcm_encode_frame, .close = roq_dpcm_encode_close, + .capabilities = CODEC_CAP_DELAY, .sample_fmts = (const enum AVSampleFormat[]){AV_SAMPLE_FMT_S16,AV_SAMPLE_FMT_NONE}, .long_name = NULL_IF_CONFIG_SMALL("id RoQ DPCM"), }; -- cgit v1.2.3