diff options
| author | Tim Redfern <tim@eclectronics.org> | 2013-12-29 12:19:38 +0000 |
|---|---|---|
| committer | Tim Redfern <tim@eclectronics.org> | 2013-12-29 12:19:38 +0000 |
| commit | f7813a5324be39d13ab536c245d15dfc602a7849 (patch) | |
| tree | fad99148b88823d34a5df2f0a25881a002eb291b /ffmpeg/libavcodec/vmdav.c | |
| parent | b7a5a477b8ff4d4e3028b9dfb9a9df0a41463f92 (diff) | |
basic type mechanism working
Diffstat (limited to 'ffmpeg/libavcodec/vmdav.c')
| -rw-r--r-- | ffmpeg/libavcodec/vmdav.c | 405 |
1 files changed, 214 insertions, 191 deletions
diff --git a/ffmpeg/libavcodec/vmdav.c b/ffmpeg/libavcodec/vmdav.c index 0e27ef6..c1fb80b 100644 --- a/ffmpeg/libavcodec/vmdav.c +++ b/ffmpeg/libavcodec/vmdav.c @@ -43,11 +43,13 @@ #include <stdlib.h> #include <string.h> +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/intreadwrite.h" #include "avcodec.h" #include "internal.h" +#include "bytestream.h" #define VMD_HEADER_SIZE 0x330 #define PALETTE_COUNT 256 @@ -59,7 +61,7 @@ typedef struct VmdVideoContext { AVCodecContext *avctx; - AVFrame prev_frame; + AVFrame *prev_frame; const unsigned char *buf; int size; @@ -74,11 +76,9 @@ typedef struct VmdVideoContext { #define QUEUE_SIZE 0x1000 #define QUEUE_MASK 0x0FFF -static void lz_unpack(const unsigned char *src, int src_len, +static int lz_unpack(const unsigned char *src, int src_len, unsigned char *dest, int dest_len) { - const unsigned char *s; - const unsigned char *s_end; unsigned char *d; unsigned char *d_end; unsigned char queue[QUEUE_SIZE]; @@ -89,19 +89,17 @@ static void lz_unpack(const unsigned char *src, int src_len, unsigned int speclen; unsigned char tag; unsigned int i, j; + GetByteContext gb; - s = src; - s_end = src + src_len; + bytestream2_init(&gb, src, src_len); d = dest; d_end = d + dest_len; - - if (s_end - s < 8) - return; - dataleft = AV_RL32(s); - s += 4; + dataleft = bytestream2_get_le32(&gb); memset(queue, 0x20, QUEUE_SIZE); - if (AV_RL32(s) == 0x56781234) { - s += 4; + if (bytestream2_get_bytes_left(&gb) < 4) + return AVERROR_INVALIDDATA; + if (bytestream2_peek_le32(&gb) == 0x56781234) { + bytestream2_skipu(&gb, 4); qpos = 0x111; speclen = 0xF + 3; } else { @@ -109,13 +107,13 @@ static void lz_unpack(const unsigned char *src, int src_len, speclen = 100; /* no speclen */ } - while (s_end - s > 0 && dataleft > 0) { - tag = *s++; + while (dataleft > 0 && bytestream2_get_bytes_left(&gb) > 0) { + tag = bytestream2_get_byteu(&gb); if ((tag == 0xFF) && (dataleft > 8)) { - if (d_end - d < 8 || s_end - s < 8) - return; + if (d_end - d < 8 || bytestream2_get_bytes_left(&gb) < 8) + return AVERROR_INVALIDDATA; for (i = 0; i < 8; i++) { - queue[qpos++] = *d++ = *s++; + queue[qpos++] = *d++ = bytestream2_get_byteu(&gb); qpos &= QUEUE_MASK; } dataleft -= 8; @@ -124,24 +122,20 @@ static void lz_unpack(const unsigned char *src, int src_len, if (dataleft == 0) break; if (tag & 0x01) { - if (d_end - d < 1 || s_end - s < 1) - return; - queue[qpos++] = *d++ = *s++; + if (d_end - d < 1 || bytestream2_get_bytes_left(&gb) < 1) + return AVERROR_INVALIDDATA; + queue[qpos++] = *d++ = bytestream2_get_byteu(&gb); qpos &= QUEUE_MASK; dataleft--; } else { - if (s_end - s < 2) - return; - chainofs = *s++; - chainofs |= ((*s & 0xF0) << 4); - chainlen = (*s++ & 0x0F) + 3; + chainofs = bytestream2_get_byte(&gb); + chainofs |= ((bytestream2_peek_byte(&gb) & 0xF0) << 4); + chainlen = (bytestream2_get_byte(&gb) & 0x0F) + 3; if (chainlen == speclen) { - if (s_end - s < 1) - return; - chainlen = *s++ + 0xF + 3; + chainlen = bytestream2_get_byte(&gb) + 0xF + 3; } if (d_end - d < chainlen) - return; + return AVERROR_INVALIDDATA; for (j = 0; j < chainlen; j++) { *d = queue[chainofs++ & QUEUE_MASK]; queue[qpos++] = *d++; @@ -153,66 +147,60 @@ static void lz_unpack(const unsigned char *src, int src_len, } } } + return d - dest; } - -static int rle_unpack(const unsigned char *src, int src_len, int src_count, - unsigned char *dest, int dest_len) +static int rle_unpack(const unsigned char *src, unsigned char *dest, + int src_count, int src_size, int dest_len) { - const unsigned char *ps; - const unsigned char *ps_end; unsigned char *pd; - int i, l; + int i, l, used = 0; unsigned char *dest_end = dest + dest_len; + GetByteContext gb; + uint16_t run_val; - ps = src; - ps_end = src + src_len; + bytestream2_init(&gb, src, src_size); pd = dest; if (src_count & 1) { - if (ps_end - ps < 1) + if (bytestream2_get_bytes_left(&gb) < 1) return 0; - *pd++ = *ps++; + *pd++ = bytestream2_get_byteu(&gb); + used++; } - src_count >>= 1; - i = 0; do { - if (ps_end - ps < 1) + if (bytestream2_get_bytes_left(&gb) < 1) break; - l = *ps++; + l = bytestream2_get_byteu(&gb); if (l & 0x80) { l = (l & 0x7F) * 2; - if (dest_end - pd < l || ps_end - ps < l) - return ps - src; - memcpy(pd, ps, l); - ps += l; + if (dest_end - pd < l || bytestream2_get_bytes_left(&gb) < l) + return bytestream2_tell(&gb); + bytestream2_get_bufferu(&gb, pd, l); pd += l; } else { - if (dest_end - pd < i || ps_end - ps < 2) - return ps - src; + if (dest_end - pd < 2*l || bytestream2_get_bytes_left(&gb) < 2) + return bytestream2_tell(&gb); + run_val = bytestream2_get_ne16(&gb); for (i = 0; i < l; i++) { - *pd++ = ps[0]; - *pd++ = ps[1]; + AV_WN16(pd, run_val); + pd += 2; } - ps += 2; + l *= 2; } - i += l; - } while (i < src_count); + used += l; + } while (used < src_count); - return ps - src; + return bytestream2_tell(&gb); } -static void vmd_decode(VmdVideoContext *s, AVFrame *frame) +static int vmd_decode(VmdVideoContext *s, AVFrame *frame) { int i; unsigned int *palette32; unsigned char r, g, b; - /* point to the start of the encoded data */ - const unsigned char *p = s->buf + 16; - const unsigned char *p_end = s->buf + s->size; + GetByteContext gb; - const unsigned char *pb; - const unsigned char *pb_end; unsigned char meth; unsigned char *dp; /* pointer to current frame */ unsigned char *pp; /* pointer to previous frame */ @@ -226,16 +214,6 @@ static void vmd_decode(VmdVideoContext *s, AVFrame *frame) frame_y = AV_RL16(&s->buf[8]); frame_width = AV_RL16(&s->buf[10]) - frame_x + 1; frame_height = AV_RL16(&s->buf[12]) - frame_y + 1; - if (frame_x < 0 || frame_width < 0 || - frame_x >= s->avctx->width || - frame_width > s->avctx->width || - frame_x + frame_width > s->avctx->width) - return; - if (frame_y < 0 || frame_height < 0 || - frame_y >= s->avctx->height || - frame_height > s->avctx->height || - frame_y + frame_height > s->avctx->height) - return; if ((frame_width == s->avctx->width && frame_height == s->avctx->height) && (frame_x || frame_y)) { @@ -246,126 +224,168 @@ static void vmd_decode(VmdVideoContext *s, AVFrame *frame) frame_x -= s->x_off; frame_y -= s->y_off; + if (frame_x < 0 || frame_width < 0 || + frame_x >= s->avctx->width || + frame_width > s->avctx->width || + frame_x + frame_width > s->avctx->width) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid horizontal range %d-%d\n", + frame_x, frame_width); + return AVERROR_INVALIDDATA; + } + if (frame_y < 0 || frame_height < 0 || + frame_y >= s->avctx->height || + frame_height > s->avctx->height || + frame_y + frame_height > s->avctx->height) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid vertical range %d-%d\n", + frame_x, frame_width); + return AVERROR_INVALIDDATA; + } + /* if only a certain region will be updated, copy the entire previous * frame before the decode */ - if (s->prev_frame.data[0] && + if (s->prev_frame->data[0] && (frame_x || frame_y || (frame_width != s->avctx->width) || (frame_height != s->avctx->height))) { - memcpy(frame->data[0], s->prev_frame.data[0], + memcpy(frame->data[0], s->prev_frame->data[0], s->avctx->height * frame->linesize[0]); } /* check if there is a new palette */ + bytestream2_init(&gb, s->buf + 16, s->size - 16); if (s->buf[15] & 0x02) { - if (p_end - p < 2 + 3 * PALETTE_COUNT) - return; - p += 2; + bytestream2_skip(&gb, 2); palette32 = (unsigned int *)s->palette; - for (i = 0; i < PALETTE_COUNT; i++) { - r = *p++ * 4; - g = *p++ * 4; - b = *p++ * 4; - palette32[i] = 0xFFU << 24 | r << 16 | g << 8 | b; - palette32[i] |= palette32[i] >> 6 & 0x30303; + if (bytestream2_get_bytes_left(&gb) >= PALETTE_COUNT * 3) { + for (i = 0; i < PALETTE_COUNT; i++) { + r = bytestream2_get_byteu(&gb) * 4; + g = bytestream2_get_byteu(&gb) * 4; + b = bytestream2_get_byteu(&gb) * 4; + palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b); + palette32[i] |= palette32[i] >> 6 & 0x30303; + } + } else { + av_log(s->avctx, AV_LOG_ERROR, "Incomplete palette\n"); + return AVERROR_INVALIDDATA; } } - if (p < p_end) { - /* originally UnpackFrame in VAG's code */ - pb = p; - pb_end = p_end; - meth = *pb++; - if (meth & 0x80) { - lz_unpack(pb, p_end - pb, s->unpack_buffer, s->unpack_buffer_size); - meth &= 0x7F; - pb = s->unpack_buffer; - pb_end = s->unpack_buffer + s->unpack_buffer_size; + + if (!s->size) + return 0; + + /* originally UnpackFrame in VAG's code */ + if (bytestream2_get_bytes_left(&gb) < 1) + return AVERROR_INVALIDDATA; + meth = bytestream2_get_byteu(&gb); + if (meth & 0x80) { + int size; + if (!s->unpack_buffer_size) { + av_log(s->avctx, AV_LOG_ERROR, + "Trying to unpack LZ-compressed frame with no LZ buffer\n"); + return AVERROR_INVALIDDATA; } + size = lz_unpack(gb.buffer, bytestream2_get_bytes_left(&gb), + s->unpack_buffer, s->unpack_buffer_size); + if (size < 0) + return size; + meth &= 0x7F; + bytestream2_init(&gb, s->unpack_buffer, size); + } - dp = &frame->data[0][frame_y * frame->linesize[0] + frame_x]; - pp = &s->prev_frame.data[0][frame_y * s->prev_frame.linesize[0] + frame_x]; - switch (meth) { - case 1: - for (i = 0; i < frame_height; i++) { - ofs = 0; - do { - if (pb_end - pb < 1) - return; - len = *pb++; - if (len & 0x80) { - len = (len & 0x7F) + 1; - if (ofs + len > frame_width || pb_end - pb < len) - return; - memcpy(&dp[ofs], pb, len); - pb += len; - ofs += len; - } else { - /* interframe pixel copy */ - if (ofs + len + 1 > frame_width || !s->prev_frame.data[0]) - return; - memcpy(&dp[ofs], &pp[ofs], len + 1); - ofs += len + 1; - } - } while (ofs < frame_width); - if (ofs > frame_width) { - av_log(s->avctx, AV_LOG_ERROR, "offset > width (%d > %d)\n", - ofs, frame_width); - break; + dp = &frame->data[0][frame_y * frame->linesize[0] + frame_x]; + pp = &s->prev_frame->data[0][frame_y * s->prev_frame->linesize[0] + frame_x]; + switch (meth) { + case 1: + for (i = 0; i < frame_height; i++) { + ofs = 0; + do { + len = bytestream2_get_byte(&gb); + if (len & 0x80) { + len = (len & 0x7F) + 1; + if (ofs + len > frame_width || + bytestream2_get_bytes_left(&gb) < len) + return AVERROR_INVALIDDATA; + bytestream2_get_bufferu(&gb, &dp[ofs], len); + ofs += len; + } else { + /* interframe pixel copy */ + if (ofs + len + 1 > frame_width || !s->prev_frame->data[0]) + return AVERROR_INVALIDDATA; + memcpy(&dp[ofs], &pp[ofs], len + 1); + ofs += len + 1; } - dp += frame->linesize[0]; - pp += s->prev_frame.linesize[0]; - } - break; - - case 2: - for (i = 0; i < frame_height; i++) { - if (pb_end -pb < frame_width) - return; - memcpy(dp, pb, frame_width); - pb += frame_width; - dp += frame->linesize[0]; - pp += s->prev_frame.linesize[0]; + } while (ofs < frame_width); + if (ofs > frame_width) { + av_log(s->avctx, AV_LOG_ERROR, + "offset > width (%d > %d)\n", + ofs, frame_width); + return AVERROR_INVALIDDATA; } - break; + dp += frame->linesize[0]; + pp += s->prev_frame->linesize[0]; + } + break; - case 3: - for (i = 0; i < frame_height; i++) { - ofs = 0; - do { - if (pb_end - pb < 1) - return; - len = *pb++; - if (len & 0x80) { - len = (len & 0x7F) + 1; - if (pb_end - pb < 1) - return; - if (*pb++ == 0xFF) - len = rle_unpack(pb, pb_end - pb, len, &dp[ofs], frame_width - ofs); - else { - if (pb_end - pb < len) - return; - memcpy(&dp[ofs], pb, len); - } - pb += len; - ofs += len; + case 2: + for (i = 0; i < frame_height; i++) { + bytestream2_get_buffer(&gb, dp, frame_width); + dp += frame->linesize[0]; + pp += s->prev_frame->linesize[0]; + } + break; + + case 3: + for (i = 0; i < frame_height; i++) { + ofs = 0; + do { + len = bytestream2_get_byte(&gb); + if (len & 0x80) { + len = (len & 0x7F) + 1; + if (bytestream2_peek_byte(&gb) == 0xFF) { + int slen = len; + bytestream2_get_byte(&gb); + len = rle_unpack(gb.buffer, &dp[ofs], + len, bytestream2_get_bytes_left(&gb), + frame_width - ofs); + ofs += slen; + bytestream2_skip(&gb, len); } else { - /* interframe pixel copy */ - if (ofs + len + 1 > frame_width || !s->prev_frame.data[0]) - return; - memcpy(&dp[ofs], &pp[ofs], len + 1); - ofs += len + 1; + bytestream2_get_buffer(&gb, &dp[ofs], len); + ofs += len; } - } while (ofs < frame_width); - if (ofs > frame_width) { - av_log(s->avctx, AV_LOG_ERROR, "offset > width (%d > %d)\n", - ofs, frame_width); + } else { + /* interframe pixel copy */ + if (ofs + len + 1 > frame_width || !s->prev_frame->data[0]) + return AVERROR_INVALIDDATA; + memcpy(&dp[ofs], &pp[ofs], len + 1); + ofs += len + 1; } - dp += frame->linesize[0]; - pp += s->prev_frame.linesize[0]; + } while (ofs < frame_width); + if (ofs > frame_width) { + av_log(s->avctx, AV_LOG_ERROR, + "offset > width (%d > %d)\n", + ofs, frame_width); + return AVERROR_INVALIDDATA; } - break; + dp += frame->linesize[0]; + pp += s->prev_frame->linesize[0]; } + break; } + return 0; +} + +static av_cold int vmdvideo_decode_end(AVCodecContext *avctx) +{ + VmdVideoContext *s = avctx->priv_data; + + av_frame_free(&s->prev_frame); + av_freep(&s->unpack_buffer); + s->unpack_buffer_size = 0; + + return 0; } static av_cold int vmdvideo_decode_init(AVCodecContext *avctx) @@ -385,14 +405,16 @@ static av_cold int vmdvideo_decode_init(AVCodecContext *avctx) if (s->avctx->extradata_size != VMD_HEADER_SIZE) { av_log(s->avctx, AV_LOG_ERROR, "expected extradata size of %d\n", VMD_HEADER_SIZE); - return -1; + return AVERROR_INVALIDDATA; } vmd_header = (unsigned char *)avctx->extradata; s->unpack_buffer_size = AV_RL32(&vmd_header[800]); - s->unpack_buffer = av_malloc(s->unpack_buffer_size); - if (!s->unpack_buffer) - return -1; + if (s->unpack_buffer_size) { + s->unpack_buffer = av_malloc(s->unpack_buffer_size); + if (!s->unpack_buffer) + return AVERROR(ENOMEM); + } /* load up the initial palette */ raw_palette = &vmd_header[28]; @@ -401,10 +423,15 @@ static av_cold int vmdvideo_decode_init(AVCodecContext *avctx) r = raw_palette[palette_index++] * 4; g = raw_palette[palette_index++] * 4; b = raw_palette[palette_index++] * 4; - palette32[i] = (r << 16) | (g << 8) | (b); + palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b); + palette32[i] |= palette32[i] >> 6 & 0x30303; } - avcodec_get_frame_defaults(&s->prev_frame); + s->prev_frame = av_frame_alloc(); + if (!s->prev_frame) { + vmdvideo_decode_end(avctx); + return AVERROR(ENOMEM); + } return 0; } @@ -423,19 +450,20 @@ static int vmdvideo_decode_frame(AVCodecContext *avctx, s->size = buf_size; if (buf_size < 16) - return buf_size; + return AVERROR_INVALIDDATA; if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; - vmd_decode(s, frame); + if ((ret = vmd_decode(s, frame)) < 0) + return ret; /* make the palette available on the way out */ memcpy(frame->data[1], s->palette, PALETTE_COUNT * 4); /* shuffle frames */ - av_frame_unref(&s->prev_frame); - if ((ret = av_frame_ref(&s->prev_frame, frame)) < 0) + av_frame_unref(s->prev_frame); + if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) return ret; *got_frame = 1; @@ -444,17 +472,6 @@ static int vmdvideo_decode_frame(AVCodecContext *avctx, return buf_size; } -static av_cold int vmdvideo_decode_end(AVCodecContext *avctx) -{ - VmdVideoContext *s = avctx->priv_data; - - av_frame_unref(&s->prev_frame); - av_free(s->unpack_buffer); - - return 0; -} - - /* * Audio Decoder */ @@ -592,6 +609,9 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx, void *data, /* ensure output buffer is large enough */ audio_chunks = buf_size / s->chunk_size; + /* drop incomplete chunks */ + buf_size = audio_chunks * s->chunk_size; + /* get output buffer */ frame->nb_samples = ((silent_chunks + audio_chunks) * avctx->block_align) / avctx->channels; @@ -603,6 +623,8 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx, void *data, /* decode silent chunks */ if (silent_chunks > 0) { int silent_size = avctx->block_align * silent_chunks; + av_assert0(avctx->block_align * silent_chunks <= frame->nb_samples * avctx->channels); + if (s->out_bps == 2) { memset(output_samples_s16, 0x00, silent_size * 2); output_samples_s16 += silent_size; @@ -615,6 +637,7 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx, void *data, /* decode audio chunks */ if (audio_chunks > 0) { buf_end = buf + buf_size; + av_assert0((buf_size & (avctx->channels > 1)) == 0); while (buf_end - buf >= s->chunk_size) { if (s->out_bps == 2) { decode_audio_s16(output_samples_s16, buf, s->chunk_size, @@ -640,6 +663,7 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx, void *data, AVCodec ff_vmdvideo_decoder = { .name = "vmdvideo", + .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD video"), .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_VMDVIDEO, .priv_data_size = sizeof(VmdVideoContext), @@ -647,16 +671,15 @@ AVCodec ff_vmdvideo_decoder = { .close = vmdvideo_decode_end, .decode = vmdvideo_decode_frame, .capabilities = CODEC_CAP_DR1, - .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD video"), }; AVCodec ff_vmdaudio_decoder = { .name = "vmdaudio", + .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD audio"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_VMDAUDIO, .priv_data_size = sizeof(VmdAudioContext), .init = vmdaudio_decode_init, .decode = vmdaudio_decode_frame, .capabilities = CODEC_CAP_DR1, - .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD audio"), }; |
