Mercurial > libavcodec.hg
annotate eatgv.c @ 9976:e52cd349e708 libavcodec
Only compile in NEON optimizations for H.264 when the H.264 decoder is enabled.
| author | diego |
|---|---|
| date | Wed, 22 Jul 2009 22:33:33 +0000 |
| parents | 0dce4fe6e6f3 |
| children | dff431441c9c |
| rev | line source |
|---|---|
| 7510 | 1 /* |
| 2 * Electronic Arts TGV Video Decoder | |
| 3 * Copyright (c) 2007-2008 Peter Ross | |
| 4 * | |
| 5 * This file is part of FFmpeg. | |
| 6 * | |
| 7 * FFmpeg is free software; you can redistribute it and/or | |
| 8 * modify it under the terms of the GNU Lesser General Public | |
| 9 * License as published by the Free Software Foundation; either | |
| 10 * version 2.1 of the License, or (at your option) any later version. | |
| 11 * | |
| 12 * FFmpeg is distributed in the hope that it will be useful, | |
| 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 15 * Lesser General Public License for more details. | |
| 16 * | |
| 17 * You should have received a copy of the GNU Lesser General Public | |
| 18 * License along with FFmpeg; if not, write to the Free Software | |
| 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
| 20 */ | |
| 21 | |
| 22 /** | |
|
8718
e9d9d946f213
Use full internal pathname in doxygen @file directives.
diego
parents:
8597
diff
changeset
|
23 * @file libavcodec/eatgv.c |
| 7510 | 24 * Electronic Arts TGV Video Decoder |
| 25 * by Peter Ross (suxen_drol at hotmail dot com) | |
| 26 * | |
| 27 * Technical details here: | |
| 28 * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGV | |
| 29 */ | |
| 30 | |
| 31 #include "avcodec.h" | |
| 32 #define ALT_BITSTREAM_READER_LE | |
| 9428 | 33 #include "get_bits.h" |
| 8597 | 34 #include "libavutil/lzo.h" |
| 7510 | 35 |
| 36 #define EA_PREAMBLE_SIZE 8 | |
| 37 #define kVGT_TAG MKTAG('k', 'V', 'G', 'T') | |
| 38 | |
| 39 typedef struct TgvContext { | |
| 40 AVCodecContext *avctx; | |
| 41 AVFrame frame; | |
| 42 AVFrame last_frame; | |
| 43 int width,height; | |
| 44 unsigned int palette[AVPALETTE_COUNT]; | |
| 45 | |
| 46 int (*mv_codebook)[2]; | |
| 47 unsigned char (*block_codebook)[16]; | |
| 48 int num_mvs; ///< current length of mv_codebook | |
| 49 int num_blocks_packed; ///< current length of block_codebook | |
| 50 } TgvContext; | |
| 51 | |
| 52 static av_cold int tgv_decode_init(AVCodecContext *avctx){ | |
| 53 TgvContext *s = avctx->priv_data; | |
| 54 s->avctx = avctx; | |
| 55 avctx->time_base = (AVRational){1, 15}; | |
| 56 avctx->pix_fmt = PIX_FMT_PAL8; | |
| 57 return 0; | |
| 58 } | |
| 59 | |
| 60 /** | |
| 61 * Unpack buffer | |
| 62 * @return 0 on success, -1 on critical buffer underflow | |
| 63 */ | |
| 64 static int unpack(const uint8_t *src, const uint8_t *src_end, unsigned char *dst, int width, int height) { | |
| 65 unsigned char *dst_end = dst + width*height; | |
|
8801
154d81241ac0
Add av_uninit macro to variable to avoid false positive warning:
diego
parents:
8718
diff
changeset
|
66 int size, size1, size2, av_uninit(offset), run; |
| 7510 | 67 unsigned char *dst_start = dst; |
| 68 | |
| 69 if (src[0] & 0x01) | |
| 70 src += 5; | |
| 71 else | |
| 72 src += 2; | |
| 73 | |
| 74 if (src+3>src_end) | |
| 75 return -1; | |
| 76 size = AV_RB24(src); | |
| 77 src += 3; | |
| 78 | |
| 79 while(size>0 && src<src_end) { | |
| 80 | |
| 81 /* determine size1 and size2 */ | |
| 82 size1 = (src[0] & 3); | |
| 83 if ( src[0] & 0x80 ) { // 1 | |
| 84 if (src[0] & 0x40 ) { // 11 | |
| 85 if ( src[0] & 0x20 ) { // 111 | |
| 86 if ( src[0] < 0xFC ) // !(111111) | |
| 87 size1 = (((src[0] & 31) + 1) << 2); | |
| 88 src++; | |
| 89 size2 = 0; | |
| 90 } else { // 110 | |
| 91 offset = ((src[0] & 0x10) << 12) + AV_RB16(&src[1]) + 1; | |
| 92 size2 = ((src[0] & 0xC) << 6) + src[3] + 5; | |
| 93 src += 4; | |
| 94 } | |
| 95 } else { // 10 | |
| 96 size1 = ( ( src[1] & 0xC0) >> 6 ); | |
| 97 offset = (AV_RB16(&src[1]) & 0x3FFF) + 1; | |
| 98 size2 = (src[0] & 0x3F) + 4; | |
| 99 src += 3; | |
| 100 } | |
| 101 } else { // 0 | |
| 102 offset = ((src[0] & 0x60) << 3) + src[1] + 1; | |
| 103 size2 = ((src[0] & 0x1C) >> 2) + 3; | |
| 104 src += 2; | |
| 105 } | |
| 106 | |
| 107 | |
| 108 /* fetch strip from src */ | |
| 109 if (size1>src_end-src) | |
| 110 break; | |
| 111 | |
| 112 if (size1>0) { | |
| 113 size -= size1; | |
| 114 run = FFMIN(size1, dst_end-dst); | |
| 115 memcpy(dst, src, run); | |
| 116 dst += run; | |
| 117 src += run; | |
| 118 } | |
| 119 | |
| 120 if (size2>0) { | |
| 121 if (dst-dst_start<offset) | |
| 122 return 0; | |
| 123 size -= size2; | |
| 124 run = FFMIN(size2, dst_end-dst); | |
| 125 av_memcpy_backptr(dst, offset, run); | |
| 126 dst += run; | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 return 0; | |
| 131 } | |
| 132 | |
| 133 /** | |
| 134 * Decode inter-frame | |
| 135 * @return 0 on success, -1 on critical buffer underflow | |
| 136 */ | |
| 137 static int tgv_decode_inter(TgvContext * s, const uint8_t *buf, const uint8_t *buf_end){ | |
| 138 unsigned char *frame0_end = s->last_frame.data[0] + s->avctx->width*s->last_frame.linesize[0]; | |
| 139 int num_mvs; | |
| 140 int num_blocks_raw; | |
| 141 int num_blocks_packed; | |
| 142 int vector_bits; | |
| 143 int i,j,x,y; | |
| 144 GetBitContext gb; | |
| 145 int mvbits; | |
| 146 const unsigned char *blocks_raw; | |
| 147 | |
| 148 if(buf+12>buf_end) | |
| 149 return -1; | |
| 150 | |
| 151 num_mvs = AV_RL16(&buf[0]); | |
| 152 num_blocks_raw = AV_RL16(&buf[2]); | |
| 153 num_blocks_packed = AV_RL16(&buf[4]); | |
| 154 vector_bits = AV_RL16(&buf[6]); | |
| 155 buf += 12; | |
| 156 | |
| 157 /* allocate codebook buffers as neccessary */ | |
| 158 if (num_mvs > s->num_mvs) { | |
| 159 s->mv_codebook = av_realloc(s->mv_codebook, num_mvs*2*sizeof(int)); | |
| 160 s->num_mvs = num_mvs; | |
| 161 } | |
| 162 | |
| 163 if (num_blocks_packed > s->num_blocks_packed) { | |
| 164 s->block_codebook = av_realloc(s->block_codebook, num_blocks_packed*16*sizeof(unsigned char)); | |
| 165 s->num_blocks_packed = num_blocks_packed; | |
| 166 } | |
| 167 | |
| 168 /* read motion vectors */ | |
| 169 mvbits = (num_mvs*2*10+31) & ~31; | |
| 170 | |
| 171 if (buf+(mvbits>>3)+16*num_blocks_raw+8*num_blocks_packed>buf_end) | |
| 172 return -1; | |
| 173 | |
| 174 init_get_bits(&gb, buf, mvbits); | |
| 175 for (i=0; i<num_mvs; i++) { | |
| 176 s->mv_codebook[i][0] = get_sbits(&gb, 10); | |
| 177 s->mv_codebook[i][1] = get_sbits(&gb, 10); | |
| 178 } | |
| 179 buf += mvbits>>3; | |
| 180 | |
| 181 /* note ptr to uncompressed blocks */ | |
| 182 blocks_raw = buf; | |
| 183 buf += num_blocks_raw*16; | |
| 184 | |
| 185 /* read compressed blocks */ | |
| 186 init_get_bits(&gb, buf, (buf_end-buf)<<3); | |
| 187 for (i=0; i<num_blocks_packed; i++) { | |
| 188 int tmp[4]; | |
| 189 for(j=0; j<4; j++) | |
| 190 tmp[j] = get_bits(&gb, 8); | |
| 191 for(j=0; j<16; j++) | |
| 192 s->block_codebook[i][15-j] = tmp[get_bits(&gb, 2)]; | |
| 193 } | |
| 194 | |
| 195 /* read vectors and build frame */ | |
| 196 for(y=0; y<s->avctx->height/4; y++) | |
| 197 for(x=0; x<s->avctx->width/4; x++) { | |
| 198 unsigned int vector = get_bits(&gb, vector_bits); | |
| 199 const unsigned char *src; | |
| 200 int src_stride; | |
| 201 | |
| 202 if (vector < num_mvs) { | |
| 203 src = s->last_frame.data[0] + | |
| 204 (y*4 + s->mv_codebook[vector][1])*s->last_frame.linesize[0] + | |
| 205 x*4 + s->mv_codebook[vector][0]; | |
| 206 src_stride = s->last_frame.linesize[0]; | |
| 207 if (src+3*src_stride+3>=frame0_end) | |
| 208 continue; | |
| 209 }else{ | |
| 210 int offset = vector - num_mvs; | |
| 211 if (offset<num_blocks_raw) | |
| 212 src = blocks_raw + 16*offset; | |
| 213 else if (offset-num_blocks_raw<num_blocks_packed) | |
| 214 src = s->block_codebook[offset-num_blocks_raw]; | |
| 215 else | |
| 216 continue; | |
| 217 src_stride = 4; | |
| 218 } | |
| 219 | |
| 220 for(j=0; j<4; j++) | |
| 221 for(i=0; i<4; i++) | |
| 222 s->frame.data[0][ (y*4+j)*s->frame.linesize[0] + (x*4+i) ] = | |
| 223 src[j*src_stride + i]; | |
| 224 } | |
| 225 | |
| 226 return 0; | |
| 227 } | |
| 228 | |
| 229 /** release AVFrame buffers if allocated */ | |
| 230 static void cond_release_buffer(AVFrame *pic) | |
| 231 { | |
| 232 if (pic->data[0]) { | |
| 233 av_freep(&pic->data[0]); | |
| 234 av_free(pic->data[1]); | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 static int tgv_decode_frame(AVCodecContext *avctx, | |
| 239 void *data, int *data_size, | |
|
9355
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
240 AVPacket *avpkt) |
| 7510 | 241 { |
|
9355
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
242 const uint8_t *buf = avpkt->data; |
|
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
243 int buf_size = avpkt->size; |
| 7510 | 244 TgvContext *s = avctx->priv_data; |
| 245 const uint8_t *buf_end = buf + buf_size; | |
| 246 int chunk_type; | |
| 247 | |
| 248 chunk_type = AV_RL32(&buf[0]); | |
| 249 buf += EA_PREAMBLE_SIZE; | |
| 250 | |
| 251 if (chunk_type==kVGT_TAG) { | |
| 252 int pal_count, i; | |
| 253 if(buf+12>buf_end) { | |
| 254 av_log(avctx, AV_LOG_WARNING, "truncated header\n"); | |
| 255 return -1; | |
| 256 } | |
| 257 | |
| 258 s->width = AV_RL16(&buf[0]); | |
| 259 s->height = AV_RL16(&buf[2]); | |
| 260 if (s->avctx->width!=s->width || s->avctx->height!=s->height) { | |
| 261 avcodec_set_dimensions(s->avctx, s->width, s->height); | |
| 262 cond_release_buffer(&s->frame); | |
| 263 cond_release_buffer(&s->last_frame); | |
| 264 } | |
| 265 | |
| 266 pal_count = AV_RL16(&buf[6]); | |
| 267 buf += 12; | |
| 268 for(i=0; i<pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) { | |
| 269 s->palette[i] = AV_RB24(buf); | |
| 270 buf += 3; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 if (avcodec_check_dimensions(avctx, s->width, s->height)) | |
| 275 return -1; | |
| 276 | |
| 277 /* shuffle */ | |
| 278 FFSWAP(AVFrame, s->frame, s->last_frame); | |
| 279 if (!s->frame.data[0]) { | |
| 280 s->frame.reference = 1; | |
| 281 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; | |
| 282 s->frame.linesize[0] = s->width; | |
| 283 | |
| 284 /* allocate additional 12 bytes to accomodate av_memcpy_backptr() OUTBUF_PADDED optimisation */ | |
| 285 s->frame.data[0] = av_malloc(s->width*s->height + 12); | |
| 286 if (!s->frame.data[0]) | |
| 287 return AVERROR_NOMEM; | |
| 288 s->frame.data[1] = av_malloc(AVPALETTE_SIZE); | |
| 289 if (!s->frame.data[1]) { | |
| 290 av_freep(&s->frame.data[0]); | |
| 291 return AVERROR_NOMEM; | |
| 292 } | |
| 293 } | |
| 294 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); | |
| 295 | |
| 296 if(chunk_type==kVGT_TAG) { | |
| 297 s->frame.key_frame = 1; | |
| 298 s->frame.pict_type = FF_I_TYPE; | |
| 299 if (unpack(buf, buf_end, s->frame.data[0], s->avctx->width, s->avctx->height)<0) { | |
| 300 av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n"); | |
| 301 return -1; | |
| 302 } | |
| 303 }else{ | |
| 304 if (!s->last_frame.data[0]) { | |
| 305 av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); | |
| 306 return buf_size; | |
| 307 } | |
| 308 s->frame.key_frame = 0; | |
| 309 s->frame.pict_type = FF_P_TYPE; | |
| 310 if (tgv_decode_inter(s, buf, buf_end)<0) { | |
| 311 av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); | |
| 312 return -1; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 *data_size = sizeof(AVFrame); | |
| 317 *(AVFrame*)data = s->frame; | |
| 318 | |
| 319 return buf_size; | |
| 320 } | |
| 321 | |
| 322 static av_cold int tgv_decode_end(AVCodecContext *avctx) | |
| 323 { | |
| 324 TgvContext *s = avctx->priv_data; | |
| 325 cond_release_buffer(&s->frame); | |
| 326 cond_release_buffer(&s->last_frame); | |
| 327 av_free(s->mv_codebook); | |
| 328 av_free(s->block_codebook); | |
| 329 return 0; | |
| 330 } | |
| 331 | |
| 332 AVCodec eatgv_decoder = { | |
| 333 "eatgv", | |
| 334 CODEC_TYPE_VIDEO, | |
| 335 CODEC_ID_TGV, | |
| 336 sizeof(TgvContext), | |
| 337 tgv_decode_init, | |
| 338 NULL, | |
| 339 tgv_decode_end, | |
| 340 tgv_decode_frame, | |
|
9083
bf274494b66e
Change a bunch of codec long_names to be more consistent and descriptive.
diego
parents:
8801
diff
changeset
|
341 .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGV video"), |
| 7510 | 342 }; |
