From 77ccd3ba546e8a420aea4c5b915df72abb49f7fd Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Fri, 2 Feb 2007 06:45:21 +0000 Subject: [PATCH] General approach to parsing chunks in VC-1 AP Originally committed as revision 7803 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavcodec/vc1.c | 164 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 40 deletions(-) diff --git a/libavcodec/vc1.c b/libavcodec/vc1.c index 0c17083039..722e885144 100644 --- a/libavcodec/vc1.c +++ b/libavcodec/vc1.c @@ -46,6 +46,19 @@ extern const uint16_t ff_msmp4_mb_i_table[64][2]; #define AC_VLC_BITS 9 static const uint16_t table_mb_intra[64][2]; +/** Markers used if VC-1 AP frame data */ +//@{ +enum VC1Code{ + VC1_CODE_RES0 = 0x00000100, + VC1_CODE_ESCAPE = 0x00000103, + VC1_CODE_ENDOFSEQ = 0x0000010A, + VC1_CODE_SLICE, + VC1_CODE_FIELD, + VC1_CODE_FRAME, + VC1_CODE_ENTRYPOINT, + VC1_CODE_SEQHDR, +}; +//@} /** Available Profiles */ //@{ @@ -4094,6 +4107,42 @@ static void vc1_decode_blocks(VC1Context *v) } } +#define IS_MARKER(x) ((((x) & ~0xFF) == VC1_CODE_RES0) && ((x) != VC1_CODE_ESCAPE)) + +/** Find VC-1 marker in buffer + * @return position where next marker starts or end of buffer if no marker found + */ +static av_always_inline uint8_t* find_next_marker(uint8_t *src, uint8_t *end) +{ + uint32_t mrk = 0xFFFFFFFF; + + if(end-src < 4) return end; + while(src < end){ + mrk = (mrk << 8) | *src++; + if(IS_MARKER(mrk)) + return src-4; + } + return end; +} + +static av_always_inline int vc1_unescape_buffer(uint8_t *src, int size, uint8_t *dst) +{ + int dsize = 0, i; + + if(size < 4){ + for(dsize = 0; dsize < size; dsize++) *dst++ = *src++; + return size; + } + for(i = 0; i < size; i++, src++) { + if(src[0] == 3 && i >= 2 && !src[-1] && !src[-2] && i < size-1 && src[1] < 4) { + dst[dsize++] = src[1]; + src++; + i++; + } else + dst[dsize++] = *src; + } + return dsize; +} /** Initialize a VC1/WMV3 decoder * @todo TODO: Handle VC-1 IDUs (Transport level?) @@ -4145,44 +4194,47 @@ static int vc1_decode_init(AVCodecContext *avctx) av_log(avctx, AV_LOG_INFO, "Read %i bits in overflow\n", -count); } } else { // VC1/WVC1 - int edata_size = avctx->extradata_size; - uint8_t *edata = avctx->extradata; + uint8_t *start = avctx->extradata, *end = avctx->extradata + avctx->extradata_size; + uint8_t *next; int size, buf2_size; + uint8_t *buf2 = NULL; + int seq_inited = 0, ep_inited = 0; if(avctx->extradata_size < 16) { - av_log(avctx, AV_LOG_ERROR, "Extradata size too small: %i\n", edata_size); + av_log(avctx, AV_LOG_ERROR, "Extradata size too small: %i\n", avctx->extradata_size); return -1; } - while(edata_size > 8) { - // test if we've found header - if(AV_RB32(edata) == 0x0000010F) { - edata += 4; - edata_size -= 4; + + buf2 = av_mallocz(avctx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if(start[0]) start++; // in WVC1 extradata first byte is its size + next = start; + for(; next < end; start = next){ + next = find_next_marker(start + 4, end); + size = next - start - 4; + if(size <= 0) continue; + buf2_size = vc1_unescape_buffer(start + 4, size, buf2); + init_get_bits(&gb, buf2, buf2_size * 8); + switch(AV_RB32(start)){ + case VC1_CODE_SEQHDR: + if(decode_sequence_header(avctx, &gb) < 0){ + av_free(buf2); + return -1; + } + seq_inited = 1; + break; + case VC1_CODE_ENTRYPOINT: + if(decode_entry_point(avctx, &gb) < 0){ + av_free(buf2); + return -1; + } + ep_inited = 1; break; } - edata_size--; - edata++; } - - init_get_bits(&gb, edata, edata_size*8); - - if (decode_sequence_header(avctx, &gb) < 0) - return -1; - - while(edata_size > 8) { - // test if we've found entry point - if(AV_RB32(edata) == 0x0000010E) { - edata += 4; - edata_size -= 4; - break; - } - edata_size--; - edata++; + av_free(buf2); + if(!seq_inited || !ep_inited){ + av_log(avctx, AV_LOG_ERROR, "Incomplete extradata\n"); + return -1; } - - init_get_bits(&gb, edata, edata_size*8); - - if (decode_entry_point(avctx, &gb) < 0) - return -1; } avctx->has_b_frames= !!(avctx->max_b_frames); s->low_delay = !avctx->has_b_frames; @@ -4246,17 +4298,49 @@ static int vc1_decode_frame(AVCodecContext *avctx, s->current_picture_ptr= &s->picture[i]; } - //for advanced profile we need to unescape buffer + //for advanced profile we may need to parse and unescape data if (avctx->codec_id == CODEC_ID_VC1) { - int i, buf_size2; - buf2 = av_malloc(buf_size + FF_INPUT_BUFFER_PADDING_SIZE); - buf_size2 = 0; - for(i = 0; i < buf_size; i++) { - if(buf[i] == 3 && i >= 2 && !buf[i-1] && !buf[i-2] && i < buf_size-1 && buf[i+1] < 4) { - buf2[buf_size2++] = buf[i+1]; - i++; - } else - buf2[buf_size2++] = buf[i]; + int buf_size2 = 0; + buf2 = av_mallocz(buf_size + FF_INPUT_BUFFER_PADDING_SIZE); + + if(IS_MARKER(AV_RB32(buf))){ /* frame starts with marker and needs to be parsed */ + uint8_t *dst = buf2, *start, *end, *next; + int size; + + next = buf; + for(start = buf, end = buf + buf_size; next < end; start = next){ + next = find_next_marker(start + 4, end); + size = next - start - 4; + if(size <= 0) continue; + switch(AV_RB32(start)){ + case VC1_CODE_FRAME: + buf_size2 = vc1_unescape_buffer(start + 4, size, buf2); + break; + case VC1_CODE_ENTRYPOINT: /* it should be before frame data */ + buf_size2 = vc1_unescape_buffer(start + 4, size, buf2); + init_get_bits(&s->gb, buf2, buf_size2*8); + decode_entry_point(avctx, &s->gb); + break; + case VC1_CODE_SLICE: + av_log(avctx, AV_LOG_ERROR, "Sliced decoding is not implemented (yet)\n"); + av_free(buf2); + return -1; + } + } + }else if(v->interlace && ((buf[0] & 0xC0) == 0xC0)){ /* WVC1 interlaced stores both fields divided by marker */ + uint8_t *divider; + + divider = find_next_marker(buf, buf + buf_size); + if((divider == (buf + buf_size)) || AV_RB32(divider) != VC1_CODE_FIELD){ + av_log(avctx, AV_LOG_ERROR, "Error in WVC1 interlaced frame\n"); + return -1; + } + + buf_size2 = vc1_unescape_buffer(buf, divider - buf, buf2); + // TODO + av_free(buf2);return -1; + }else{ + buf_size2 = vc1_unescape_buffer(buf, buf_size, buf2); } init_get_bits(&s->gb, buf2, buf_size2*8); } else