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

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Tue Jul 16 01:36:00 EEST 2019


Paul B Mahol:
> Signed-off-by: Paul B Mahol <onemda at gmail.com>
> ---
>  configure               |   1 +
>  libavcodec/Makefile     |   1 +
>  libavcodec/allcodecs.c  |   1 +
>  libavcodec/avcodec.h    |   1 +
>  libavcodec/codec_desc.c |   7 ++
>  libavcodec/imm5.c       | 234 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/riff.c      |   1 +
>  7 files changed, 246 insertions(+)
>  create mode 100644 libavcodec/imm5.c
> 
> diff --git a/configure b/configure
> index 5a4f507246..d03cf43350 100755
> --- a/configure
> +++ b/configure
> @@ -2714,6 +2714,7 @@ huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp"
>  hymt_decoder_select="huffyuv_decoder"
>  iac_decoder_select="imc_decoder"
>  imc_decoder_select="bswapdsp fft mdct sinewin"
> +imm5_decoder_select="h264_decoder hevc_decoder"
>  indeo3_decoder_select="hpeldsp"
>  indeo4_decoder_select="ividsp"
>  indeo5_decoder_select="ividsp"
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 3cd73fbcc6..39f4d9118c 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -393,6 +393,7 @@ OBJS-$(CONFIG_IFF_ILBM_DECODER)        += iff.o
>  OBJS-$(CONFIG_ILBC_DECODER)            += ilbcdec.o
>  OBJS-$(CONFIG_IMC_DECODER)             += imc.o
>  OBJS-$(CONFIG_IMM4_DECODER)            += imm4.o
> +OBJS-$(CONFIG_IMM5_DECODER)            += imm5.o
>  OBJS-$(CONFIG_INDEO2_DECODER)          += indeo2.o
>  OBJS-$(CONFIG_INDEO3_DECODER)          += indeo3.o
>  OBJS-$(CONFIG_INDEO4_DECODER)          += indeo4.o ivi.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index d2f9a39ce5..fe7f773925 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -158,6 +158,7 @@ extern AVCodec ff_hymt_decoder;
>  extern AVCodec ff_idcin_decoder;
>  extern AVCodec ff_iff_ilbm_decoder;
>  extern AVCodec ff_imm4_decoder;
> +extern AVCodec ff_imm5_decoder;
>  extern AVCodec ff_indeo2_decoder;
>  extern AVCodec ff_indeo3_decoder;
>  extern AVCodec ff_indeo4_decoder;
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 2528bd89ab..da6f92b443 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -457,6 +457,7 @@ enum AVCodecID {
>      AV_CODEC_ID_AGM,
>      AV_CODEC_ID_LSCR,
>      AV_CODEC_ID_VP4,
> +    AV_CODEC_ID_IMM5,
>  
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 4d033c20ff..e6373be504 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1726,6 +1726,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
>          .long_name = NULL_IF_CONFIG_SMALL("On2 VP4"),
>          .props     = AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_IMM5,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "imm5",
> +        .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM5"),
> +        .props     = AV_CODEC_PROP_LOSSY,
> +    },
>  
>      /* various PCM "codecs" */
>      {
> diff --git a/libavcodec/imm5.c b/libavcodec/imm5.c
> new file mode 100644
> index 0000000000..f6b2ce691c
> --- /dev/null
> +++ b/libavcodec/imm5.c
> @@ -0,0 +1,234 @@
> +/*
> + * Copyright (c) 2019 Paul B Mahol
> + *
> + * 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 "bytestream.h"
> +#include "internal.h"
> +
> +typedef struct IMM5Context {
> +    AVCodecContext *h264_avctx;   // wrapper context for H264
> +    AVCodecContext *hevc_avctx;   // wrapper context for HEVC
> +
> +    int width, height;
> +    GetByteContext gbc;

width, height and gbc don't seem to be used at all.

> +
> +    uint8_t *imm5_buffer;
> +    int imm5_buffer_size;
> +} IMM5Context;
> +
> +static const struct IMM5_unit {
> +    uint8_t bits[14];
> +    uint8_t len;
> +} IMM5_units[14] = {
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x0B, 0x0F, 0x88 }, 12 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x83, 0xE2 }, 12 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x81, 0xE8, 0x80 }, 13 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x0B, 0x04, 0xA2 }, 12 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x81, 0x28, 0x80 }, 13 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x80, 0x92, 0x20 }, 13 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x0B, 0x0F, 0xC8 }, 13 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x83, 0xF2 }, 13 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x81, 0xEC, 0x80 }, 14 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x0B, 0x04, 0xB2 }, 13 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x81, 0x2C, 0x80 }, 14 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x80, 0x93, 0x20 }, 14 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x68, 0xDE, 0x3C, 0x80 }, 8 },
> +    { { 0x00, 0x00, 0x00, 0x01, 0x68, 0xCE, 0x32, 0x28 }, 8 },
> +};
> +
> +static av_cold int imm5_init(AVCodecContext *avctx)
> +{
> +    IMM5Context *ctx = avctx->priv_data;
> +    const AVCodec *codec;
> +    int ret;
> +
> +    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
> +    if (!codec)
> +        return AVERROR_BUG;
> +    ctx->h264_avctx = avcodec_alloc_context3(codec);
> +    if (!ctx->h264_avctx)
> +        return AVERROR(ENOMEM);
> +    ctx->h264_avctx->flags = avctx->flags;
> +    ctx->h264_avctx->flags2 = avctx->flags2;
> +    ret = ff_codec_open2_recursive(ctx->h264_avctx, codec, NULL);
> +    if (ret < 0)
> +        return ret;
> +
> +    codec = avcodec_find_decoder(AV_CODEC_ID_HEVC);
> +    if (!codec)
> +        return AVERROR_BUG;
> +    ctx->hevc_avctx = avcodec_alloc_context3(codec);
> +    if (!ctx->hevc_avctx)
> +        return AVERROR(ENOMEM);
> +    ctx->hevc_avctx->flags = avctx->flags;
> +    ctx->hevc_avctx->flags2 = avctx->flags2;
> +    ret = ff_codec_open2_recursive(ctx->hevc_avctx, codec, NULL);
> +    if (ret < 0)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +static int imm5_decode_frame(AVCodecContext *avctx, void *data,
> +                             int *got_frame, AVPacket *avpkt)
> +{
> +    IMM5Context *ctx = avctx->priv_data;
> +    AVFrame *frame;
> +    AVCodecContext *codec_avctx = ctx->h264_avctx;
> +    int buf_size = avpkt->size;
> +    uint8_t *buf = avpkt->data;
> +    AVPacket hpkt;
> +    int ret;
> +
> +    if (avpkt->size > 24 && avpkt->data[8] <= 1 && AV_RL32(avpkt->data + 4) < avpkt->size) {
> +        int codec_type = avpkt->data[1];
> +        int index = avpkt->data[10];
> +        int new_size = AV_RL32(avpkt->data + 4);
> +        int height, width, offset;
The width and height values that you infer here from the index don't
seem to be used at all.
> +
> +        if (codec_type == 0xA)
> +            codec_avctx = ctx->hevc_avctx;
> +
> +        switch (index) {
> +        case 1:
> +            width = 352;
> +            height = 240;
> +            break;
> +        case 2:
> +            height = 240;
> +            width = 704;
> +            break;
> +        case 4:
> +        case 132:
> +            height = 480;
> +            width = 704;
> +            break;
> +        case 17:
> +            width = 352;
> +            height = 288;
> +            break;
> +        case 18:
> +            height = 288;
> +            width = 704;
> +            index = codec_type ? 11 : 5;
> +            break;
> +        case 133:
> +            width = 640;
> +            height = 480;
> +            break;
> +        case 134:
> +            width = 720;
> +            height = 480;
> +            break;
> +        case 135:
> +            width = 720;
> +            height = 576;
> +            break;
> +        case 136:
> +            width = 800;
> +            height = 600;
> +            break;
> +        case 137:
> +            width = 1024;
> +            height = 768;
> +            break;
> +        case 138:
> +            width = 1280;
> +            height = 1024;
> +            break;
> +        case 139:
> +            width = 1600;
> +            height = 1200;
> +            break;
> +        default:
> +            height = 576;
> +            width = 704;
> +            break;
> +        }
> +
> +        if (index >= 1 && index <= 12) {
> +            index -= 1;
> +            av_fast_padded_malloc(&ctx->imm5_buffer, &ctx->imm5_buffer_size, buf_size);
> +            memcpy(ctx->imm5_buffer, IMM5_units[index].bits, IMM5_units[index].len);
> +            offset = IMM5_units[index].len;
> +            if (avpkt->data[1] == 2) {
> +                memcpy(ctx->imm5_buffer + offset, IMM5_units[12].bits, IMM5_units[12].len);
> +                offset += IMM5_units[12].len;
> +            } else {
> +                memcpy(ctx->imm5_buffer + offset, IMM5_units[13].bits, IMM5_units[13].len);
> +                offset += IMM5_units[13].len;
> +            }
> +            memcpy(ctx->imm5_buffer + offset, avpkt->data + 24, new_size);

You have checked above that new_size < avpkt->size, but unless you
intend to copy a part of avpkt's padding, you should check that
avpkt->size >= 24 + new_size.

Given that your current approach sends non-refcounted packets to the
decoder and thereby forces copying the data, I suggest the following
modifications:
The stuff you write in front of avpkt->data + 24 is always < 24 bytes
long (namely 14 + 8 at most). So increment pkt->data by 24 - offset so
that you have exactly offset bytes in front of the first byte of the
packet data that you want to keep; set pkt->size to offset + new_size,
make the packet writable via av_packet_make_writable and overwrite the
first offset bytes of the packet with the data that you already copy
here (similar to the truehd_core bsf).
If we are not here, but in the "else" case below, simply modify the
packet's data and size.
That way you can get rid of hpkt and the imm5_buffer and (more
importantly), you avoid one round of copying of data. Furthermore, the
packet you actually send will contain the proper timestamps and
side-data (if this is a consideration for this format at all).

> +            buf = ctx->imm5_buffer;
> +            buf_size = new_size + offset;
> +        } else {
> +            buf += 24;
> +            buf_size -= 24;
> +        }
> +    }
> +
> +    frame = av_frame_alloc();
> +
> +    av_init_packet(&hpkt);
> +    hpkt.data = buf;
> +    hpkt.size = buf_size;
> +
> +    ret = avcodec_send_packet(codec_avctx, &hpkt);
> +    if (ret < 0) {
> +        av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n");
> +        return ret;
> +    }
> +
> +    ret = avcodec_receive_frame(codec_avctx, frame);
> +    if (ret < 0)
> +        return ret;
> +
> +    avctx->pix_fmt = codec_avctx->pix_fmt;
> +    ret = av_frame_ref(data, frame);
> +    if (ret < 0)
> +        return ret;
> +
> +    *got_frame = 1;
> +
> +    return avpkt->size;
> +}
> +
> +static av_cold int imm5_close(AVCodecContext *avctx)
> +{
> +    IMM5Context *ctx = avctx->priv_data;
> +
> +    avcodec_free_context(&ctx->h264_avctx);
> +    avcodec_free_context(&ctx->hevc_avctx);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_imm5_decoder = {
> +    .name           = "imm5",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Infinity IMM5"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_IMM5,
> +    .init           = imm5_init,
> +    .decode         = imm5_decode_frame,
> +    .close          = imm5_close,
> +    .priv_data_size = sizeof(IMM5Context),
> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
> +                      FF_CODEC_CAP_INIT_CLEANUP,
> +};
> diff --git a/libavformat/riff.c b/libavformat/riff.c
> index e755ad8d5f..610974ebf0 100644
> --- a/libavformat/riff.c
> +++ b/libavformat/riff.c
> @@ -488,6 +488,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>      { AV_CODEC_ID_AGM,          MKTAG('A', 'G', 'M', '6') },
>      { AV_CODEC_ID_AGM,          MKTAG('A', 'G', 'M', '7') },
>      { AV_CODEC_ID_LSCR,         MKTAG('L', 'S', 'C', 'R') },
> +    { AV_CODEC_ID_IMM5,         MKTAG('I', 'M', 'M', '5') },
>      { AV_CODEC_ID_NONE,         0 }
>  };
>  
> 



More information about the ffmpeg-devel mailing list