1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00
FFmpeg/libavcodec/mss3.c
Andreas Rheinhardt a247ac640d avcodec: Constify AVCodecs
Given that the AVCodec.next pointer has now been removed, most of the
AVCodecs are not modified at all any more and can therefore be made
const (as this patch does); the only exceptions are the very few codecs
for external libraries that have a init_static_data callback.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
Signed-off-by: James Almer <jamrial@gmail.com>
2021-04-27 10:43:15 -03:00

874 lines
23 KiB
C

/*
* Microsoft Screen 3 (aka Microsoft ATC Screen) decoder
* Copyright (c) 2012 Konstantin Shishkov
*
* 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
* Microsoft Screen 3 (aka Microsoft ATC Screen) decoder
*/
#include "avcodec.h"
#include "bytestream.h"
#include "internal.h"
#include "mathops.h"
#include "mss34dsp.h"
#define HEADER_SIZE 27
#define MODEL2_SCALE 13
#define MODEL_SCALE 15
#define MODEL256_SEC_SCALE 9
typedef struct Model2 {
int upd_val, till_rescale;
unsigned zero_freq, zero_weight;
unsigned total_freq, total_weight;
} Model2;
typedef struct Model {
int weights[16], freqs[16];
int num_syms;
int tot_weight;
int upd_val, max_upd_val, till_rescale;
} Model;
typedef struct Model256 {
int weights[256], freqs[256];
int tot_weight;
int secondary[68];
int sec_size;
int upd_val, max_upd_val, till_rescale;
} Model256;
#define RAC_BOTTOM 0x01000000
typedef struct RangeCoder {
const uint8_t *src, *src_end;
uint32_t range, low;
int got_error;
} RangeCoder;
enum BlockType {
FILL_BLOCK = 0,
IMAGE_BLOCK,
DCT_BLOCK,
HAAR_BLOCK,
SKIP_BLOCK
};
typedef struct BlockTypeContext {
int last_type;
Model bt_model[5];
} BlockTypeContext;
typedef struct FillBlockCoder {
int fill_val;
Model coef_model;
} FillBlockCoder;
typedef struct ImageBlockCoder {
Model256 esc_model, vec_entry_model;
Model vec_size_model;
Model vq_model[125];
} ImageBlockCoder;
typedef struct DCTBlockCoder {
int *prev_dc;
ptrdiff_t prev_dc_stride;
int prev_dc_height;
int quality;
uint16_t qmat[64];
Model dc_model;
Model2 sign_model;
Model256 ac_model;
} DCTBlockCoder;
typedef struct HaarBlockCoder {
int quality, scale;
Model256 coef_model;
Model coef_hi_model;
} HaarBlockCoder;
typedef struct MSS3Context {
AVCodecContext *avctx;
AVFrame *pic;
int got_error;
RangeCoder coder;
BlockTypeContext btype[3];
FillBlockCoder fill_coder[3];
ImageBlockCoder image_coder[3];
DCTBlockCoder dct_coder[3];
HaarBlockCoder haar_coder[3];
int dctblock[64];
int hblock[16 * 16];
} MSS3Context;
static void model2_reset(Model2 *m)
{
m->zero_weight = 1;
m->total_weight = 2;
m->zero_freq = 0x1000;
m->total_freq = 0x2000;
m->upd_val = 4;
m->till_rescale = 4;
}
static void model2_update(Model2 *m, int bit)
{
unsigned scale;
if (!bit)
m->zero_weight++;
m->till_rescale--;
if (m->till_rescale)
return;
m->total_weight += m->upd_val;
if (m->total_weight > 0x2000) {
m->total_weight = (m->total_weight + 1) >> 1;
m->zero_weight = (m->zero_weight + 1) >> 1;
if (m->total_weight == m->zero_weight)
m->total_weight = m->zero_weight + 1;
}
m->upd_val = m->upd_val * 5 >> 2;
if (m->upd_val > 64)
m->upd_val = 64;
scale = 0x80000000u / m->total_weight;
m->zero_freq = m->zero_weight * scale >> 18;
m->total_freq = m->total_weight * scale >> 18;
m->till_rescale = m->upd_val;
}
static void model_update(Model *m, int val)
{
int i, sum = 0;
unsigned scale;
m->weights[val]++;
m->till_rescale--;
if (m->till_rescale)
return;
m->tot_weight += m->upd_val;
if (m->tot_weight > 0x8000) {
m->tot_weight = 0;
for (i = 0; i < m->num_syms; i++) {
m->weights[i] = (m->weights[i] + 1) >> 1;
m->tot_weight += m->weights[i];
}
}
scale = 0x80000000u / m->tot_weight;
for (i = 0; i < m->num_syms; i++) {
m->freqs[i] = sum * scale >> 16;
sum += m->weights[i];
}
m->upd_val = m->upd_val * 5 >> 2;
if (m->upd_val > m->max_upd_val)
m->upd_val = m->max_upd_val;
m->till_rescale = m->upd_val;
}
static void model_reset(Model *m)
{
int i;
m->tot_weight = 0;
for (i = 0; i < m->num_syms - 1; i++)
m->weights[i] = 1;
m->weights[m->num_syms - 1] = 0;
m->upd_val = m->num_syms;
m->till_rescale = 1;
model_update(m, m->num_syms - 1);
m->till_rescale =
m->upd_val = (m->num_syms + 6) >> 1;
}
static av_cold void model_init(Model *m, int num_syms)
{
m->num_syms = num_syms;
m->max_upd_val = 8 * num_syms + 48;
model_reset(m);
}
static void model256_update(Model256 *m, int val)
{
int i, sum = 0;
unsigned scale;
int send, sidx = 1;
m->weights[val]++;
m->till_rescale--;
if (m->till_rescale)
return;
m->tot_weight += m->upd_val;
if (m->tot_weight > 0x8000) {
m->tot_weight = 0;
for (i = 0; i < 256; i++) {
m->weights[i] = (m->weights[i] + 1) >> 1;
m->tot_weight += m->weights[i];
}
}
scale = 0x80000000u / m->tot_weight;
m->secondary[0] = 0;
for (i = 0; i < 256; i++) {
m->freqs[i] = sum * scale >> 16;
sum += m->weights[i];
send = m->freqs[i] >> MODEL256_SEC_SCALE;
while (sidx <= send)
m->secondary[sidx++] = i - 1;
}
while (sidx < m->sec_size)
m->secondary[sidx++] = 255;
m->upd_val = m->upd_val * 5 >> 2;
if (m->upd_val > m->max_upd_val)
m->upd_val = m->max_upd_val;
m->till_rescale = m->upd_val;
}
static void model256_reset(Model256 *m)
{
int i;
for (i = 0; i < 255; i++)
m->weights[i] = 1;
m->weights[255] = 0;
m->tot_weight = 0;
m->upd_val = 256;
m->till_rescale = 1;
model256_update(m, 255);
m->till_rescale =
m->upd_val = (256 + 6) >> 1;
}
static av_cold void model256_init(Model256 *m)
{
m->max_upd_val = 8 * 256 + 48;
m->sec_size = (1 << 6) + 2;
model256_reset(m);
}
static void rac_init(RangeCoder *c, const uint8_t *src, int size)
{
int i;
c->src = src;
c->src_end = src + size;
c->low = 0;
for (i = 0; i < FFMIN(size, 4); i++)
c->low = (c->low << 8) | *c->src++;
c->range = 0xFFFFFFFF;
c->got_error = 0;
}
static void rac_normalise(RangeCoder *c)
{
for (;;) {
c->range <<= 8;
c->low <<= 8;
if (c->src < c->src_end) {
c->low |= *c->src++;
} else if (!c->low) {
c->got_error = 1;
c->low = 1;
}
if (c->low > c->range) {
c->got_error = 1;
c->low = 1;
}
if (c->range >= RAC_BOTTOM)
return;
}
}
static int rac_get_bit(RangeCoder *c)
{
int bit;
c->range >>= 1;
bit = (c->range <= c->low);
if (bit)
c->low -= c->range;
if (c->range < RAC_BOTTOM)
rac_normalise(c);
return bit;
}
static int rac_get_bits(RangeCoder *c, int nbits)
{
int val;
c->range >>= nbits;
val = c->low / c->range;
c->low -= c->range * val;
if (c->range < RAC_BOTTOM)
rac_normalise(c);
return val;
}
static int rac_get_model2_sym(RangeCoder *c, Model2 *m)
{
int bit, helper;
helper = m->zero_freq * (c->range >> MODEL2_SCALE);
bit = (c->low >= helper);
if (bit) {
c->low -= helper;
c->range -= helper;
} else {
c->range = helper;
}
if (c->range < RAC_BOTTOM)
rac_normalise(c);
model2_update(m, bit);
return bit;
}
static int rac_get_model_sym(RangeCoder *c, Model *m)
{
int val;
int end, end2;
unsigned prob, prob2, helper;
prob = 0;
prob2 = c->range;
c->range >>= MODEL_SCALE;
val = 0;
end = m->num_syms >> 1;
end2 = m->num_syms;
do {
helper = m->freqs[end] * c->range;
if (helper <= c->low) {
val = end;
prob = helper;
} else {
end2 = end;
prob2 = helper;
}
end = (end2 + val) >> 1;
} while (end != val);
c->low -= prob;
c->range = prob2 - prob;
if (c->range < RAC_BOTTOM)
rac_normalise(c);
model_update(m, val);
return val;
}
static int rac_get_model256_sym(RangeCoder *c, Model256 *m)
{
int val;
int start, end;
int ssym;
unsigned prob, prob2, helper;
prob2 = c->range;
c->range >>= MODEL_SCALE;
helper = c->low / c->range;
ssym = helper >> MODEL256_SEC_SCALE;
val = m->secondary[ssym];
end = start = m->secondary[ssym + 1] + 1;
while (end > val + 1) {
ssym = (end + val) >> 1;
if (m->freqs[ssym] <= helper) {
end = start;
val = ssym;
} else {
end = (end + val) >> 1;
start = ssym;
}
}
prob = m->freqs[val] * c->range;
if (val != 255)
prob2 = m->freqs[val + 1] * c->range;
c->low -= prob;
c->range = prob2 - prob;
if (c->range < RAC_BOTTOM)
rac_normalise(c);
model256_update(m, val);
return val;
}
static int decode_block_type(RangeCoder *c, BlockTypeContext *bt)
{
bt->last_type = rac_get_model_sym(c, &bt->bt_model[bt->last_type]);
return bt->last_type;
}
static int decode_coeff(RangeCoder *c, Model *m)
{
int val, sign;
val = rac_get_model_sym(c, m);
if (val) {
sign = rac_get_bit(c);
if (val > 1) {
val--;
val = (1 << val) + rac_get_bits(c, val);
}
if (!sign)
val = -val;
}
return val;
}
static void decode_fill_block(RangeCoder *c, FillBlockCoder *fc,
uint8_t *dst, ptrdiff_t stride, int block_size)
{
int i;
fc->fill_val += decode_coeff(c, &fc->coef_model);
for (i = 0; i < block_size; i++, dst += stride)
memset(dst, fc->fill_val, block_size);
}
static void decode_image_block(RangeCoder *c, ImageBlockCoder *ic,
uint8_t *dst, ptrdiff_t stride, int block_size)
{
int i, j;
int vec_size;
int vec[4];
int prev_line[16];
int A, B, C;
vec_size = rac_get_model_sym(c, &ic->vec_size_model) + 2;
for (i = 0; i < vec_size; i++)
vec[i] = rac_get_model256_sym(c, &ic->vec_entry_model);
for (; i < 4; i++)
vec[i] = 0;
memset(prev_line, 0, sizeof(prev_line));
for (j = 0; j < block_size; j++) {
A = 0;
B = 0;
for (i = 0; i < block_size; i++) {
C = B;
B = prev_line[i];
A = rac_get_model_sym(c, &ic->vq_model[A + B * 5 + C * 25]);
prev_line[i] = A;
if (A < 4)
dst[i] = vec[A];
else
dst[i] = rac_get_model256_sym(c, &ic->esc_model);
}
dst += stride;
}
}
static int decode_dct(RangeCoder *c, DCTBlockCoder *bc, int *block,
int bx, int by)
{
int skip, val, sign, pos = 1, zz_pos, dc;
int blk_pos = bx + by * bc->prev_dc_stride;
memset(block, 0, sizeof(*block) * 64);
dc = decode_coeff(c, &bc->dc_model);
if (by) {
if (bx) {
int l, tl, t;
l = bc->prev_dc[blk_pos - 1];
tl = bc->prev_dc[blk_pos - 1 - bc->prev_dc_stride];
t = bc->prev_dc[blk_pos - bc->prev_dc_stride];
if (FFABS(t - tl) <= FFABS(l - tl))
dc += l;
else
dc += t;
} else {
dc += bc->prev_dc[blk_pos - bc->prev_dc_stride];
}
} else if (bx) {
dc += bc->prev_dc[bx - 1];
}
bc->prev_dc[blk_pos] = dc;
block[0] = dc * bc->qmat[0];
while (pos < 64) {
val = rac_get_model256_sym(c, &bc->ac_model);
if (!val)
return 0;
if (val == 0xF0) {
pos += 16;
continue;
}
skip = val >> 4;
val = val & 0xF;
if (!val)
return -1;
pos += skip;
if (pos >= 64)
return -1;
sign = rac_get_model2_sym(c, &bc->sign_model);
if (val > 1) {
val--;
val = (1 << val) + rac_get_bits(c, val);
}
if (!sign)
val = -val;
zz_pos = ff_zigzag_direct[pos];
block[zz_pos] = val * bc->qmat[zz_pos];
pos++;
}
return pos == 64 ? 0 : -1;
}
static void decode_dct_block(RangeCoder *c, DCTBlockCoder *bc,
uint8_t *dst, ptrdiff_t stride, int block_size,
int *block, int mb_x, int mb_y)
{
int i, j;
int bx, by;
int nblocks = block_size >> 3;
bx = mb_x * nblocks;
by = mb_y * nblocks;
for (j = 0; j < nblocks; j++) {
for (i = 0; i < nblocks; i++) {
if (decode_dct(c, bc, block, bx + i, by + j)) {
c->got_error = 1;
return;
}
ff_mss34_dct_put(dst + i * 8, stride, block);
}
dst += 8 * stride;
}
}
static void decode_haar_block(RangeCoder *c, HaarBlockCoder *hc,
uint8_t *dst, ptrdiff_t stride,
int block_size, int *block)
{
const int hsize = block_size >> 1;
int A, B, C, D, t1, t2, t3, t4;
int i, j;
for (j = 0; j < block_size; j++) {
for (i = 0; i < block_size; i++) {
if (i < hsize && j < hsize)
block[i] = rac_get_model256_sym(c, &hc->coef_model);
else
block[i] = decode_coeff(c, &hc->coef_hi_model);
block[i] *= hc->scale;
}
block += block_size;
}
block -= block_size * block_size;
for (j = 0; j < hsize; j++) {
for (i = 0; i < hsize; i++) {
A = block[i];
B = block[i + hsize];
C = block[i + hsize * block_size];
D = block[i + hsize * block_size + hsize];
t1 = A - B;
t2 = C - D;
t3 = A + B;
t4 = C + D;
dst[i * 2] = av_clip_uint8(t1 - t2);
dst[i * 2 + stride] = av_clip_uint8(t1 + t2);
dst[i * 2 + 1] = av_clip_uint8(t3 - t4);
dst[i * 2 + 1 + stride] = av_clip_uint8(t3 + t4);
}
block += block_size;
dst += stride * 2;
}
}
static void reset_coders(MSS3Context *ctx, int quality)
{
int i, j;
for (i = 0; i < 3; i++) {
ctx->btype[i].last_type = SKIP_BLOCK;
for (j = 0; j < 5; j++)
model_reset(&ctx->btype[i].bt_model[j]);
ctx->fill_coder[i].fill_val = 0;
model_reset(&ctx->fill_coder[i].coef_model);
model256_reset(&ctx->image_coder[i].esc_model);
model256_reset(&ctx->image_coder[i].vec_entry_model);
model_reset(&ctx->image_coder[i].vec_size_model);
for (j = 0; j < 125; j++)
model_reset(&ctx->image_coder[i].vq_model[j]);
if (ctx->dct_coder[i].quality != quality) {
ctx->dct_coder[i].quality = quality;
ff_mss34_gen_quant_mat(ctx->dct_coder[i].qmat, quality, !i);
}
memset(ctx->dct_coder[i].prev_dc, 0,
sizeof(*ctx->dct_coder[i].prev_dc) *
ctx->dct_coder[i].prev_dc_stride *
ctx->dct_coder[i].prev_dc_height);
model_reset(&ctx->dct_coder[i].dc_model);
model2_reset(&ctx->dct_coder[i].sign_model);
model256_reset(&ctx->dct_coder[i].ac_model);
if (ctx->haar_coder[i].quality != quality) {
ctx->haar_coder[i].quality = quality;
ctx->haar_coder[i].scale = 17 - 7 * quality / 50;
}
model_reset(&ctx->haar_coder[i].coef_hi_model);
model256_reset(&ctx->haar_coder[i].coef_model);
}
}
static av_cold void init_coders(MSS3Context *ctx)
{
int i, j;
for (i = 0; i < 3; i++) {
for (j = 0; j < 5; j++)
model_init(&ctx->btype[i].bt_model[j], 5);
model_init(&ctx->fill_coder[i].coef_model, 12);
model256_init(&ctx->image_coder[i].esc_model);
model256_init(&ctx->image_coder[i].vec_entry_model);
model_init(&ctx->image_coder[i].vec_size_model, 3);
for (j = 0; j < 125; j++)
model_init(&ctx->image_coder[i].vq_model[j], 5);
model_init(&ctx->dct_coder[i].dc_model, 12);
model256_init(&ctx->dct_coder[i].ac_model);
model_init(&ctx->haar_coder[i].coef_hi_model, 12);
model256_init(&ctx->haar_coder[i].coef_model);
}
}
static int mss3_decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
AVPacket *avpkt)
{
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size;
MSS3Context *c = avctx->priv_data;
RangeCoder *acoder = &c->coder;
GetByteContext gb;
uint8_t *dst[3];
int dec_width, dec_height, dec_x, dec_y, quality, keyframe;
int x, y, i, mb_width, mb_height, blk_size, btype;
int ret;
if (buf_size < HEADER_SIZE) {
av_log(avctx, AV_LOG_ERROR,
"Frame should have at least %d bytes, got %d instead\n",
HEADER_SIZE, buf_size);
return AVERROR_INVALIDDATA;
}
bytestream2_init(&gb, buf, buf_size);
keyframe = bytestream2_get_be32(&gb);
if (keyframe & ~0x301) {
av_log(avctx, AV_LOG_ERROR, "Invalid frame type %X\n", keyframe);
return AVERROR_INVALIDDATA;
}
keyframe = !(keyframe & 1);
bytestream2_skip(&gb, 6);
dec_x = bytestream2_get_be16(&gb);
dec_y = bytestream2_get_be16(&gb);
dec_width = bytestream2_get_be16(&gb);
dec_height = bytestream2_get_be16(&gb);
if (dec_x + dec_width > avctx->width ||
dec_y + dec_height > avctx->height ||
(dec_width | dec_height) & 0xF) {
av_log(avctx, AV_LOG_ERROR, "Invalid frame dimensions %dx%d +%d,%d\n",
dec_width, dec_height, dec_x, dec_y);
return AVERROR_INVALIDDATA;
}
bytestream2_skip(&gb, 4);
quality = bytestream2_get_byte(&gb);
if (quality < 1 || quality > 100) {
av_log(avctx, AV_LOG_ERROR, "Invalid quality setting %d\n", quality);
return AVERROR_INVALIDDATA;
}
bytestream2_skip(&gb, 4);
if (keyframe && !bytestream2_get_bytes_left(&gb)) {
av_log(avctx, AV_LOG_ERROR, "Keyframe without data found\n");
return AVERROR_INVALIDDATA;
}
if (!keyframe && c->got_error)
return buf_size;
c->got_error = 0;
if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0)
return ret;
c->pic->key_frame = keyframe;
c->pic->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
if (!bytestream2_get_bytes_left(&gb)) {
if ((ret = av_frame_ref(data, c->pic)) < 0)
return ret;
*got_frame = 1;
return buf_size;
}
reset_coders(c, quality);
rac_init(acoder, buf + HEADER_SIZE, buf_size - HEADER_SIZE);
mb_width = dec_width >> 4;
mb_height = dec_height >> 4;
dst[0] = c->pic->data[0] + dec_x + dec_y * c->pic->linesize[0];
dst[1] = c->pic->data[1] + dec_x / 2 + (dec_y / 2) * c->pic->linesize[1];
dst[2] = c->pic->data[2] + dec_x / 2 + (dec_y / 2) * c->pic->linesize[2];
for (y = 0; y < mb_height; y++) {
for (x = 0; x < mb_width; x++) {
for (i = 0; i < 3; i++) {
blk_size = 8 << !i;
btype = decode_block_type(acoder, c->btype + i);
switch (btype) {
case FILL_BLOCK:
decode_fill_block(acoder, c->fill_coder + i,
dst[i] + x * blk_size,
c->pic->linesize[i], blk_size);
break;
case IMAGE_BLOCK:
decode_image_block(acoder, c->image_coder + i,
dst[i] + x * blk_size,
c->pic->linesize[i], blk_size);
break;
case DCT_BLOCK:
decode_dct_block(acoder, c->dct_coder + i,
dst[i] + x * blk_size,
c->pic->linesize[i], blk_size,
c->dctblock, x, y);
break;
case HAAR_BLOCK:
decode_haar_block(acoder, c->haar_coder + i,
dst[i] + x * blk_size,
c->pic->linesize[i], blk_size,
c->hblock);
break;
}
if (c->got_error || acoder->got_error) {
av_log(avctx, AV_LOG_ERROR, "Error decoding block %d,%d\n",
x, y);
c->got_error = 1;
return AVERROR_INVALIDDATA;
}
}
}
dst[0] += c->pic->linesize[0] * 16;
dst[1] += c->pic->linesize[1] * 8;
dst[2] += c->pic->linesize[2] * 8;
}
if ((ret = av_frame_ref(data, c->pic)) < 0)
return ret;
*got_frame = 1;
return buf_size;
}
static av_cold int mss3_decode_end(AVCodecContext *avctx)
{
MSS3Context * const c = avctx->priv_data;
int i;
av_frame_free(&c->pic);
for (i = 0; i < 3; i++)
av_freep(&c->dct_coder[i].prev_dc);
return 0;
}
static av_cold int mss3_decode_init(AVCodecContext *avctx)
{
MSS3Context * const c = avctx->priv_data;
int i;
c->avctx = avctx;
if ((avctx->width & 0xF) || (avctx->height & 0xF)) {
av_log(avctx, AV_LOG_ERROR,
"Image dimensions should be a multiple of 16.\n");
return AVERROR_INVALIDDATA;
}
c->got_error = 0;
for (i = 0; i < 3; i++) {
int b_width = avctx->width >> (2 + !!i);
int b_height = avctx->height >> (2 + !!i);
c->dct_coder[i].prev_dc_stride = b_width;
c->dct_coder[i].prev_dc_height = b_height;
c->dct_coder[i].prev_dc = av_malloc(sizeof(*c->dct_coder[i].prev_dc) *
b_width * b_height);
if (!c->dct_coder[i].prev_dc) {
av_log(avctx, AV_LOG_ERROR, "Cannot allocate buffer\n");
return AVERROR(ENOMEM);
}
}
c->pic = av_frame_alloc();
if (!c->pic)
return AVERROR(ENOMEM);
avctx->pix_fmt = AV_PIX_FMT_YUV420P;
init_coders(c);
return 0;
}
const AVCodec ff_msa1_decoder = {
.name = "msa1",
.long_name = NULL_IF_CONFIG_SMALL("MS ATC Screen"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_MSA1,
.priv_data_size = sizeof(MSS3Context),
.init = mss3_decode_init,
.close = mss3_decode_end,
.decode = mss3_decode_frame,
.capabilities = AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
};