yading@10: /* yading@10: * Electronic Arts TGQ Video Decoder yading@10: * Copyright (c) 2007-2008 Peter Ross yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * Electronic Arts TGQ Video Decoder yading@10: * @author Peter Ross yading@10: * yading@10: * Technical details here: yading@10: * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGQ yading@10: */ yading@10: yading@10: #include "avcodec.h" yading@10: #define BITSTREAM_READER_LE yading@10: #include "get_bits.h" yading@10: #include "bytestream.h" yading@10: #include "dsputil.h" yading@10: #include "aandcttab.h" yading@10: #include "eaidct.h" yading@10: #include "internal.h" yading@10: yading@10: typedef struct TgqContext { yading@10: AVCodecContext *avctx; yading@10: int width, height; yading@10: ScanTable scantable; yading@10: int qtable[64]; yading@10: DECLARE_ALIGNED(16, int16_t, block)[6][64]; yading@10: GetByteContext gb; yading@10: } TgqContext; yading@10: yading@10: static av_cold int tgq_decode_init(AVCodecContext *avctx) yading@10: { yading@10: TgqContext *s = avctx->priv_data; yading@10: uint8_t idct_permutation[64]; yading@10: s->avctx = avctx; yading@10: ff_init_scantable_permutation(idct_permutation, FF_NO_IDCT_PERM); yading@10: ff_init_scantable(idct_permutation, &s->scantable, ff_zigzag_direct); yading@10: avctx->time_base = (AVRational){1, 15}; yading@10: avctx->pix_fmt = AV_PIX_FMT_YUV420P; yading@10: return 0; yading@10: } yading@10: yading@10: static void tgq_decode_block(TgqContext *s, int16_t block[64], GetBitContext *gb) yading@10: { yading@10: uint8_t *perm = s->scantable.permutated; yading@10: int i, j, value; yading@10: block[0] = get_sbits(gb, 8) * s->qtable[0]; yading@10: for (i = 1; i < 64;) { yading@10: switch (show_bits(gb, 3)) { yading@10: case 4: yading@10: block[perm[i++]] = 0; yading@10: case 0: yading@10: block[perm[i++]] = 0; yading@10: skip_bits(gb, 3); yading@10: break; yading@10: case 5: yading@10: case 1: yading@10: skip_bits(gb, 2); yading@10: value = get_bits(gb, 6); yading@10: for (j = 0; j < value; j++) yading@10: block[perm[i++]] = 0; yading@10: break; yading@10: case 6: yading@10: skip_bits(gb, 3); yading@10: block[perm[i]] = -s->qtable[perm[i]]; yading@10: i++; yading@10: break; yading@10: case 2: yading@10: skip_bits(gb, 3); yading@10: block[perm[i]] = s->qtable[perm[i]]; yading@10: i++; yading@10: break; yading@10: case 7: // 111b yading@10: case 3: // 011b yading@10: skip_bits(gb, 2); yading@10: if (show_bits(gb, 6) == 0x3F) { yading@10: skip_bits(gb, 6); yading@10: block[perm[i]] = get_sbits(gb, 8) * s->qtable[perm[i]]; yading@10: } else { yading@10: block[perm[i]] = get_sbits(gb, 6) * s->qtable[perm[i]]; yading@10: } yading@10: i++; yading@10: break; yading@10: } yading@10: } yading@10: block[0] += 128 << 4; yading@10: } yading@10: yading@10: static void tgq_idct_put_mb(TgqContext *s, int16_t (*block)[64], AVFrame *frame, yading@10: int mb_x, int mb_y) yading@10: { yading@10: int linesize = frame->linesize[0]; yading@10: uint8_t *dest_y = frame->data[0] + (mb_y * 16 * linesize) + mb_x * 16; yading@10: uint8_t *dest_cb = frame->data[1] + (mb_y * 8 * frame->linesize[1]) + mb_x * 8; yading@10: uint8_t *dest_cr = frame->data[2] + (mb_y * 8 * frame->linesize[2]) + mb_x * 8; yading@10: yading@10: ff_ea_idct_put_c(dest_y , linesize, block[0]); yading@10: ff_ea_idct_put_c(dest_y + 8, linesize, block[1]); yading@10: ff_ea_idct_put_c(dest_y + 8 * linesize , linesize, block[2]); yading@10: ff_ea_idct_put_c(dest_y + 8 * linesize + 8, linesize, block[3]); yading@10: if (!(s->avctx->flags & CODEC_FLAG_GRAY)) { yading@10: ff_ea_idct_put_c(dest_cb, frame->linesize[1], block[4]); yading@10: ff_ea_idct_put_c(dest_cr, frame->linesize[2], block[5]); yading@10: } yading@10: } yading@10: yading@10: static inline void tgq_dconly(TgqContext *s, unsigned char *dst, yading@10: int dst_stride, int dc) yading@10: { yading@10: int level = av_clip_uint8((dc*s->qtable[0] + 2056) >> 4); yading@10: int j; yading@10: for (j = 0; j < 8; j++) yading@10: memset(dst + j * dst_stride, level, 8); yading@10: } yading@10: yading@10: static void tgq_idct_put_mb_dconly(TgqContext *s, AVFrame *frame, yading@10: int mb_x, int mb_y, const int8_t *dc) yading@10: { yading@10: int linesize = frame->linesize[0]; yading@10: uint8_t *dest_y = frame->data[0] + (mb_y * 16 * linesize) + mb_x * 16; yading@10: uint8_t *dest_cb = frame->data[1] + (mb_y * 8 * frame->linesize[1]) + mb_x * 8; yading@10: uint8_t *dest_cr = frame->data[2] + (mb_y * 8 * frame->linesize[2]) + mb_x * 8; yading@10: tgq_dconly(s, dest_y, linesize, dc[0]); yading@10: tgq_dconly(s, dest_y + 8, linesize, dc[1]); yading@10: tgq_dconly(s, dest_y + 8 * linesize, linesize, dc[2]); yading@10: tgq_dconly(s, dest_y + 8 * linesize + 8, linesize, dc[3]); yading@10: if (!(s->avctx->flags & CODEC_FLAG_GRAY)) { yading@10: tgq_dconly(s, dest_cb, frame->linesize[1], dc[4]); yading@10: tgq_dconly(s, dest_cr, frame->linesize[2], dc[5]); yading@10: } yading@10: } yading@10: yading@10: static int tgq_decode_mb(TgqContext *s, AVFrame *frame, int mb_y, int mb_x) yading@10: { yading@10: int mode; yading@10: int i; yading@10: int8_t dc[6]; yading@10: yading@10: mode = bytestream2_get_byte(&s->gb); yading@10: if (mode > 12) { yading@10: GetBitContext gb; yading@10: init_get_bits(&gb, s->gb.buffer, FFMIN(bytestream2_get_bytes_left(&s->gb), mode) * 8); yading@10: for (i = 0; i < 6; i++) yading@10: tgq_decode_block(s, s->block[i], &gb); yading@10: tgq_idct_put_mb(s, s->block, frame, mb_x, mb_y); yading@10: bytestream2_skip(&s->gb, mode); yading@10: } else { yading@10: if (mode == 3) { yading@10: memset(dc, bytestream2_get_byte(&s->gb), 4); yading@10: dc[4] = bytestream2_get_byte(&s->gb); yading@10: dc[5] = bytestream2_get_byte(&s->gb); yading@10: } else if (mode == 6) { yading@10: bytestream2_get_buffer(&s->gb, dc, 6); yading@10: } else if (mode == 12) { yading@10: for (i = 0; i < 6; i++) { yading@10: dc[i] = bytestream2_get_byte(&s->gb); yading@10: bytestream2_skip(&s->gb, 1); yading@10: } yading@10: } else { yading@10: av_log(s->avctx, AV_LOG_ERROR, "unsupported mb mode %i\n", mode); yading@10: return -1; yading@10: } yading@10: tgq_idct_put_mb_dconly(s, frame, mb_x, mb_y, dc); yading@10: } yading@10: return 0; yading@10: } yading@10: yading@10: static void tgq_calculate_qtable(TgqContext *s, int quant) yading@10: { yading@10: int i, j; yading@10: const int a = (14 * (100 - quant)) / 100 + 1; yading@10: const int b = (11 * (100 - quant)) / 100 + 4; yading@10: for (j = 0; j < 8; j++) yading@10: for (i = 0; i < 8; i++) yading@10: s->qtable[j * 8 + i] = ((a * (j + i) / (7 + 7) + b) * yading@10: ff_inv_aanscales[j * 8 + i]) >> (14 - 4); yading@10: } yading@10: yading@10: static int tgq_decode_frame(AVCodecContext *avctx, yading@10: void *data, int *got_frame, yading@10: AVPacket *avpkt) yading@10: { yading@10: const uint8_t *buf = avpkt->data; yading@10: int buf_size = avpkt->size; yading@10: TgqContext *s = avctx->priv_data; yading@10: AVFrame *frame = data; yading@10: int x, y, ret; yading@10: int big_endian; yading@10: yading@10: if (buf_size < 16) { yading@10: av_log(avctx, AV_LOG_WARNING, "truncated header\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: big_endian = AV_RL32(&buf[4]) > 0x000FFFFF; yading@10: bytestream2_init(&s->gb, buf + 8, buf_size - 8); yading@10: if (big_endian) { yading@10: s->width = bytestream2_get_be16u(&s->gb); yading@10: s->height = bytestream2_get_be16u(&s->gb); yading@10: } else { yading@10: s->width = bytestream2_get_le16u(&s->gb); yading@10: s->height = bytestream2_get_le16u(&s->gb); yading@10: } yading@10: yading@10: if (s->avctx->width!=s->width || s->avctx->height!=s->height) { yading@10: avcodec_set_dimensions(s->avctx, s->width, s->height); yading@10: } yading@10: tgq_calculate_qtable(s, bytestream2_get_byteu(&s->gb)); yading@10: bytestream2_skip(&s->gb, 3); yading@10: yading@10: if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) yading@10: return ret; yading@10: frame->key_frame = 1; yading@10: frame->pict_type = AV_PICTURE_TYPE_I; yading@10: yading@10: for (y = 0; y < FFALIGN(avctx->height, 16) >> 4; y++) yading@10: for (x = 0; x < FFALIGN(avctx->width, 16) >> 4; x++) yading@10: if (tgq_decode_mb(s, frame, y, x) < 0) yading@10: return AVERROR_INVALIDDATA; yading@10: yading@10: *got_frame = 1; yading@10: yading@10: return avpkt->size; yading@10: } yading@10: yading@10: AVCodec ff_eatgq_decoder = { yading@10: .name = "eatgq", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_TGQ, yading@10: .priv_data_size = sizeof(TgqContext), yading@10: .init = tgq_decode_init, yading@10: .decode = tgq_decode_frame, yading@10: .capabilities = CODEC_CAP_DR1, yading@10: .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGQ video"), yading@10: };