mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2024-12-27 09:52:17 +00:00
4243da4ff4
This is possible, because every given FFCodec has to implement exactly one of these. Doing so decreases sizeof(FFCodec) and therefore decreases the size of the binary. Notice that in case of position-independent code the decrease is in .data.rel.ro, so that this translates to decreased memory consumption. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
358 lines
11 KiB
C
358 lines
11 KiB
C
/*
|
|
* Copyright (c) 2003 Michael Niedermayer
|
|
*
|
|
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* ASUS V1/V2 decoder.
|
|
*/
|
|
|
|
#include "libavutil/attributes.h"
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/thread.h"
|
|
|
|
#include "asv.h"
|
|
#include "avcodec.h"
|
|
#include "blockdsp.h"
|
|
#include "codec_internal.h"
|
|
#include "config_components.h"
|
|
#include "idctdsp.h"
|
|
#include "internal.h"
|
|
#include "mpeg12data.h"
|
|
|
|
#define CCP_VLC_BITS 5
|
|
#define DC_CCP_VLC_BITS 4
|
|
#define AC_CCP_VLC_BITS 6
|
|
#define ASV1_LEVEL_VLC_BITS 4
|
|
#define ASV2_LEVEL_VLC_BITS 10
|
|
|
|
static VLC ccp_vlc;
|
|
static VLC level_vlc;
|
|
static VLC dc_ccp_vlc;
|
|
static VLC ac_ccp_vlc;
|
|
static VLC asv2_level_vlc;
|
|
|
|
static av_cold void init_vlcs(void)
|
|
{
|
|
INIT_VLC_STATIC(&ccp_vlc, CCP_VLC_BITS, 17,
|
|
&ff_asv_ccp_tab[0][1], 2, 1,
|
|
&ff_asv_ccp_tab[0][0], 2, 1, 32);
|
|
INIT_LE_VLC_STATIC(&dc_ccp_vlc, DC_CCP_VLC_BITS, 8,
|
|
&ff_asv_dc_ccp_tab[0][1], 2, 1,
|
|
&ff_asv_dc_ccp_tab[0][0], 2, 1, 16);
|
|
INIT_LE_VLC_STATIC(&ac_ccp_vlc, AC_CCP_VLC_BITS, 16,
|
|
&ff_asv_ac_ccp_tab[0][1], 2, 1,
|
|
&ff_asv_ac_ccp_tab[0][0], 2, 1, 64);
|
|
INIT_VLC_STATIC(&level_vlc, ASV1_LEVEL_VLC_BITS, 7,
|
|
&ff_asv_level_tab[0][1], 2, 1,
|
|
&ff_asv_level_tab[0][0], 2, 1, 16);
|
|
INIT_LE_VLC_STATIC(&asv2_level_vlc, ASV2_LEVEL_VLC_BITS, 63,
|
|
&ff_asv2_level_tab[0][1], 4, 2,
|
|
&ff_asv2_level_tab[0][0], 4, 2, 1024);
|
|
}
|
|
|
|
static inline int asv1_get_level(GetBitContext *gb)
|
|
{
|
|
int code = get_vlc2(gb, level_vlc.table, ASV1_LEVEL_VLC_BITS, 1);
|
|
|
|
if (code == 3)
|
|
return get_sbits(gb, 8);
|
|
else
|
|
return code - 3;
|
|
}
|
|
|
|
// get_vlc2() is big-endian in this file
|
|
static inline int asv2_get_vlc2(GetBitContext *gb, VLC_TYPE (*table)[2], int bits)
|
|
{
|
|
unsigned int index;
|
|
int code, n;
|
|
|
|
OPEN_READER(re, gb);
|
|
UPDATE_CACHE_LE(re, gb);
|
|
|
|
index = SHOW_UBITS_LE(re, gb, bits);
|
|
code = table[index][0];
|
|
n = table[index][1];
|
|
LAST_SKIP_BITS(re, gb, n);
|
|
|
|
CLOSE_READER(re, gb);
|
|
|
|
return code;
|
|
}
|
|
|
|
static inline int asv2_get_level(GetBitContext *gb)
|
|
{
|
|
int code = asv2_get_vlc2(gb, asv2_level_vlc.table, ASV2_LEVEL_VLC_BITS);
|
|
|
|
if (code == 31)
|
|
return (int8_t) get_bits_le(gb, 8);
|
|
else
|
|
return code - 31;
|
|
}
|
|
|
|
static inline int asv1_decode_block(ASV1Context *a, int16_t block[64])
|
|
{
|
|
int i;
|
|
|
|
block[0] = 8 * get_bits(&a->gb, 8);
|
|
|
|
for (i = 0; i < 11; i++) {
|
|
const int ccp = get_vlc2(&a->gb, ccp_vlc.table, CCP_VLC_BITS, 1);
|
|
|
|
if (ccp) {
|
|
if (ccp == 16)
|
|
break;
|
|
if (ccp < 0 || i >= 10) {
|
|
av_log(a->avctx, AV_LOG_ERROR, "coded coeff pattern damaged\n");
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
if (ccp & 8)
|
|
block[a->scantable.permutated[4 * i + 0]] = (asv1_get_level(&a->gb) * a->intra_matrix[4 * i + 0]) >> 4;
|
|
if (ccp & 4)
|
|
block[a->scantable.permutated[4 * i + 1]] = (asv1_get_level(&a->gb) * a->intra_matrix[4 * i + 1]) >> 4;
|
|
if (ccp & 2)
|
|
block[a->scantable.permutated[4 * i + 2]] = (asv1_get_level(&a->gb) * a->intra_matrix[4 * i + 2]) >> 4;
|
|
if (ccp & 1)
|
|
block[a->scantable.permutated[4 * i + 3]] = (asv1_get_level(&a->gb) * a->intra_matrix[4 * i + 3]) >> 4;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int asv2_decode_block(ASV1Context *a, int16_t block[64])
|
|
{
|
|
int i, count, ccp;
|
|
|
|
count = get_bits_le(&a->gb, 4);
|
|
|
|
block[0] = 8 * get_bits_le(&a->gb, 8);
|
|
|
|
ccp = asv2_get_vlc2(&a->gb, dc_ccp_vlc.table, DC_CCP_VLC_BITS);
|
|
if (ccp) {
|
|
if (ccp & 4)
|
|
block[a->scantable.permutated[1]] = (asv2_get_level(&a->gb) * a->intra_matrix[1]) >> 4;
|
|
if (ccp & 2)
|
|
block[a->scantable.permutated[2]] = (asv2_get_level(&a->gb) * a->intra_matrix[2]) >> 4;
|
|
if (ccp & 1)
|
|
block[a->scantable.permutated[3]] = (asv2_get_level(&a->gb) * a->intra_matrix[3]) >> 4;
|
|
}
|
|
|
|
for (i = 1; i < count + 1; i++) {
|
|
const int ccp = asv2_get_vlc2(&a->gb, ac_ccp_vlc.table, AC_CCP_VLC_BITS);
|
|
|
|
if (ccp) {
|
|
if (ccp & 8)
|
|
block[a->scantable.permutated[4 * i + 0]] = (asv2_get_level(&a->gb) * a->intra_matrix[4 * i + 0]) >> 4;
|
|
if (ccp & 4)
|
|
block[a->scantable.permutated[4 * i + 1]] = (asv2_get_level(&a->gb) * a->intra_matrix[4 * i + 1]) >> 4;
|
|
if (ccp & 2)
|
|
block[a->scantable.permutated[4 * i + 2]] = (asv2_get_level(&a->gb) * a->intra_matrix[4 * i + 2]) >> 4;
|
|
if (ccp & 1)
|
|
block[a->scantable.permutated[4 * i + 3]] = (asv2_get_level(&a->gb) * a->intra_matrix[4 * i + 3]) >> 4;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int decode_mb(ASV1Context *a, int16_t block[6][64])
|
|
{
|
|
int i, ret;
|
|
|
|
a->bdsp.clear_blocks(block[0]);
|
|
|
|
if (a->avctx->codec_id == AV_CODEC_ID_ASV1) {
|
|
for (i = 0; i < 6; i++) {
|
|
if ((ret = asv1_decode_block(a, block[i])) < 0)
|
|
return ret;
|
|
}
|
|
} else {
|
|
for (i = 0; i < 6; i++) {
|
|
if ((ret = asv2_decode_block(a, block[i])) < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline void idct_put(ASV1Context *a, AVFrame *frame, int mb_x, int mb_y)
|
|
{
|
|
int16_t(*block)[64] = a->block;
|
|
int linesize = frame->linesize[0];
|
|
|
|
uint8_t *dest_y = frame->data[0] + (mb_y * 16 * linesize) + mb_x * 16;
|
|
uint8_t *dest_cb = frame->data[1] + (mb_y * 8 * frame->linesize[1]) + mb_x * 8;
|
|
uint8_t *dest_cr = frame->data[2] + (mb_y * 8 * frame->linesize[2]) + mb_x * 8;
|
|
|
|
a->idsp.idct_put(dest_y, linesize, block[0]);
|
|
a->idsp.idct_put(dest_y + 8, linesize, block[1]);
|
|
a->idsp.idct_put(dest_y + 8 * linesize, linesize, block[2]);
|
|
a->idsp.idct_put(dest_y + 8 * linesize + 8, linesize, block[3]);
|
|
|
|
if (!(a->avctx->flags & AV_CODEC_FLAG_GRAY)) {
|
|
a->idsp.idct_put(dest_cb, frame->linesize[1], block[4]);
|
|
a->idsp.idct_put(dest_cr, frame->linesize[2], block[5]);
|
|
}
|
|
}
|
|
|
|
static int decode_frame(AVCodecContext *avctx, AVFrame *p,
|
|
int *got_frame, AVPacket *avpkt)
|
|
{
|
|
ASV1Context *const a = avctx->priv_data;
|
|
const uint8_t *buf = avpkt->data;
|
|
int buf_size = avpkt->size;
|
|
int mb_x, mb_y, ret;
|
|
|
|
if (buf_size * 8LL < a->mb_height * a->mb_width * 13LL)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
|
|
return ret;
|
|
p->pict_type = AV_PICTURE_TYPE_I;
|
|
p->key_frame = 1;
|
|
|
|
if (avctx->codec_id == AV_CODEC_ID_ASV1) {
|
|
av_fast_padded_malloc(&a->bitstream_buffer, &a->bitstream_buffer_size,
|
|
buf_size);
|
|
if (!a->bitstream_buffer)
|
|
return AVERROR(ENOMEM);
|
|
|
|
a->bbdsp.bswap_buf((uint32_t *) a->bitstream_buffer,
|
|
(const uint32_t *) buf, buf_size / 4);
|
|
ret = init_get_bits8(&a->gb, a->bitstream_buffer, buf_size);
|
|
} else {
|
|
ret = init_get_bits8_le(&a->gb, buf, buf_size);
|
|
}
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for (mb_y = 0; mb_y < a->mb_height2; mb_y++) {
|
|
for (mb_x = 0; mb_x < a->mb_width2; mb_x++) {
|
|
if ((ret = decode_mb(a, a->block)) < 0)
|
|
return ret;
|
|
|
|
idct_put(a, p, mb_x, mb_y);
|
|
}
|
|
}
|
|
|
|
if (a->mb_width2 != a->mb_width) {
|
|
mb_x = a->mb_width2;
|
|
for (mb_y = 0; mb_y < a->mb_height2; mb_y++) {
|
|
if ((ret = decode_mb(a, a->block)) < 0)
|
|
return ret;
|
|
|
|
idct_put(a, p, mb_x, mb_y);
|
|
}
|
|
}
|
|
|
|
if (a->mb_height2 != a->mb_height) {
|
|
mb_y = a->mb_height2;
|
|
for (mb_x = 0; mb_x < a->mb_width; mb_x++) {
|
|
if ((ret = decode_mb(a, a->block)) < 0)
|
|
return ret;
|
|
|
|
idct_put(a, p, mb_x, mb_y);
|
|
}
|
|
}
|
|
|
|
*got_frame = 1;
|
|
|
|
emms_c();
|
|
|
|
return (get_bits_count(&a->gb) + 31) / 32 * 4;
|
|
}
|
|
|
|
static av_cold int decode_init(AVCodecContext *avctx)
|
|
{
|
|
static AVOnce init_static_once = AV_ONCE_INIT;
|
|
ASV1Context *const a = avctx->priv_data;
|
|
const int scale = avctx->codec_id == AV_CODEC_ID_ASV1 ? 1 : 2;
|
|
int i;
|
|
|
|
if (avctx->extradata_size < 1) {
|
|
av_log(avctx, AV_LOG_WARNING, "No extradata provided\n");
|
|
}
|
|
|
|
ff_asv_common_init(avctx);
|
|
ff_blockdsp_init(&a->bdsp, avctx);
|
|
ff_idctdsp_init(&a->idsp, avctx);
|
|
ff_init_scantable(a->idsp.idct_permutation, &a->scantable, ff_asv_scantab);
|
|
avctx->pix_fmt = AV_PIX_FMT_YUV420P;
|
|
|
|
if (avctx->extradata_size < 1 || (a->inv_qscale = avctx->extradata[0]) == 0) {
|
|
av_log(avctx, AV_LOG_ERROR, "illegal qscale 0\n");
|
|
if (avctx->codec_id == AV_CODEC_ID_ASV1)
|
|
a->inv_qscale = 6;
|
|
else
|
|
a->inv_qscale = 10;
|
|
}
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
int index = ff_asv_scantab[i];
|
|
|
|
a->intra_matrix[i] = 64 * scale * ff_mpeg1_default_intra_matrix[index] /
|
|
a->inv_qscale;
|
|
}
|
|
|
|
ff_thread_once(&init_static_once, init_vlcs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static av_cold int decode_end(AVCodecContext *avctx)
|
|
{
|
|
ASV1Context *const a = avctx->priv_data;
|
|
|
|
av_freep(&a->bitstream_buffer);
|
|
a->bitstream_buffer_size = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_ASV1_DECODER
|
|
const FFCodec ff_asv1_decoder = {
|
|
.p.name = "asv1",
|
|
.p.long_name = NULL_IF_CONFIG_SMALL("ASUS V1"),
|
|
.p.type = AVMEDIA_TYPE_VIDEO,
|
|
.p.id = AV_CODEC_ID_ASV1,
|
|
.priv_data_size = sizeof(ASV1Context),
|
|
.init = decode_init,
|
|
.close = decode_end,
|
|
FF_CODEC_DECODE_CB(decode_frame),
|
|
.p.capabilities = AV_CODEC_CAP_DR1,
|
|
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
|
|
};
|
|
#endif
|
|
|
|
#if CONFIG_ASV2_DECODER
|
|
const FFCodec ff_asv2_decoder = {
|
|
.p.name = "asv2",
|
|
.p.long_name = NULL_IF_CONFIG_SMALL("ASUS V2"),
|
|
.p.type = AVMEDIA_TYPE_VIDEO,
|
|
.p.id = AV_CODEC_ID_ASV2,
|
|
.priv_data_size = sizeof(ASV1Context),
|
|
.init = decode_init,
|
|
FF_CODEC_DECODE_CB(decode_frame),
|
|
.p.capabilities = AV_CODEC_CAP_DR1,
|
|
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
|
|
};
|
|
#endif
|