avcodec: LEAD MCMP decoder

Partially fixes ticket #798

Reviewed-by: James Almer <jamrial@gmail.com>
Reviewed-by: Michael Niedermayer <michael@niedermayer.cc>
Reviewed-by: Paul B Mahol <onemda@gmail.com>
Signed-off-by: Peter Ross <pross@xvid.org>
This commit is contained in:
Peter Ross 2022-10-14 13:19:39 +11:00
parent f084e9b0be
commit 10869cd849
11 changed files with 350 additions and 2 deletions

View File

@ -1,6 +1,9 @@
Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
- LEAD MCMP decoder
version 6.1:
- libaribcaption decoder
- Playdate video decoder and demuxer

1
configure vendored
View File

@ -2887,6 +2887,7 @@ ipu_decoder_select="mpegvideodec"
jpegls_decoder_select="mjpeg_decoder"
jv_decoder_select="blockdsp"
lagarith_decoder_select="llviddsp"
lead_decoder_select="idctdsp jpegtables"
ljpeg_encoder_select="jpegtables"
lscr_decoder_select="inflate_wrapper"
magicyuv_decoder_select="llviddsp"

View File

@ -997,6 +997,7 @@ following image formats are supported:
@item Lagarith @tab @tab X
@item LCL (LossLess Codec Library) MSZH @tab @tab X
@item LCL (LossLess Codec Library) ZLIB @tab E @tab E
@item LEAD MCMP @tab @tab X
@item LOCO @tab @tab X
@item LucasArts SANM/Smush @tab @tab X
@tab Used in LucasArts games / SMUSH animations.

View File

@ -479,6 +479,7 @@ OBJS-$(CONFIG_JV_DECODER) += jvdec.o
OBJS-$(CONFIG_KGV1_DECODER) += kgv1dec.o
OBJS-$(CONFIG_KMVC_DECODER) += kmvc.o
OBJS-$(CONFIG_LAGARITH_DECODER) += lagarith.o lagarithrac.o
OBJS-$(CONFIG_LEAD_DECODER) += leaddec.o jpegquanttables.o
OBJS-$(CONFIG_LJPEG_ENCODER) += ljpegenc.o mjpegenc_common.o
OBJS-$(CONFIG_LOCO_DECODER) += loco.o
OBJS-$(CONFIG_LSCR_DECODER) += lscrdec.o png.o pngdec.o pngdsp.o

View File

@ -188,6 +188,7 @@ extern const FFCodec ff_jv_decoder;
extern const FFCodec ff_kgv1_decoder;
extern const FFCodec ff_kmvc_decoder;
extern const FFCodec ff_lagarith_decoder;
extern const FFCodec ff_lead_decoder;
extern const FFCodec ff_ljpeg_encoder;
extern const FFCodec ff_loco_decoder;
extern const FFCodec ff_lscr_decoder;

View File

