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, Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest. releases are sorted from youngest to oldest.
version <next>:
- LEAD MCMP decoder
version 6.1: version 6.1:
- libaribcaption decoder - libaribcaption decoder
- Playdate video decoder and demuxer - Playdate video decoder and demuxer

1
configure vendored
View File

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

View File

@ -997,6 +997,7 @@ following image formats are supported:
@item Lagarith @tab @tab X @item Lagarith @tab @tab X
@item LCL (LossLess Codec Library) MSZH @tab @tab X @item LCL (LossLess Codec Library) MSZH @tab @tab X
@item LCL (LossLess Codec Library) ZLIB @tab E @tab E @item LCL (LossLess Codec Library) ZLIB @tab E @tab E
@item LEAD MCMP @tab @tab X
@item LOCO @tab @tab X @item LOCO @tab @tab X
@item LucasArts SANM/Smush @tab @tab X @item LucasArts SANM/Smush @tab @tab X
@tab Used in LucasArts games / SMUSH animations. @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_KGV1_DECODER) += kgv1dec.o
OBJS-$(CONFIG_KMVC_DECODER) += kmvc.o OBJS-$(CONFIG_KMVC_DECODER) += kmvc.o
OBJS-$(CONFIG_LAGARITH_DECODER) += lagarith.o lagarithrac.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_LJPEG_ENCODER) += ljpegenc.o mjpegenc_common.o
OBJS-$(CONFIG_LOCO_DECODER) += loco.o OBJS-$(CONFIG_LOCO_DECODER) += loco.o
OBJS-$(CONFIG_LSCR_DECODER) += lscrdec.o png.o pngdec.o pngdsp.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_kgv1_decoder;
extern const FFCodec ff_kmvc_decoder; extern const FFCodec ff_kmvc_decoder;
extern const FFCodec ff_lagarith_decoder; extern const FFCodec ff_lagarith_decoder;
extern const FFCodec ff_lead_decoder;
extern const FFCodec ff_ljpeg_encoder; extern const FFCodec ff_ljpeg_encoder;
extern const FFCodec ff_loco_decoder; extern const FFCodec ff_loco_decoder;
extern const FFCodec ff_lscr_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"), .long_name = NULL_IF_CONFIG_SMALL("vMix Video"),
.props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, .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" */ /* various PCM "codecs" */
{ {

View File

@ -324,6 +324,7 @@ enum AVCodecID {
AV_CODEC_ID_EVC, AV_CODEC_ID_EVC,
AV_CODEC_ID_RTV1, AV_CODEC_ID_RTV1,
AV_CODEC_ID_VMIX, AV_CODEC_ID_VMIX,
AV_CODEC_ID_LEAD,
/* various PCM "codecs" */ /* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio 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" #include "version_major.h"
#define LIBAVCODEC_VERSION_MINOR 32 #define LIBAVCODEC_VERSION_MINOR 33
#define LIBAVCODEC_VERSION_MICRO 102 #define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \ 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_VQC, MKTAG('V', 'Q', 'C', '2') },
{ AV_CODEC_ID_RTV1, MKTAG('R', 'T', 'V', '1') }, { AV_CODEC_ID_RTV1, MKTAG('R', 'T', 'V', '1') },
{ AV_CODEC_ID_VMIX, MKTAG('V', 'M', 'X', '1') }, { AV_CODEC_ID_VMIX, MKTAG('V', 'M', 'X', '1') },
{ AV_CODEC_ID_LEAD, MKTAG('L', 'E', 'A', 'D') },
{ AV_CODEC_ID_NONE, 0 } { AV_CODEC_ID_NONE, 0 }
}; };