Commit 0ec6a0872d for ffmpeg
commit 0ec6a0872d2aadbf664820487d037479957fd108
Author: Tom Needham <06needhamt@gmail.com>
Date: Sat Sep 5 22:19:14 2026 +0000
libavcodec: Add Infinity IMM6 Decoder
Signed-off-by: Tom Needham <06needhamt@gmail.com>
diff --git a/Changelog b/Changelog
index 32e7c8bfe7..c4f1c4689e 100644
--- a/Changelog
+++ b/Changelog
@@ -19,6 +19,7 @@ version <next>:
- Panasonic RW2 decoder
- xHE-AAC encoding via libfdk-xhe-aac
- jpegapp bitstream filter
+- Infinity IMM6 video decoder
version 9.0:
diff --git a/configure b/configure
index b724e10929..d2a69d7783 100755
--- a/configure
+++ b/configure
@@ -3185,6 +3185,7 @@ iac_decoder_select="imc_decoder"
imc_decoder_select="bswapdsp sinewin"
imm4_decoder_select="bswapdsp idctdsp"
imm5_decoder_select="h264_decoder hevc_decoder"
+imm6_decoder_select="h264_decoder"
indeo3_decoder_select="hpeldsp"
indeo4_decoder_select="ividsp"
indeo5_decoder_select="ividsp"
diff --git a/doc/APIchanges b/doc/APIchanges
index da21e47e88..4e02c0996d 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
API changes, most recent first:
+2026-10-18 - xxxxxxxxxx - lavc 63.21.100 - codec_id.h
+ Add AV_CODEC_ID_IMM6.
+
2026-10-xx - xxxxxxxxxx - lavf 63.11.100 - avio.h
Add avio_open_dir2().
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 7032f140c6..50bc41c18b 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -1136,6 +1136,8 @@ following image formats are supported:
@item IFF ByteRun1 @tab @tab X
@tab IFF run length encoded bitmap
@item Infinity IMM4 @tab @tab X
+@item Infinity IMM5 @tab @tab X
+@item Infinity IMM6 @tab @tab X
@item Intel H.263 @tab @tab X
@item Intel Indeo 2 @tab @tab X
@item Intel Indeo 3 @tab @tab X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 249b0fe0b8..3d0be56320 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -500,6 +500,7 @@ 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_IMM6_DECODER) += imm6.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 c344cb539e..b12dcc405a 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -173,6 +173,7 @@ extern const FFCodec ff_idcin_decoder;
extern const FFCodec ff_iff_ilbm_decoder;
extern const FFCodec ff_imm4_decoder;
extern const FFCodec ff_imm5_decoder;
+extern const FFCodec ff_imm6_decoder;
extern const FFCodec ff_indeo2_decoder;
extern const FFCodec ff_indeo3_decoder;
extern const FFCodec ff_indeo4_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index e91366f43b..70d8df71f0 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2009,6 +2009,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
.props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
.mime_types= MT("image/x-panasonic-rw2"),
},
+ {
+ .id = AV_CODEC_ID_IMM6,
+ .type = AVMEDIA_TYPE_VIDEO,
+ .name = "imm6",
+ .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM6"),
+ .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER,
+ },
/* various PCM "codecs" */
{
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 0133751d31..a4e8e30dfd 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -326,6 +326,7 @@ enum AVCodecID {
AV_CODEC_ID_WEBP_ANIM,
AV_CODEC_ID_ASTC,
AV_CODEC_ID_RW2,
+ AV_CODEC_ID_IMM6,
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/imm6.c b/libavcodec/imm6.c
new file mode 100644
index 0000000000..caca0459c9
--- /dev/null
+++ b/libavcodec/imm6.c
@@ -0,0 +1,196 @@
+/*
+ * Infinity IMM6 video decoder
+ *
+ * Copyright (c) 2026 Tom Needham
+ *
+ * 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 "libavutil/attributes.h"
+#include "libavutil/attributes_internal.h"
+#include "libavutil/intreadwrite.h"
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "decode.h"
+
+typedef struct IMM6Context {
+ AVCodecContext *h264_avctx;
+ int draining;
+} IMM6Context;
+
+static av_cold int imm6_init(AVCodecContext *avctx)
+{
+ IMM6Context *s = avctx->priv_data;
+ int ret;
+
+ EXTERN const FFCodec ff_h264_decoder;
+
+ s->h264_avctx = avcodec_alloc_context3(&ff_h264_decoder.p);
+ if (!s->h264_avctx)
+ return AVERROR(ENOMEM);
+
+ /* The wrapper delegates threading to the nested H.264 decoder. */
+ s->h264_avctx->thread_count = avctx->thread_count;
+ s->h264_avctx->thread_type = avctx->thread_type;
+ s->h264_avctx->flags = avctx->flags;
+ s->h264_avctx->flags2 = avctx->flags2;
+ s->h264_avctx->max_pixels = avctx->max_pixels;
+ s->h264_avctx->pkt_timebase = avctx->pkt_timebase;
+ s->h264_avctx->skip_frame = avctx->skip_frame;
+ s->h264_avctx->skip_loop_filter = avctx->skip_loop_filter;
+ s->h264_avctx->apply_cropping = 0;
+
+ ret = avcodec_open2(s->h264_avctx, NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int imm6_send_packet(AVCodecContext *avctx, AVPacket *pkt)
+{
+ IMM6Context *s = avctx->priv_data;
+ AVPacket h264_pkt;
+ uint32_t payload_size;
+ int ret;
+
+ if (pkt->size < 24) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Packet too small for IMM6 header: %d bytes\n", pkt->size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (pkt->data[8] > 1) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Invalid IMM6 mode %u\n", pkt->data[8]);
+ return AVERROR_INVALIDDATA;
+ }
+
+ payload_size = AV_RL32(pkt->data + 4);
+ if (payload_size > (unsigned)pkt->size - 24) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Invalid IMM6 payload size %u in %d-byte packet\n",
+ payload_size, pkt->size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ h264_pkt = *pkt;
+ h264_pkt.data += 24;
+ h264_pkt.size = payload_size;
+
+ s->h264_avctx->skip_frame = avctx->skip_frame;
+ s->h264_avctx->skip_loop_filter = avctx->skip_loop_filter;
+
+ ret = avcodec_send_packet(s->h264_avctx, &h264_pkt);
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ av_log(avctx, AV_LOG_ERROR,
+ "Error submitting IMM6 H.264 payload for decoding\n");
+
+ return ret;
+}
+
+static void imm6_update_context(AVCodecContext *avctx)
+{
+ IMM6Context *s = avctx->priv_data;
+ AVCodecContext *h264 = s->h264_avctx;
+
+ avctx->pix_fmt = h264->pix_fmt;
+ avctx->coded_width = h264->coded_width;
+ avctx->coded_height = h264->coded_height;
+ avctx->width = h264->width;
+ avctx->height = h264->height;
+ avctx->delay = h264->delay;
+ avctx->colorspace = h264->colorspace;
+ avctx->color_range = h264->color_range;
+ avctx->color_trc = h264->color_trc;
+ avctx->color_primaries = h264->color_primaries;
+ avctx->chroma_sample_location = h264->chroma_sample_location;
+ avctx->sample_aspect_ratio = h264->sample_aspect_ratio;
+ avctx->has_b_frames = h264->has_b_frames;
+}
+
+static int imm6_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+ IMM6Context *s = avctx->priv_data;
+ AVPacket pkt = { 0 };
+ int ret;
+
+ for (;;) {
+ ret = avcodec_receive_frame(s->h264_avctx, frame);
+ if (ret >= 0) {
+ imm6_update_context(avctx);
+ return 0;
+ }
+ if (ret == AVERROR_EOF)
+ return ret;
+ if (ret != AVERROR(EAGAIN))
+ return ret;
+
+ if (s->draining)
+ return AVERROR(EAGAIN);
+
+ ret = ff_decode_get_packet(avctx, &pkt);
+ if (ret == AVERROR(EAGAIN))
+ return ret;
+ if (ret == AVERROR_EOF) {
+ s->draining = 1;
+ ret = avcodec_send_packet(s->h264_avctx, NULL);
+ if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN))
+ return ret;
+ continue;
+ }
+ if (ret < 0)
+ return ret;
+
+ ret = imm6_send_packet(avctx, &pkt);
+ av_packet_unref(&pkt);
+ if (ret < 0)
+ return ret;
+ }
+}
+
+static av_cold void imm6_flush(AVCodecContext *avctx)
+{
+ IMM6Context *s = avctx->priv_data;
+
+ avcodec_flush_buffers(s->h264_avctx);
+ s->draining = 0;
+}
+
+static av_cold int imm6_close(AVCodecContext *avctx)
+{
+ IMM6Context *s = avctx->priv_data;
+
+ avcodec_free_context(&s->h264_avctx);
+
+ return 0;
+}
+
+const FFCodec ff_imm6_decoder = {
+ .p.name = "imm6",
+ CODEC_LONG_NAME("Infinity IMM6"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_IMM6,
+ .init = imm6_init,
+ FF_CODEC_RECEIVE_FRAME_CB(imm6_receive_frame),
+ .close = imm6_close,
+ .flush = imm6_flush,
+ .priv_data_size = sizeof(IMM6Context),
+ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index ef9018e70b..9f3de34a5c 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,8 +29,8 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 20
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MINOR 2
+#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \