FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/imm6.c
Date: 2026-10-19 01:23:10
Exec Total Coverage
Lines: 74 94 78.7%
Functions: 5 6 83.3%
Branches: 19 36 52.8%

Line Branch Exec Source
1 /*
2 * Infinity IMM6 video decoder
3 *
4 * Copyright (c) 2026 Tom Needham
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "libavutil/attributes.h"
24 #include "libavutil/attributes_internal.h"
25 #include "libavutil/intreadwrite.h"
26
27 #include "avcodec.h"
28 #include "codec_internal.h"
29 #include "decode.h"
30
31 typedef struct IMM6Context {
32 AVCodecContext *h264_avctx;
33 int draining;
34 } IMM6Context;
35
36 2 static av_cold int imm6_init(AVCodecContext *avctx)
37 {
38 2 IMM6Context *s = avctx->priv_data;
39 int ret;
40
41 EXTERN const FFCodec ff_h264_decoder;
42
43 2 s->h264_avctx = avcodec_alloc_context3(&ff_h264_decoder.p);
44
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!s->h264_avctx)
45 ✗ return AVERROR(ENOMEM);
46
47 /* The wrapper delegates threading to the nested H.264 decoder. */
48 2 s->h264_avctx->thread_count = avctx->thread_count;
49 2 s->h264_avctx->thread_type = avctx->thread_type;
50 2 s->h264_avctx->flags = avctx->flags;
51 2 s->h264_avctx->flags2 = avctx->flags2;
52 2 s->h264_avctx->max_pixels = avctx->max_pixels;
53 2 s->h264_avctx->pkt_timebase = avctx->pkt_timebase;
54 2 s->h264_avctx->skip_frame = avctx->skip_frame;
55 2 s->h264_avctx->skip_loop_filter = avctx->skip_loop_filter;
56 2 s->h264_avctx->apply_cropping = 0;
57
58 2 ret = avcodec_open2(s->h264_avctx, NULL, NULL);
59
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
60 ✗ return ret;
61
62 2 return 0;
63 }
64
65 13 static int imm6_send_packet(AVCodecContext *avctx, AVPacket *pkt)
66 {
67 13 IMM6Context *s = avctx->priv_data;
68 AVPacket h264_pkt;
69 uint32_t payload_size;
70 int ret;
71
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (pkt->size < 24) {
73 ✗ av_log(avctx, AV_LOG_ERROR,
74 "Packet too small for IMM6 header: %d bytes\n", pkt->size);
75 ✗ return AVERROR_INVALIDDATA;
76 }
77
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (pkt->data[8] > 1) {
79 ✗ av_log(avctx, AV_LOG_ERROR,
80 ✗ "Invalid IMM6 mode %u\n", pkt->data[8]);
81 ✗ return AVERROR_INVALIDDATA;
82 }
83
84 13 payload_size = AV_RL32(pkt->data + 4);
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (payload_size > (unsigned)pkt->size - 24) {
86 ✗ av_log(avctx, AV_LOG_ERROR,
87 "Invalid IMM6 payload size %u in %d-byte packet\n",
88 payload_size, pkt->size);
89 ✗ return AVERROR_INVALIDDATA;
90 }
91
92 13 h264_pkt = *pkt;
93 13 h264_pkt.data += 24;
94 13 h264_pkt.size = payload_size;
95
96 13 s->h264_avctx->skip_frame = avctx->skip_frame;
97 13 s->h264_avctx->skip_loop_filter = avctx->skip_loop_filter;
98
99 13 ret = avcodec_send_packet(s->h264_avctx, &h264_pkt);
100
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if (ret < 0 && ret != AVERROR(EAGAIN))
101 ✗ av_log(avctx, AV_LOG_ERROR,
102 "Error submitting IMM6 H.264 payload for decoding\n");
103
104 13 return ret;
105 }
106
107 11 static void imm6_update_context(AVCodecContext *avctx)
108 {
109 11 IMM6Context *s = avctx->priv_data;
110 11 AVCodecContext *h264 = s->h264_avctx;
111
112 11 avctx->pix_fmt = h264->pix_fmt;
113 11 avctx->coded_width = h264->coded_width;
114 11 avctx->coded_height = h264->coded_height;
115 11 avctx->width = h264->width;
116 11 avctx->height = h264->height;
117 11 avctx->delay = h264->delay;
118 11 avctx->colorspace = h264->colorspace;
119 11 avctx->color_range = h264->color_range;
120 11 avctx->color_trc = h264->color_trc;
121 11 avctx->color_primaries = h264->color_primaries;
122 11 avctx->chroma_sample_location = h264->chroma_sample_location;
123 11 avctx->sample_aspect_ratio = h264->sample_aspect_ratio;
124 11 avctx->has_b_frames = h264->has_b_frames;
125 11 }
126
127 28 static int imm6_receive_frame(AVCodecContext *avctx, AVFrame *frame)
128 {
129 28 IMM6Context *s = avctx->priv_data;
130 28 AVPacket pkt = { 0 };
131 int ret;
132
133 for (;;) {
134 42 ret = avcodec_receive_frame(s->h264_avctx, frame);
135
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 31 times.
42 if (ret >= 0) {
136 11 imm6_update_context(avctx);
137 11 return 0;
138 }
139
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30 times.
31 if (ret == AVERROR_EOF)
140 1 return ret;
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (ret != AVERROR(EAGAIN))
142 ✗ return ret;
143
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (s->draining)
145 ✗ return AVERROR(EAGAIN);
146
147 30 ret = ff_decode_get_packet(avctx, &pkt);
148
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 14 times.
30 if (ret == AVERROR(EAGAIN))
149 16 return ret;
150
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
14 if (ret == AVERROR_EOF) {
151 1 s->draining = 1;
152 1 ret = avcodec_send_packet(s->h264_avctx, NULL);
153
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if (ret < 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN))
154 ✗ return ret;
155 1 continue;
156 }
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (ret < 0)
158 ✗ return ret;
159
160 13 ret = imm6_send_packet(avctx, &pkt);
161 13 av_packet_unref(&pkt);
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (ret < 0)
163 ✗ return ret;
164 }
165 }
166
167 ✗ static av_cold void imm6_flush(AVCodecContext *avctx)
168 {
169 ✗ IMM6Context *s = avctx->priv_data;
170
171 ✗ avcodec_flush_buffers(s->h264_avctx);
172 ✗ s->draining = 0;
173 ✗ }
174
175 2 static av_cold int imm6_close(AVCodecContext *avctx)
176 {
177 2 IMM6Context *s = avctx->priv_data;
178
179 2 avcodec_free_context(&s->h264_avctx);
180
181 2 return 0;
182 }
183
184 const FFCodec ff_imm6_decoder = {
185 .p.name = "imm6",
186 CODEC_LONG_NAME("Infinity IMM6"),
187 .p.type = AVMEDIA_TYPE_VIDEO,
188 .p.id = AV_CODEC_ID_IMM6,
189 .init = imm6_init,
190 FF_CODEC_RECEIVE_FRAME_CB(imm6_receive_frame),
191 .close = imm6_close,
192 .flush = imm6_flush,
193 .priv_data_size = sizeof(IMM6Context),
194 .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
195 .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS,
196 };
197