[FFmpeg-devel] [PATCH] avcodec: add VMX1 decoder

Andreas Rheinhardt andreas.rheinhardt at outlook.com
Sat Jun 10 13:24:51 EEST 2023


Paul B Mahol:
> +static av_cold int decode_init(AVCodecContext *avctx)
> +{
> +    VMIXContext *s = avctx->priv_data;
> +
> +    avctx->bits_per_raw_sample = 8;
> +    avctx->pix_fmt = AV_PIX_FMT_YUV422P;
> +
> +    avctx->coded_width = FFALIGN(avctx->width, 16);
> +    avctx->coded_height = FFALIGN(avctx->height, 16);
> +
> +    ff_idctdsp_init(&s->idsp, avctx);

Missing idctdsp configure dependency.

> +    ff_permute_scantable(s->scan, ff_zigzag_direct,
> +                         s->idsp.idct_permutation);
> +    return 0;
> +}
> +
> +static inline int get_se_golomb_vmix(GetBitContext *gb)
> +{
> +    unsigned int buf = get_ue_golomb_long(gb);
> +    int sign = (buf & 1) - 1;
> +    return ((buf >> 1) ^ (~sign));
> +}
> +
> +static int decode_dcac(AVCodecContext *avctx,
> +                       GetBitContext *dc_gb, GetBitContext *ac_gb,
> +                       unsigned *dcrun, unsigned *acrun,
> +                       AVFrame *frame, int width, int by, int plane)
> +{
> +    const ptrdiff_t linesize = frame->linesize[plane];
> +    uint8_t *dst = frame->data[plane] + by * linesize;
> +    unsigned dc_run = *dcrun, ac_run = *acrun;
> +    LOCAL_ALIGNED_32(int16_t, block, [64]);
> +    VMIXContext *s = avctx->priv_data;
> +    const int16_t *factors = s->factors;
> +    const uint8_t *scan = s->scan;
> +    const int add = plane ? 0 : 1024;
> +    int i, dc_v = 0, ac_v = 0, dc = 0;
> +
> +    for (int y = 0; y < 2; y++) {
> +        for (int x = 0; x < width; x += 8) {
> +            memset(block, 0, sizeof(*block)*64);
> +
> +            if (dc_run > 0) {
> +                dc_run--;
> +            } else {
> +                dc_v = get_se_golomb_vmix(dc_gb);
> +                dc += dc_v;
> +                if (!dc_v)
> +                    dc_run = get_ue_golomb_long(dc_gb);
> +            }
> +
> +            for (int n = 0; n < 64; n++) {
> +                if (ac_run > 0) {
> +                    ac_run--;
> +                    continue;
> +                }
> +
> +                ac_v = get_se_golomb_vmix(ac_gb);
> +                i = scan[n];
> +                block[i] = (ac_v * factors[i]) >> 4;
> +                if (!ac_v)
> +                    ac_run = get_ue_golomb_long(ac_gb);
> +            }
> +
> +            block[0] = ((dc + add) * 16) >> 4;
> +            s->idsp.idct_put(dst + x, linesize, block);
> +        }
> +
> +        dst += 8 * linesize;
> +    }
> +
> +    *dcrun = dc_run;
> +    *acrun = ac_run;
> +
> +    return 0;
> +}
> +
> +static int decode_slice(AVCodecContext *avctx, AVFrame *frame,
> +                        const uint8_t *dc_src, unsigned dc_slice_size,
> +                        const uint8_t *ac_src, unsigned ac_slice_size,
> +                        int by)
> +{
> +    unsigned dc_run = 0, ac_run = 0;
> +    GetBitContext dc_gb, ac_gb;
> +    int ret;
> +
> +    ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size);
> +    if (ret < 0)
> +        return ret;
> +
> +    ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size);
> +    if (ret < 0)
> +        return ret;
> +
> +    for (int p = 0; p < 3; p++) {
> +        const int rshift = !!p;
> +        ret = decode_dcac(avctx, &dc_gb, &ac_gb,
> +                          &dc_run, &ac_run, frame,
> +                          frame->width >> rshift, by, p);
> +        if (ret < 0)
> +            return ret;
> +
> +        if (get_bits_left(&dc_gb) < 0)
> +            return AVERROR_INVALIDDATA;
> +        if (get_bits_left(&ac_gb) < 0)
> +            return AVERROR_INVALIDDATA;
> +
> +        align_get_bits(&dc_gb);
> +        align_get_bits(&ac_gb);
> +    }
> +
> +    if (get_bits_left(&dc_gb) > 0)
> +        return AVERROR_INVALIDDATA;
> +    if (get_bits_left(&ac_gb) > 0)
> +        return AVERROR_INVALIDDATA;
> +
> +    return 0;
> +}
> +
> +static int decode_slices(AVCodecContext *avctx, void *arg,
> +                         int n, int thread_nb)
> +{
> +    VMIXContext *s = avctx->priv_data;
> +    const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr;
> +    const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr;
> +    unsigned dc_slice_size = s->slices[n].dc_size;
> +    unsigned ac_slice_size = s->slices[n].ac_size;
> +    AVFrame *frame = arg;
> +
> +    return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size,
> +                        ac_slice_ptr, ac_slice_size, n * 16);
> +}
> +
> +static int decode_frame(AVCodecContext *avctx,
> +                        AVFrame *frame, int *got_frame,
> +                        AVPacket *avpkt)
> +{
> +    VMIXContext *s = avctx->priv_data;
> +    unsigned offset = 3, q;
> +    int ret;
> +
> +    if (avpkt->size <= 7)
> +        return AVERROR_INVALIDDATA;
> +
> +    if (avpkt->data[0] != 0x01)
> +        return AVERROR_INVALIDDATA;
> +
> +    q = av_clip(99 - av_clip(avpkt->data[1], 0, 99), 0, FF_ARRAY_ELEMS(quality) - 1);

The inner av_clip() is redundant.

> +    for (int n = 0; n < 64; n++)
> +        s->factors[n] = quant[n] * quality[q];
> +
> +    s->nb_slices = avpkt->data[2];
> +    if (!s->nb_slices || s->nb_slices > (avctx->height + 15) / 16)
> +        return AVERROR_INVALIDDATA;
> +
> +    ret = ff_thread_get_buffer(avctx, frame, 0);

This should be moved after the loops below in order to avoid an
allocation in case of invalid input.

> +    if (ret < 0)
> +        return ret;
> +
> +    for (int n = 0; n < s->nb_slices; n++) {
> +        unsigned slice_size;
> +
> +        if (offset + 4 > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        slice_size = AV_RL32(avpkt->data + offset);
> +        if (slice_size > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        if (avpkt->size - slice_size - 4LL < offset)
> +            return AVERROR_INVALIDDATA;
> +
> +        s->slices[n].dc_size = slice_size;
> +        s->slices[n].dc_ptr = avpkt->data + offset + 4;
> +        offset += slice_size + 4;
> +    }
> +
> +    for (int n = 0; n < s->nb_slices; n++) {
> +        unsigned slice_size;
> +
> +        if (offset + 4 > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        slice_size = AV_RL32(avpkt->data + offset);
> +        if (slice_size > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        if (avpkt->size - slice_size - 4LL < offset)
> +            return AVERROR_INVALIDDATA;
> +
> +        s->slices[n].ac_size = slice_size;
> +        s->slices[n].ac_ptr = avpkt->data + offset + 4;
> +        offset += slice_size + 4;
> +    }

These loops could be combined if you modified the way you stored the
data by putting the dc information into slices[n] and the corresponding
ac information into slices[n + nb_slices]. In this case, you would need
to unpack this in decode_slices.
If you don't do this, then there is no need to put nb_slices in the
context. It can be local to this function.

> +
> +    avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices);
> +
> +    frame->pict_type = AV_PICTURE_TYPE_I;
> +    frame->flags |= AV_FRAME_FLAG_KEY;
> +
> +    *got_frame = 1;
> +
> +    return avpkt->size;
> +}



More information about the ffmpeg-devel mailing list