@ -1960,6 +1960,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
.long_name = NULL_IF_CONFIG_SMALL("vMix Video"),
.props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
},
{
.id = AV_CODEC_ID_LEAD,
.type = AVMEDIA_TYPE_VIDEO,
.name = "lead",
.long_name = NULL_IF_CONFIG_SMALL("LEAD MCMP"),
.props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
},
/* various PCM "codecs" */
{

View File

@ -324,6 +324,7 @@ enum AVCodecID {
AV_CODEC_ID_EVC,
AV_CODEC_ID_RTV1,
AV_CODEC_ID_VMIX,
AV_CODEC_ID_LEAD,
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs

62
libavcodec/leaddata.h Normal file
View File

@ -0,0 +1,62 @@
/*
* LEAD MCMP decoder tables
*
* 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
*/
#ifndef AVCODEC_LEADDATA_H
#define AVCODEC_LEADDATA_H
#include <stdint.h>
static const uint8_t luma_dc_len[]={
2, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9
};
static const uint8_t chroma_dc_len[]={
2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};
static const uint8_t luma_ac_len[]={
2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 7, 8,
8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11,
12, 12, 12, 12, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16
};
static const uint8_t chroma_ac_len[]={
2, 2, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
11, 11, 11, 11, 12, 12, 12, 12, 14, 15, 15, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16
};
#endif /* AVCODEC_LEADDATA_H */

270
libavcodec/leaddec.c Normal file
View File

@ -0,0 +1,270 @@
/*
* LEAD MCMP decoder
*
* Copyright (c) 2023 Peter Ross
*
* 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
*/
#include "avcodec.h"
#include "blockdsp.h"
#include "codec_internal.h"
#include "copy_block.h"
#include "decode.h"
#include "get_bits.h"
#include "idctdsp.h"
#include "jpegquanttables.h"
#include "jpegtables.h"
#include "leaddata.h"
#include "libavutil/mem_internal.h"
#include "libavutil/thread.h"
#define LUMA_DC_BITS 9
#define CHROMA_DC_BITS 11
#define LUMA_AC_BITS 10
#define CHROMA_AC_BITS 10
static VLC luma_dc_vlc;
static VLC chroma_dc_vlc;
static VLC luma_ac_vlc;
static VLC chroma_ac_vlc;
static av_cold void lead_init_static_data(void)
{
VLC_INIT_STATIC_FROM_LENGTHS(&luma_dc_vlc, LUMA_DC_BITS, FF_ARRAY_ELEMS(luma_dc_len),
luma_dc_len, 1,
0, 0, 0,
0, 0, 1 << LUMA_DC_BITS);
VLC_INIT_STATIC_FROM_LENGTHS(&chroma_dc_vlc, CHROMA_DC_BITS, FF_ARRAY_ELEMS(chroma_dc_len),
chroma_dc_len, 1,
0, 0, 0,
0, 0, 1 << CHROMA_DC_BITS);
VLC_INIT_STATIC_FROM_LENGTHS(&luma_ac_vlc, LUMA_AC_BITS, FF_ARRAY_ELEMS(luma_ac_len),
luma_ac_len, 1,
ff_mjpeg_val_ac_luminance, 1, 1,
0, 0, 1160);
VLC_INIT_STATIC_FROM_LENGTHS(&chroma_ac_vlc, CHROMA_AC_BITS, FF_ARRAY_ELEMS(chroma_ac_len),
chroma_ac_len, 1,
ff_mjpeg_val_ac_chrominance, 1, 1,
0, 0, 1160);
}
typedef struct LeadContext {
uint8_t *bitstream_buf;
unsigned int bitstream_buf_size;
BlockDSPContext bdsp;
IDCTDSPContext idsp;
uint8_t permutated_scantable[64];
} LeadContext;
static av_cold int lead_decode_init(AVCodecContext * avctx)
{
static AVOnce init_static_once = AV_ONCE_INIT;
LeadContext *s = avctx->priv_data;
if (avctx->extradata_size < 20)
return AVERROR_INVALIDDATA;
ff_blockdsp_init(&s->bdsp);
ff_idctdsp_init(&s->idsp, avctx);
ff_permute_scantable(s->permutated_scantable, ff_zigzag_direct, s->idsp.idct_permutation);
ff_thread_once(&init_static_once, lead_init_static_data);
return 0;
}
static void calc_dequant(uint16_t * dequant, const uint8_t * quant_tbl, int q)
{
for (int i = 0; i < 64; i++)
dequant[i] = av_clip(q * quant_tbl[ff_zigzag_direct[i]] / 50, 2, 32767);
}
static int decode_block(LeadContext * s, GetBitContext * gb,
const VLCElem * dc_table, int dc_bits, const VLCElem * ac_table, int ac_bits,
int16_t * dc_pred, const uint16_t * dequant,
uint8_t * dst, int stride)
{
DECLARE_ALIGNED(32, int16_t, block)[64];
int size;
s->bdsp.clear_block(block);
size = get_vlc2(gb, dc_table, dc_bits, 1);
if (size < 0)
return AVERROR_INVALIDDATA;
if (size)
*dc_pred += get_xbits(gb, size);
block[0] = (1 << 10) + *dc_pred * dequant[0];
for (int i = 1; i < 64; i++) {
int symbol = get_vlc2(gb, ac_table, ac_bits, 2);
if (symbol < 0)
return AVERROR_INVALIDDATA;
if (!symbol)
break;
i += symbol >> 4;
if (i >= 64)
return AVERROR_INVALIDDATA;
size = symbol & 0xF;
if (size)
block[s->permutated_scantable[i]] = get_xbits(gb, size) * dequant[i];
}
s->idsp.idct_put(dst, stride, block);
return 0;
}
static int lead_decode_frame(AVCodecContext *avctx, AVFrame * frame,
int * got_frame, AVPacket * avpkt)
{
LeadContext *s = avctx->priv_data;
const uint8_t * buf = avpkt->data;
int ret, format, yuv20p_half = 0, fields = 1, q, size;
GetBitContext gb;
int16_t dc_pred[3] = {0, 0, 0};
uint16_t dequant[2][64];
if (avpkt->size < 8)
return AVERROR_INVALIDDATA;
format = AV_RL16(buf + 4);
switch(format) {
case 0x8000:
yuv20p_half = 1;
// fall-through
case 0x1000:
avctx->pix_fmt = AV_PIX_FMT_YUV420P;
break;
case 0x2000:
avctx->pix_fmt = AV_PIX_FMT_YUV444P;
break;
case 0x2006:
avctx->pix_fmt = AV_PIX_FMT_YUV444P;
fields = 2;
break;
default:
avpriv_request_sample(avctx, "unsupported format 0x%x", format);
return AVERROR_PATCHWELCOME;
}
q = AV_RL16(buf + 6);
calc_dequant(dequant[0], ff_mjpeg_std_luminance_quant_tbl, q);
calc_dequant(dequant[1], ff_mjpeg_std_chrominance_quant_tbl, q);
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
return ret;
frame->flags |= AV_FRAME_FLAG_KEY;
frame->pict_type = AV_PICTURE_TYPE_I;
av_fast_padded_malloc(&s->bitstream_buf, &s->bitstream_buf_size, avpkt->size - 8);
if (!s->bitstream_buf)
return AVERROR(ENOMEM);
size = 0;
for (int i = 8; i < avpkt->size; i++) {
int src = buf[i] ^ 0x80;
s->bitstream_buf[size++] = src;
if (src == 0xFF && i + 1 < avpkt->size && (buf[i + 1] ^ 0x80) == 0x00)
i++;
}
init_get_bits8(&gb, s->bitstream_buf, size);
if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) {
for (int mb_y = 0; mb_y < avctx->height / 16; mb_y++)
for (int mb_x = 0; mb_x < avctx->width / 16; mb_x++)
for (int b = 0; b < (yuv20p_half ? 4 : 6); b++) {
int luma_block = yuv20p_half ? 2 : 4;
const VLCElem * dc_vlc = b < luma_block ? luma_dc_vlc.table : chroma_dc_vlc.table;
int dc_bits = b < luma_block ? LUMA_DC_BITS : CHROMA_DC_BITS;
const VLCElem * ac_vlc = b < luma_block ? luma_ac_vlc.table : chroma_ac_vlc.table;
int ac_bits = b < luma_block ? LUMA_AC_BITS : CHROMA_AC_BITS;
int plane = b < luma_block ? 0 : b - (yuv20p_half ? 1 : 3);
int x, y;
if (b < luma_block) {
y = 16*mb_y + 8*(b >> 1);
x = 16*mb_x + 8*(b & 1);
} else {
y = 8*mb_y;
x = 8*mb_x;
}
ret = decode_block(s, &gb, dc_vlc, dc_bits, ac_vlc, ac_bits,
dc_pred + plane, dequant[!(b < 4)],
frame->data[plane] + y*frame->linesize[plane] + x,
(yuv20p_half && b < 2 ? 2 : 1) * frame->linesize[plane]);
if (ret < 0)
return ret;
if (yuv20p_half && b < 2)
copy_block8(frame->data[plane] + (y + 1)*frame->linesize[plane] + x,
frame->data[plane] + y*frame->linesize[plane] + x,
2*frame->linesize[plane], 2*frame->linesize[plane], 8);
}
} else {
for (int f = 0; f < fields; f++)
for (int j = 0; j < avctx->height / fields / 8; j++)
for (int i = 0; i < avctx->width / 8; i++)
for (int plane = 0; plane < 3; plane++) {
const VLCElem * dc_vlc = !plane ? luma_dc_vlc.table : chroma_dc_vlc.table;
int dc_bits = !plane ? LUMA_DC_BITS : CHROMA_DC_BITS;
const VLCElem * ac_vlc = !plane ? luma_ac_vlc.table : chroma_ac_vlc.table;
int ac_bits = !plane ? LUMA_AC_BITS : CHROMA_AC_BITS;
ret = decode_block(s, &gb, dc_vlc, dc_bits, ac_vlc, ac_bits,
dc_pred + plane, dequant[!!plane],
frame->data[plane] + (f + 8*j*fields)*frame->linesize[plane] + 8*i,
fields * frame->linesize[plane]);
if (ret < 0)
return ret;
}
}
*got_frame = 1;
return avpkt->size;
}
static av_cold int lead_decode_end(AVCodecContext * avctx)
{
LeadContext *s = avctx->priv_data;
av_freep(&s->bitstream_buf);
return 0;
}
const FFCodec ff_lead_decoder = {
.p.name = "lead",
CODEC_LONG_NAME("LEAD MCMP"),
.p.type = AVMEDIA_TYPE_VIDEO,
.p.id = AV_CODEC_ID_LEAD,
.priv_data_size = sizeof(LeadContext),
.init = lead_decode_init,
.close = lead_decode_end,
FF_CODEC_DECODE_CB(lead_decode_frame),
.p.capabilities = AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
};

View File

@ -29,8 +29,8 @@
#include "version_major.h"
#define LIBAVCODEC_VERSION_MINOR 32
#define LIBAVCODEC_VERSION_MICRO 102
#define LIBAVCODEC_VERSION_MINOR 33
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \

View File

@ -505,6 +505,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_VQC, MKTAG('V', 'Q', 'C', '2') },
{ AV_CODEC_ID_RTV1, MKTAG('R', 'T', 'V', '1') },
{ AV_CODEC_ID_VMIX, MKTAG('V', 'M', 'X', '1') },
{ AV_CODEC_ID_LEAD, MKTAG('L', 'E', 'A', 'D') },
{ AV_CODEC_ID_NONE, 0 }
};