FFmpeg/libavcodec/clearvideo.c
Andreas Rheinhardt 790f793844 avutil/common: Don't auto-include mem.h
There are lots of files that don't need it: The number of object
files that actually need it went down from 2011 to 884 here.

Keep it for external users in order to not cause breakages.

Also improve the other headers a bit while just at it.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2024-03-31 00:08:43 +01:00

752 lines
23 KiB
C

/*
* ClearVideo decoder
* Copyright (c) 2012-2018 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
* ClearVideo decoder
*/
#include "libavutil/mem.h"
#include "libavutil/mem_internal.h"
#include "libavutil/thread.h"
#include "avcodec.h"
#include "bytestream.h"
#include "codec_internal.h"
#include "decode.h"
#include "get_bits.h"
#include "idctdsp.h"
#include "mathops.h"
#include "clearvideodata.h"
#define CLV_VLC_BITS 9
typedef struct LevelCodes {
const VLCElem *flags_cb;
const VLCElem *mv_cb;
const VLCElem *bias_cb;
} LevelCodes;
typedef struct MV {
int16_t x, y;
} MV;
static const MV zero_mv = { 0 };
typedef struct MVInfo {
int mb_w;
int mb_h;
int mb_size;
int mb_stride;
int top;
MV *mv;
} MVInfo;
typedef struct CLVContext {
AVCodecContext *avctx;
IDCTDSPContext idsp;
AVFrame *pic;
AVFrame *prev;
GetBitContext gb;
int mb_width, mb_height;
int pmb_width, pmb_height;
MVInfo mvi;
int tile_size;
int tile_shift;
int luma_dc_quant, chroma_dc_quant, ac_quant;
DECLARE_ALIGNED(16, int16_t, block)[64];
int top_dc[3], left_dc[4];
} CLVContext;
static VLCElem dc_vlc[1104], ac_vlc[554];
static LevelCodes lev[4 + 3 + 3]; // 0..3: Y, 4..6: U, 7..9: V
static inline int decode_block(CLVContext *ctx, int16_t *blk, int has_ac,
int ac_quant)
{
GetBitContext *gb = &ctx->gb;
int idx = 1, last = 0, val, skip;
memset(blk, 0, sizeof(*blk) * 64);
blk[0] = get_vlc2(gb, dc_vlc, CLV_VLC_BITS, 3);
if (!has_ac)
return 0;
while (idx < 64 && !last) {
val = get_vlc2(gb, ac_vlc, CLV_VLC_BITS, 2);
if (val < 0)
return AVERROR_INVALIDDATA;
if (val != 0x1BFF) {
last = val >> 12;
skip = (val >> 4) & 0xFF;
val &= 0xF;
if (get_bits1(gb))
val = -val;
} else {
last = get_bits1(gb);
skip = get_bits(gb, 6);
val = get_sbits(gb, 8);
}
if (val) {
int aval = FFABS(val), sign = val < 0;
val = ac_quant * (2 * aval + 1);
if (!(ac_quant & 1))
val--;
if (sign)
val = -val;
}
idx += skip;
if (idx >= 64)
return AVERROR_INVALIDDATA;
blk[ff_zigzag_direct[idx++]] = val;
}
return (idx <= 64 && last) ? 0 : -1;
}
#define DCT_TEMPLATE(blk, step, bias, shift, dshift, OP) \
const int t0 = OP(2841 * blk[1 * step] + 565 * blk[7 * step]); \
const int t1 = OP( 565 * blk[1 * step] - 2841 * blk[7 * step]); \
const int t2 = OP(1609 * blk[5 * step] + 2408 * blk[3 * step]); \
const int t3 = OP(2408 * blk[5 * step] - 1609 * blk[3 * step]); \
const int t4 = OP(1108 * blk[2 * step] - 2676 * blk[6 * step]); \
const int t5 = OP(2676 * blk[2 * step] + 1108 * blk[6 * step]); \
const int t6 = ((blk[0 * step] + blk[4 * step]) * (1 << dshift)) + bias; \
const int t7 = ((blk[0 * step] - blk[4 * step]) * (1 << dshift)) + bias; \
const int t8 = t0 + t2; \
const int t9 = t0 - t2; \
const int tA = (int)(181U * (t9 + (t1 - t3)) + 0x80) >> 8; \
const int tB = (int)(181U * (t9 - (t1 - t3)) + 0x80) >> 8; \
const int tC = t1 + t3; \
\
blk[0 * step] = (t6 + t5 + t8) >> shift; \
blk[1 * step] = (t7 + t4 + tA) >> shift; \
blk[2 * step] = (t7 - t4 + tB) >> shift; \
blk[3 * step] = (t6 - t5 + tC) >> shift; \
blk[4 * step] = (t6 - t5 - tC) >> shift; \
blk[5 * step] = (t7 - t4 - tB) >> shift; \
blk[6 * step] = (t7 + t4 - tA) >> shift; \
blk[7 * step] = (t6 + t5 - t8) >> shift; \
#define ROP(x) x
#define COP(x) (((x) + 4) >> 3)
static void clv_dct(int16_t *block)
{
int i;
int16_t *ptr;
ptr = block;
for (i = 0; i < 8; i++) {
DCT_TEMPLATE(ptr, 1, 0x80, 8, 11, ROP);
ptr += 8;
}
ptr = block;
for (i = 0; i < 8; i++) {
DCT_TEMPLATE(ptr, 8, 0x2000, 14, 8, COP);
ptr++;
}
}
static int decode_mb(CLVContext *c, int x, int y)
{
int i, has_ac[6], off;
for (i = 0; i < 6; i++)
has_ac[i] = get_bits1(&c->gb);
off = x * 16 + y * 16 * c->pic->linesize[0];
for (i = 0; i < 4; i++) {
if (decode_block(c, c->block, has_ac[i], c->ac_quant) < 0)
return AVERROR_INVALIDDATA;
if (!x && !(i & 1)) {
c->block[0] += c->top_dc[0];
c->top_dc[0] = c->block[0];
} else {
c->block[0] += c->left_dc[(i & 2) >> 1];
}
c->left_dc[(i & 2) >> 1] = c->block[0];
c->block[0] *= c->luma_dc_quant;
clv_dct(c->block);
if (i == 2)
off += c->pic->linesize[0] * 8;
c->idsp.put_pixels_clamped(c->block,
c->pic->data[0] + off + (i & 1) * 8,
c->pic->linesize[0]);
}
off = x * 8 + y * 8 * c->pic->linesize[1];
for (i = 1; i < 3; i++) {
if (decode_block(c, c->block, has_ac[i + 3], c->ac_quant) < 0)
return AVERROR_INVALIDDATA;
if (!x) {
c->block[0] += c->top_dc[i];
c->top_dc[i] = c->block[0];
} else {
c->block[0] += c->left_dc[i + 1];
}
c->left_dc[i + 1] = c->block[0];
c->block[0] *= c->chroma_dc_quant;
clv_dct(c->block);
c->idsp.put_pixels_clamped(c->block, c->pic->data[i] + off,
c->pic->linesize[i]);
}
return 0;
}
static int copy_block(AVCodecContext *avctx, AVFrame *dst, const AVFrame *src,
int plane, int x, int y, int dx, int dy, int size)
{
int shift = plane > 0;
int sx = x + dx;
int sy = y + dy;
int sstride, dstride, soff, doff;
uint8_t *dbuf;
const uint8_t *sbuf;
int i;
if (x < 0 || sx < 0 || y < 0 || sy < 0 ||
x + size > avctx->coded_width >> shift ||
y + size > avctx->coded_height >> shift ||
sx + size > avctx->coded_width >> shift ||
sy + size > avctx->coded_height >> shift)
return AVERROR_INVALIDDATA;
sstride = src->linesize[plane];
dstride = dst->linesize[plane];
soff = sx + sy * sstride;
sbuf = src->data[plane];
doff = x + y * dstride;
dbuf = dst->data[plane];
for (i = 0; i < size; i++) {
uint8_t *dptr = &dbuf[doff];
const uint8_t *sptr = &sbuf[soff];
memcpy(dptr, sptr, size);
doff += dstride;
soff += sstride;
}
return 0;
}
static int copyadd_block(AVCodecContext *avctx, AVFrame *dst, const AVFrame *src,
int plane, int x, int y, int dx, int dy, int size, int bias)
{
int shift = plane > 0;
int sx = x + dx;
int sy = y + dy;
int sstride = src->linesize[plane];
int dstride = dst->linesize[plane];
int soff = sx + sy * sstride;
const uint8_t *sbuf = src->data[plane];
int doff = x + y * dstride;
uint8_t *dbuf = dst->data[plane];
int i, j;
if (x < 0 || sx < 0 || y < 0 || sy < 0 ||
x + size > avctx->coded_width >> shift ||
y + size > avctx->coded_height >> shift ||
sx + size > avctx->coded_width >> shift ||
sy + size > avctx->coded_height >> shift)
return AVERROR_INVALIDDATA;
for (j = 0; j < size; j++) {
uint8_t *dptr = &dbuf[doff];
const uint8_t *sptr = &sbuf[soff];
for (i = 0; i < size; i++) {
int val = sptr[i] + bias;
dptr[i] = av_clip_uint8(val);
}
doff += dstride;
soff += sstride;
}
return 0;
}
static MV *mvi_predict(MVInfo *mvi, int mb_x, int mb_y)
{
MV res, pred_mv;
int left_mv, right_mv, top_mv, bot_mv;
if (mvi->top) {
if (mb_x > 0) {
pred_mv = mvi->mv[mvi->mb_stride + mb_x - 1];
} else {
pred_mv = zero_mv;
}
} else if ((mb_x == 0) || (mb_x == mvi->mb_w - 1)) {
pred_mv = mvi->mv[mb_x];
} else {
MV A = mvi->mv[mvi->mb_stride + mb_x - 1];
MV B = mvi->mv[ mb_x ];
MV C = mvi->mv[ mb_x + 1];
pred_mv.x = mid_pred(A.x, B.x, C.x);
pred_mv.y = mid_pred(A.y, B.y, C.y);
}
res = pred_mv;
left_mv = -((mb_x * mvi->mb_size));
right_mv = ((mvi->mb_w - mb_x - 1) * mvi->mb_size);
if (res.x < left_mv) {
res.x = left_mv;
}
if (res.x > right_mv) {
res.x = right_mv;
}
top_mv = -((mb_y * mvi->mb_size));
bot_mv = ((mvi->mb_h - mb_y - 1) * mvi->mb_size);
if (res.y < top_mv) {
res.y = top_mv;
}
if (res.y > bot_mv) {
res.y = bot_mv;
}
mvi->mv[mvi->mb_stride + mb_x].x = res.x;
mvi->mv[mvi->mb_stride + mb_x].y = res.y;
return &mvi->mv[mvi->mb_stride + mb_x];
}
static void mvi_update_prediction(MV *mv, MV diff)
{
mv->x += diff.x;
mv->y += diff.y;
}
static void mvi_reset(MVInfo *mvi, int mb_w, int mb_h, int mb_size)
{
mvi->top = 1;
mvi->mb_w = mb_w;
mvi->mb_h = mb_h;
mvi->mb_size = mb_size;
mvi->mb_stride = mb_w;
memset(mvi->mv, 0, sizeof(MV) * mvi->mb_stride * 2);
}
static void mvi_update_row(MVInfo *mvi)
{
int i;
mvi->top = 0;
for (i = 0 ; i < mvi->mb_stride; i++) {
mvi->mv[i] = mvi->mv[mvi->mb_stride + i];
}
}
static int tile_do_block(AVCodecContext *avctx, AVFrame *dst, const AVFrame *src,
int plane, int x, int y, int dx, int dy, int size, int bias)
{
int ret;
if (!bias) {
ret = copy_block(avctx, dst, src, plane, x, y, dx, dy, size);
} else {
ret = copyadd_block(avctx, dst, src, plane, x, y, dx, dy, size, bias);
}
return ret;
}
static int decode_tile(AVCodecContext *avctx, GetBitContext *gb,
const LevelCodes *lc,
AVFrame *dst, const AVFrame *src,
int plane, int x, int y, int size,
MV root_mv, MV *pred)
{
int i, flags = 0;
int16_t bias = 0;
MV mv = { 0 };
int err;
if (lc->flags_cb)
flags = get_vlc2(gb, lc->flags_cb, CLV_VLC_BITS, 2);
if (lc->mv_cb) {
uint16_t mv_code = get_vlc2(gb, lc->mv_cb, CLV_VLC_BITS, 2);
if (mv_code != MV_ESC) {
mv.x = (int8_t)(mv_code & 0xff);
mv.y = (int8_t)(mv_code >> 8);
} else {
mv.x = get_sbits(gb, 8);
mv.y = get_sbits(gb, 8);
}
if (pred)
mvi_update_prediction(pred, mv);
}
mv.x += root_mv.x;
mv.y += root_mv.y;
if (lc->bias_cb) {
uint16_t bias_val = get_vlc2(gb, lc->bias_cb, CLV_VLC_BITS, 2);
if (bias_val != BIAS_ESC) {
bias = (int16_t)(bias_val);
} else {
bias = get_sbits(gb, 16);
}
}
if (flags) {
int hsize = size >> 1;
for (i = 0; i < 4; i++) {
int xoff = (i & 2) == 0 ? 0 : hsize;
int yoff = (i & 1) == 0 ? 0 : hsize;
if (flags & (1 << i)) {
err = decode_tile(avctx, gb, lc + 1, dst, src, plane,
x + xoff, y + yoff, hsize, root_mv, NULL);
} else {
err = tile_do_block(avctx, dst, src, plane, x + xoff, y + yoff,
mv.x, mv.y, hsize, bias);
}
if (err < 0)
return err;
}
} else {
err = tile_do_block(avctx, dst, src, plane, x, y, mv.x, mv.y, size, bias);
if (err < 0)
return err;
}
return 0;
}
static void extend_edges(AVFrame *buf, int tile_size)
{
int comp, i, j;
for (comp = 0; comp < 3; comp++) {
int shift = comp > 0;
int w = buf->width >> shift;
int h = buf->height >> shift;
int size = comp == 0 ? tile_size : tile_size >> 1;
int stride = buf->linesize[comp];
uint8_t *framebuf = buf->data[comp];
int right = size - (w & (size - 1));
int bottom = size - (h & (size - 1));
if ((right == size) && (bottom == size)) {
return;
}
if (right != size) {
int off = w;
for (j = 0; j < h; j++) {
for (i = 0; i < right; i++) {
framebuf[off + i] = 0x80;
}
off += stride;
}
}
if (bottom != size) {
int off = h * stride;
for (j = 0; j < bottom; j++) {
for (i = 0; i < stride; i++) {
framebuf[off + i] = 0x80;
}
off += stride;
}
}
}
}
static int clv_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
int *got_frame, AVPacket *avpkt)
{
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size;
CLVContext *c = avctx->priv_data;
GetByteContext gb;
uint32_t frame_type;
int i, j, ret;
int mb_ret = 0;
bytestream2_init(&gb, buf, buf_size);
if (avctx->codec_tag == MKTAG('C', 'L', 'V', '1')) {
int skip = bytestream2_get_byte(&gb);
bytestream2_skip(&gb, (skip + 1) * 8);
}
frame_type = bytestream2_get_byte(&gb);
if ((frame_type & 0x7f) == 0x30) {
*got_frame = 0;
return buf_size;
} else if (frame_type & 0x2) {
if (buf_size < c->mb_width * c->mb_height) {
av_log(avctx, AV_LOG_ERROR, "Packet too small\n");
return AVERROR_INVALIDDATA;
}
if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0)
return ret;
c->pic->flags |= AV_FRAME_FLAG_KEY;
c->pic->pict_type = AV_PICTURE_TYPE_I;
bytestream2_get_be32(&gb); // frame size;
c->ac_quant = bytestream2_get_byte(&gb);
c->luma_dc_quant = 32;
c->chroma_dc_quant = 32;
if ((ret = init_get_bits8(&c->gb, buf + bytestream2_tell(&gb),
buf_size - bytestream2_tell(&gb))) < 0)
return ret;
for (i = 0; i < 3; i++)
c->top_dc[i] = 32;
for (i = 0; i < 4; i++)
c->left_dc[i] = 32;
for (j = 0; j < c->mb_height; j++) {
for (i = 0; i < c->mb_width; i++) {
ret = decode_mb(c, i, j);
if (ret < 0)
mb_ret = ret;
}
}
extend_edges(c->pic, c->tile_size);
} else {
int plane;
if (c->pmb_width * c->pmb_height > 8LL*(buf_size - bytestream2_tell(&gb)))
return AVERROR_INVALIDDATA;
if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0)
return ret;
ret = av_frame_copy(c->pic, c->prev);
if (ret < 0)
return ret;
if ((ret = init_get_bits8(&c->gb, buf + bytestream2_tell(&gb),
buf_size - bytestream2_tell(&gb))) < 0)
return ret;
mvi_reset(&c->mvi, c->pmb_width, c->pmb_height, 1 << c->tile_shift);
for (j = 0; j < c->pmb_height; j++) {
for (i = 0; i < c->pmb_width; i++) {
MV *mvp, mv;
if (get_bits_left(&c->gb) <= 0)
return AVERROR_INVALIDDATA;
mvp = mvi_predict(&c->mvi, i, j);
mv = *mvp;
if (get_bits1(&c->gb)) {
for (plane = 0; plane < 3; plane++) {
int16_t x = plane == 0 ? i << c->tile_shift : i << (c->tile_shift - 1);
int16_t y = plane == 0 ? j << c->tile_shift : j << (c->tile_shift - 1);
int16_t size = plane == 0 ? 1 << c->tile_shift : 1 << (c->tile_shift - 1);
int16_t mx = plane == 0 ? mv.x : mv.x / 2;
int16_t my = plane == 0 ? mv.y : mv.y / 2;
ret = copy_block(avctx, c->pic, c->prev, plane, x, y, mx, my, size);
if (ret < 0)
mb_ret = ret;
}
} else {
int x = i << c->tile_shift;
int y = j << c->tile_shift;
int size = 1 << c->tile_shift;
MV cmv;
ret = decode_tile(avctx, &c->gb, &lev[0], c->pic, c->prev, // Y
0, x, y, size, mv, mvp);
if (ret < 0)
mb_ret = ret;
x = i << (c->tile_shift - 1);
y = j << (c->tile_shift - 1);
size = 1 << (c->tile_shift - 1);
cmv = *mvp;
cmv.x /= 2;
cmv.y /= 2;
ret = decode_tile(avctx, &c->gb, &lev[4], c->pic, c->prev, // U
1, x, y, size, cmv, NULL);
if (ret < 0)
mb_ret = ret;
ret = decode_tile(avctx, &c->gb, &lev[7], c->pic, c->prev, // U
2, x, y, size, cmv, NULL);
if (ret < 0)
mb_ret = ret;
}
}
mvi_update_row(&c->mvi);
}
extend_edges(c->pic, c->tile_size);
c->pic->flags &= ~AV_FRAME_FLAG_KEY;
c->pic->pict_type = AV_PICTURE_TYPE_P;
}
if ((ret = av_frame_ref(rframe, c->pic)) < 0)
return ret;
FFSWAP(AVFrame *, c->pic, c->prev);
*got_frame = 1;
if (get_bits_left(&c->gb) < 0)
av_log(c->avctx, AV_LOG_WARNING, "overread %d\n", -get_bits_left(&c->gb));
return mb_ret < 0 ? mb_ret : buf_size;
}
static av_cold const VLCElem *build_vlc(VLCInitState *state,
const uint8_t counts[16],
const uint16_t **syms)
{
uint8_t lens[MAX_VLC_ENTRIES];
const uint16_t *symbols = *syms;
unsigned num = 0;
for (int i = 0; i < 16; i++) {
unsigned count = counts[i];
if (count == 255) /* Special case for Y_3 table */
count = 303;
for (count += num; num < count; num++)
lens[num] = i + 1;
}
*syms += num;
return ff_vlc_init_tables_from_lengths(state, CLV_VLC_BITS, num, lens, 1,
symbols, 2, 2, 0, 0);
}
static av_cold void clv_init_static(void)
{
static VLCElem vlc_buf[16716];
VLCInitState state = VLC_INIT_STATE(vlc_buf);
const uint16_t *mv_syms = clv_mv_syms, *bias_syms = clv_bias_syms;
VLC_INIT_STATIC_TABLE_FROM_LENGTHS(dc_vlc, CLV_VLC_BITS, NUM_DC_CODES,
clv_dc_lens, 1,
clv_dc_syms, 1, 1, -63, 0);
VLC_INIT_STATIC_TABLE_FROM_LENGTHS(ac_vlc, CLV_VLC_BITS, NUM_AC_CODES,
clv_ac_bits, 1,
clv_ac_syms, 2, 2, 0, 0);
for (unsigned i = 0, j = 0, k = 0;; i++) {
if (0x36F & (1 << i)) {
lev[i].mv_cb = build_vlc(&state, clv_mv_len_counts[k], &mv_syms);
k++;
}
if (i == FF_ARRAY_ELEMS(lev) - 1)
break;
if (0x1B7 & (1 << i)) {
lev[i].flags_cb =
ff_vlc_init_tables_from_lengths(&state, CLV_VLC_BITS, 16,
clv_flags_bits[j], 1,
clv_flags_syms[j], 1, 1,
0, 0);
lev[i + 1].bias_cb = build_vlc(&state, clv_bias_len_counts[j],
&bias_syms);
j++;
}
}
}
static av_cold int clv_decode_init(AVCodecContext *avctx)
{
static AVOnce init_static_once = AV_ONCE_INIT;
CLVContext *const c = avctx->priv_data;
int ret, w, h;
if (avctx->extradata_size == 110) {
c->tile_size = AV_RL32(&avctx->extradata[94]);
} else if (avctx->extradata_size == 150) {
c->tile_size = AV_RB32(&avctx->extradata[134]);
} else if (!avctx->extradata_size) {
c->tile_size = 16;
} else {
av_log(avctx, AV_LOG_ERROR, "Unsupported extradata size: %d\n", avctx->extradata_size);
return AVERROR_INVALIDDATA;
}
c->tile_shift = av_log2(c->tile_size);
if (1U << c->tile_shift != c->tile_size || c->tile_shift < 1 || c->tile_shift > 30) {
av_log(avctx, AV_LOG_ERROR, "Tile size: %d, is not power of 2 > 1 and < 2^31\n", c->tile_size);
return AVERROR_INVALIDDATA;
}
avctx->pix_fmt = AV_PIX_FMT_YUV420P;
w = avctx->width;
h = avctx->height;
ret = ff_set_dimensions(avctx, FFALIGN(w, 1 << c->tile_shift), FFALIGN(h, 1 << c->tile_shift));
if (ret < 0)
return ret;
avctx->width = w;
avctx->height = h;
c->avctx = avctx;
c->mb_width = FFALIGN(avctx->width, 16) >> 4;
c->mb_height = FFALIGN(avctx->height, 16) >> 4;
c->pmb_width = (w + c->tile_size - 1) >> c->tile_shift;
c->pmb_height = (h + c->tile_size - 1) >> c->tile_shift;
c->pic = av_frame_alloc();
c->prev = av_frame_alloc();
c->mvi.mv = av_calloc(c->pmb_width * 2, sizeof(*c->mvi.mv));
if (!c->pic || !c->prev || !c->mvi.mv)
return AVERROR(ENOMEM);
ff_idctdsp_init(&c->idsp, avctx);
ff_thread_once(&init_static_once, clv_init_static);
return 0;
}
static av_cold int clv_decode_end(AVCodecContext *avctx)
{
CLVContext *const c = avctx->priv_data;
av_frame_free(&c->prev);
av_frame_free(&c->pic);
av_freep(&c->mvi.mv);
return 0;
}
const FFCodec ff_clearvideo_decoder = {
.p.name = "clearvideo",
CODEC_LONG_NAME("Iterated Systems ClearVideo"),
.p.type = AVMEDIA_TYPE_VIDEO,
.p.id = AV_CODEC_ID_CLEARVIDEO,
.priv_data_size = sizeof(CLVContext),
.init = clv_decode_init,
.close = clv_decode_end,
FF_CODEC_DECODE_CB(clv_decode_frame),
.p.capabilities = AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
};