You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	Electronic Arts TGQ video decoder
Originally committed as revision 15789 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
		| @@ -139,6 +139,7 @@ version <next> | ||||
| - ASS and SSA demuxer and muxer | ||||
| - liba52 wrapper removed | ||||
| - Speex decoding via libspeex | ||||
| - Electronic Arts TGQ decoder | ||||
|  | ||||
| version 0.4.9-pre1: | ||||
|  | ||||
|   | ||||
| @@ -230,6 +230,7 @@ following image formats are supported: | ||||
| @item Electronic Arts CMV    @tab     @tab  X | ||||
|     @tab Used in NHL 95 game. | ||||
| @item Electronic Arts TGV    @tab     @tab  X | ||||
| @item Electronic Arts TGQ    @tab     @tab  X | ||||
| @item FFmpeg Video 1         @tab  X  @tab  X | ||||
|     @tab experimental lossless codec (fourcc: FFV1) | ||||
| @item Flash Screen Video     @tab  X  @tab  X | ||||
|   | ||||
| @@ -68,6 +68,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER)         += dv.o | ||||
| OBJS-$(CONFIG_DXA_DECODER)             += dxa.o | ||||
| OBJS-$(CONFIG_EAC3_DECODER)            += eac3dec.o ac3dec.o ac3tab.o ac3dec_data.o ac3.o | ||||
| OBJS-$(CONFIG_EACMV_DECODER)           += eacmv.o | ||||
| OBJS-$(CONFIG_EATGQ_DECODER)           += eatgq.o | ||||
| OBJS-$(CONFIG_EATGV_DECODER)           += eatgv.o | ||||
| OBJS-$(CONFIG_EIGHTBPS_DECODER)        += 8bps.o | ||||
| OBJS-$(CONFIG_EIGHTSVX_EXP_DECODER)    += 8svx.o | ||||
|   | ||||
| @@ -79,6 +79,7 @@ void avcodec_register_all(void) | ||||
|     REGISTER_ENCDEC  (DVVIDEO, dvvideo); | ||||
|     REGISTER_DECODER (DXA, dxa); | ||||
|     REGISTER_DECODER (EACMV, eacmv); | ||||
|     REGISTER_DECODER (EATGQ, eatgq); | ||||
|     REGISTER_DECODER (EATGV, eatgv); | ||||
|     REGISTER_DECODER (EIGHTBPS, eightbps); | ||||
|     REGISTER_DECODER (EIGHTSVX_EXP, eightsvx_exp); | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
| #include "libavutil/avutil.h" | ||||
|  | ||||
| #define LIBAVCODEC_VERSION_MAJOR 52 | ||||
| #define LIBAVCODEC_VERSION_MINOR  2 | ||||
| #define LIBAVCODEC_VERSION_MINOR  3 | ||||
| #define LIBAVCODEC_VERSION_MICRO  0 | ||||
|  | ||||
| #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | ||||
| @@ -189,6 +189,7 @@ enum CodecID { | ||||
|     CODEC_ID_CMV, | ||||
|     CODEC_ID_MOTIONPIXELS, | ||||
|     CODEC_ID_TGV, | ||||
|     CODEC_ID_TGQ, | ||||
|  | ||||
|     /* various PCM "codecs" */ | ||||
|     CODEC_ID_PCM_S16LE= 0x10000, | ||||
| @@ -1388,6 +1389,7 @@ typedef struct AVCodecContext { | ||||
| #define FF_IDCT_SIMPLEVIS     18 | ||||
| #define FF_IDCT_WMV2          19 | ||||
| #define FF_IDCT_FAAN          20 | ||||
| #define FF_IDCT_EA            21 | ||||
|  | ||||
|     /** | ||||
|      * slice count | ||||
|   | ||||
							
								
								
									
										255
									
								
								libavcodec/eatgq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								libavcodec/eatgq.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,255 @@ | ||||
| /* | ||||
|  * Electronic Arts TGQ Video Decoder | ||||
|  * Copyright (c) 2007-2008 Peter Ross <pross@xvid.org> | ||||
|  * | ||||
|  * This file is part of FFmpeg. | ||||
|  * | ||||
|  * FFmpeg is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * FFmpeg is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with FFmpeg; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * @file eatgq.c | ||||
|  * Electronic Arts TGQ Video Decoder | ||||
|  * @author Peter Ross <pross@xvid.org> | ||||
|  * | ||||
|  * Technical details here: | ||||
|  * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGQ | ||||
|  */ | ||||
|  | ||||
| #include "avcodec.h" | ||||
| #define ALT_BITSTREAM_READER_LE | ||||
| #include "bitstream.h" | ||||
| #include "bytestream.h" | ||||
| #include "dsputil.h" | ||||
| extern const uint16_t ff_inv_aanscales[64]; //mpegvideo_enc.c | ||||
|  | ||||
| typedef struct TgqContext { | ||||
|     AVCodecContext *avctx; | ||||
|     DSPContext dsp; | ||||
|     AVFrame frame; | ||||
|     int width,height; | ||||
|     ScanTable scantable; | ||||
|     int qtable[64]; | ||||
| } TgqContext; | ||||
|  | ||||
| static av_cold int tgq_decode_init(AVCodecContext *avctx){ | ||||
|     TgqContext *s = avctx->priv_data; | ||||
|     s->avctx = avctx; | ||||
|     if(avctx->idct_algo==FF_IDCT_AUTO) | ||||
|         avctx->idct_algo=FF_IDCT_EA; | ||||
|     dsputil_init(&s->dsp, avctx); | ||||
|     ff_init_scantable(s->dsp.idct_permutation, &s->scantable, ff_zigzag_direct); | ||||
|     avctx->time_base = (AVRational){1, 15}; | ||||
|     avctx->pix_fmt = PIX_FMT_YUV420P; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void tgq_decode_block(TgqContext *s, DCTELEM block[64], GetBitContext *gb){ | ||||
|     uint8_t *perm = s->scantable.permutated; | ||||
|     int i,j,value; | ||||
|     block[0] = get_sbits(gb,8) * s->qtable[0]; | ||||
|     for(i=1; i<64; ) { | ||||
|         switch(show_bits(gb,3)) { | ||||
|         case 4: | ||||
|             block[perm[i++]] = 0; | ||||
|         case 0: | ||||
|             block[perm[i++]] = 0; | ||||
|             skip_bits(gb,3); | ||||
|             break; | ||||
|         case 5: | ||||
|         case 1: | ||||
|             skip_bits(gb,2); | ||||
|             value = get_bits(gb,6); | ||||
|             for(j=0; j<value; j++) | ||||
|                 block[perm[i++]] = 0; | ||||
|             break; | ||||
|         case 6: | ||||
|             skip_bits(gb,3); | ||||
|             block[perm[i]] = -s->qtable[perm[i]]; | ||||
|             i++; | ||||
|             break; | ||||
|         case 2: | ||||
|             skip_bits(gb,3); | ||||
|             block[perm[i]] = s->qtable[perm[i]]; | ||||
|             i++; | ||||
|             break; | ||||
|         case 7: // 111b | ||||
|         case 3: // 011b | ||||
|             skip_bits(gb,2); | ||||
|             if (show_bits(gb,6)==0x3F) { | ||||
|                 skip_bits(gb, 6); | ||||
|                 block[perm[i]] = get_sbits(gb,8)*s->qtable[perm[i]]; | ||||
|             }else{ | ||||
|                 block[perm[i]] = get_sbits(gb,6)*s->qtable[perm[i]]; | ||||
|             } | ||||
|             i++; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     block[0] += 128<<4; | ||||
| } | ||||
|  | ||||
| static void tgq_idct_put_mb(TgqContext *s, DCTELEM (*block)[64], int mb_x, int mb_y){ | ||||
|     int linesize= s->frame.linesize[0]; | ||||
|     uint8_t *dest_y  = s->frame.data[0] + (mb_y * 16* linesize            ) + mb_x * 16; | ||||
|     uint8_t *dest_cb = s->frame.data[1] + (mb_y * 8 * s->frame.linesize[1]) + mb_x * 8; | ||||
|     uint8_t *dest_cr = s->frame.data[2] + (mb_y * 8 * s->frame.linesize[2]) + mb_x * 8; | ||||
|  | ||||
|     s->dsp.idct_put(dest_y                 , linesize, block[0]); | ||||
|     s->dsp.idct_put(dest_y              + 8, linesize, block[1]); | ||||
|     s->dsp.idct_put(dest_y + 8*linesize    , linesize, block[2]); | ||||
|     s->dsp.idct_put(dest_y + 8*linesize + 8, linesize, block[3]); | ||||
|     if(!(s->avctx->flags&CODEC_FLAG_GRAY)){ | ||||
|          s->dsp.idct_put(dest_cb, s->frame.linesize[1], block[4]); | ||||
|          s->dsp.idct_put(dest_cr, s->frame.linesize[2], block[5]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline void tgq_dconly(TgqContext *s, unsigned char *dst, int dst_stride, int dc){ | ||||
|     int level = av_clip_uint8((dc*s->qtable[0] + 2056)>>4); | ||||
|     int j; | ||||
|     for(j=0;j<8;j++) | ||||
|         memset(dst+j*dst_stride, level, 8); | ||||
| } | ||||
|  | ||||
| static void tgq_idct_put_mb_dconly(TgqContext *s, int mb_x, int mb_y, const int8_t *dc) | ||||
| { | ||||
|     int linesize= s->frame.linesize[0]; | ||||
|     uint8_t *dest_y  = s->frame.data[0] + (mb_y * 16* linesize            ) + mb_x * 16; | ||||
|     uint8_t *dest_cb = s->frame.data[1] + (mb_y * 8 * s->frame.linesize[1]) + mb_x * 8; | ||||
|     uint8_t *dest_cr = s->frame.data[2] + (mb_y * 8 * s->frame.linesize[2]) + mb_x * 8; | ||||
|     tgq_dconly(s,dest_y                 , linesize, dc[0]); | ||||
|     tgq_dconly(s,dest_y              + 8, linesize, dc[1]); | ||||
|     tgq_dconly(s,dest_y + 8*linesize    , linesize, dc[2]); | ||||
|     tgq_dconly(s,dest_y + 8*linesize + 8, linesize, dc[3]); | ||||
|     if(!(s->avctx->flags&CODEC_FLAG_GRAY)) { | ||||
|         tgq_dconly(s,dest_cb, s->frame.linesize[1], dc[4]); | ||||
|         tgq_dconly(s,dest_cr, s->frame.linesize[2], dc[5]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tgq_decode_mb(TgqContext *s, int mb_y, int mb_x, const int8_t **bs, const int8_t *buf_end){ | ||||
|     int mode; | ||||
|     int i; | ||||
|     int8_t dc[6]; | ||||
|     DCTELEM block[6][64]; | ||||
|  | ||||
|     mode = bytestream_get_byte((const uint8_t**)bs); | ||||
|     if (mode>buf_end-*bs) { | ||||
|         av_log(s->avctx, AV_LOG_ERROR, "truncated macroblock\n"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (mode>12) { | ||||
|         GetBitContext gb; | ||||
|         init_get_bits(&gb, *bs, mode*8); | ||||
|         for(i=0; i<6; i++) | ||||
|             tgq_decode_block(s, block[i], &gb); | ||||
|         tgq_idct_put_mb(s, block, mb_x, mb_y); | ||||
|     }else{ | ||||
|         if (mode==3) { | ||||
|             memset(dc, (*bs)[0], 4); | ||||
|             dc[4] = (*bs)[1]; | ||||
|             dc[5] = (*bs)[2]; | ||||
|         }else if (mode==6) { | ||||
|             memcpy(dc, *bs, 6); | ||||
|         }else if (mode==12) { | ||||
|             for(i=0; i<6; i++) | ||||
|                 dc[i] = (*bs)[i*2]; | ||||
|         }else{ | ||||
|             av_log(s->avctx, AV_LOG_ERROR, "unsupported mb mode %i\n", mode); | ||||
|         } | ||||
|         tgq_idct_put_mb_dconly(s, mb_x, mb_y, dc); | ||||
|     } | ||||
|     *bs += mode; | ||||
| } | ||||
|  | ||||
| static void tgq_calculate_qtable(TgqContext *s, int quant){ | ||||
|     int i,j; | ||||
|     const int a = (14*(100-quant))/100 + 1; | ||||
|     const int b = (11*(100-quant))/100 + 4; | ||||
|     for(j=0;j<8;j++) | ||||
|     for(i=0;i<8;i++) | ||||
|         if (s->avctx->idct_algo==FF_IDCT_EA) | ||||
|             s->qtable[j*8+i] = ((a*(j+i)/(7+7) + b)*ff_inv_aanscales[j*8+i])>>(14-4); | ||||
|         else | ||||
|             s->qtable[j*8+i] = (a*(j+i)/(7+7) + b)<<3; | ||||
| } | ||||
|  | ||||
| static int tgq_decode_frame(AVCodecContext *avctx, | ||||
|                             void *data, int *data_size, | ||||
|                             const uint8_t *buf, int buf_size){ | ||||
|     const uint8_t *buf_start = buf; | ||||
|     const uint8_t *buf_end = buf + buf_size; | ||||
|     TgqContext *s = avctx->priv_data; | ||||
|     int x,y; | ||||
|  | ||||
|     int big_endian = AV_RL32(&buf[4]) > 0x000FFFFF; | ||||
|     buf += 8; | ||||
|  | ||||
|     if(8>buf_end-buf) { | ||||
|         av_log(avctx, AV_LOG_WARNING, "truncated header\n"); | ||||
|         return -1; | ||||
|     } | ||||
|     s->width  = big_endian ? AV_RB16(&buf[0]) : AV_RL16(&buf[0]); | ||||
|     s->height = big_endian ? AV_RB16(&buf[2]) : AV_RL16(&buf[2]); | ||||
|  | ||||
|     if (s->avctx->width!=s->width || s->avctx->height!=s->height) { | ||||
|         avcodec_set_dimensions(s->avctx, s->width, s->height); | ||||
|         if (s->frame.data[0]) | ||||
|             avctx->release_buffer(avctx, &s->frame); | ||||
|     } | ||||
|     tgq_calculate_qtable(s, buf[4]); | ||||
|     buf += 8; | ||||
|  | ||||
|     if (!s->frame.data[0]) { | ||||
|         s->frame.key_frame = 1; | ||||
|         s->frame.pict_type = FF_I_TYPE; | ||||
|         s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; | ||||
|         if (avctx->get_buffer(avctx, &s->frame)) { | ||||
|             av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (y=0; y<(avctx->height+15)/16; y++) | ||||
|     for (x=0; x<(avctx->width+15)/16; x++) | ||||
|         tgq_decode_mb(s, y, x, (const int8_t**)&buf, (const int8_t*)buf_end); | ||||
|  | ||||
|     *data_size = sizeof(AVFrame); | ||||
|     *(AVFrame*)data = s->frame; | ||||
|  | ||||
|     return buf-buf_start; | ||||
| } | ||||
|  | ||||
| static av_cold int tgq_decode_end(AVCodecContext *avctx){ | ||||
|     TgqContext *s = avctx->priv_data; | ||||
|     if (s->frame.data[0]) | ||||
|         s->avctx->release_buffer(avctx, &s->frame); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| AVCodec eatgq_decoder = { | ||||
|     "eatgq", | ||||
|     CODEC_TYPE_VIDEO, | ||||
|     CODEC_ID_TGQ, | ||||
|     sizeof(TgqContext), | ||||
|     tgq_decode_init, | ||||
|     NULL, | ||||
|     tgq_decode_end, | ||||
|     tgq_decode_frame, | ||||
|     CODEC_CAP_DR1, | ||||
|     .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGQ Video"), | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user