FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/ifv.c
Date: 2026-09-25 23:13:28
Exec Total Coverage
Lines: 3 159 1.9%
Functions: 1 6 16.7%
Branches: 1 82 1.2%

Line Branch Exec Source
1 /*
2 * IFV demuxer
3 *
4 * Copyright (c) 2019 Swaraj Hota
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/channel_layout.h"
24 #include "avformat.h"
25 #include "demux.h"
26 #include "internal.h"
27 #include "avio_internal.h"
28
29 typedef struct IFVContext {
30 uint32_t next_video_index;
31 uint32_t next_audio_index;
32 uint32_t total_vframes;
33 uint32_t total_aframes;
34
35 int width, height;
36 int is_audio_present;
37 int sample_rate;
38
39 int video_stream_index;
40 int audio_stream_index;
41 } IFVContext;
42
43 8066 static int ifv_probe(const AVProbeData *p)
44 {
45 static const uint8_t ifv_magic[] = {0x11, 0xd2, 0xd3, 0xab, 0xba, 0xa9,
46 0xcf, 0x11, 0x8e, 0xe6, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65, 0x44};
47
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8066 times.
8066 if (!memcmp(p->buf, ifv_magic, sizeof(ifv_magic)))
49 ✗ return AVPROBE_SCORE_MAX;
50
51 8066 return 0;
52 }
53
54 ✗ static int read_index(AVFormatContext *s,
55 enum AVMediaType frame_type,
56 uint32_t start_index)
57 {
58 ✗ IFVContext *ifv = s->priv_data;
59 AVStream *st;
60 int64_t pos, size, timestamp;
61 uint32_t end_index, i;
62 int ret;
63
64 ✗ if (frame_type == AVMEDIA_TYPE_VIDEO) {
65 ✗ end_index = ifv->total_vframes;
66 ✗ st = s->streams[ifv->video_stream_index];
67 } else {
68 ✗ end_index = ifv->total_aframes;
69 ✗ st = s->streams[ifv->audio_stream_index];
70 }
71
72 ✗ for (i = start_index; i < end_index; i++) {
73 ✗ if (avio_feof(s->pb))
74 ✗ return AVERROR_EOF;
75 ✗ pos = avio_rl32(s->pb);
76 ✗ size = avio_rl32(s->pb);
77
78 ✗ avio_skip(s->pb, 8);
79 ✗ timestamp = avio_rl32(s->pb);
80
81 ✗ ret = av_add_index_entry(st, pos, timestamp, size, 0, 0);
82 ✗ if (ret < 0)
83 ✗ return ret;
84
85 ✗ avio_skip(s->pb, frame_type == AVMEDIA_TYPE_VIDEO ? 8: 4);
86 }
87
88 ✗ return 0;
89 }
90
91 ✗ static int parse_header(AVFormatContext *s)
92 {
93 ✗ IFVContext *ifv = s->priv_data;
94 uint32_t aud_magic;
95 uint32_t vid_magic;
96
97 ✗ avio_skip(s->pb, 0x34);
98 ✗ ff_dict_set_timestamp(&s->metadata, "creation_time", avio_rl32(s->pb) * 1000000LL);
99 ✗ avio_skip(s->pb, 0x24);
100
101 ✗ ifv->width = avio_rl16(s->pb);
102 ✗ ifv->height = avio_rl16(s->pb);
103
104 ✗ avio_skip(s->pb, 0x8);
105 ✗ vid_magic = avio_rl32(s->pb);
106
107 ✗ if (vid_magic != MKTAG('H','2','6','4'))
108 ✗ avpriv_request_sample(s, "Unknown video codec %x", vid_magic);
109
110 ✗ avio_skip(s->pb, 0x2c);
111 ✗ ifv->sample_rate = avio_rl32(s->pb);
112 ✗ aud_magic = avio_rl32(s->pb);
113
114 ✗ if (aud_magic == MKTAG('G','R','A','W')) {
115 ✗ ifv->is_audio_present = 1;
116 ✗ } else if (aud_magic == MKTAG('P','C','M','U')) {
117 ✗ ifv->is_audio_present = 0;
118 } else {
119 ✗ avpriv_request_sample(s, "Unknown audio codec %x", aud_magic);
120 }
121
122 ✗ avio_skip(s->pb, 0x44);
123 ✗ ifv->total_vframes = avio_rl32(s->pb);
124 ✗ ifv->total_aframes = avio_rl32(s->pb);
125
126 ✗ return 0;
127 }
128
129 ✗ static int ifv_read_header(AVFormatContext *s)
130 {
131 ✗ IFVContext *ifv = s->priv_data;
132 AVStream *st;
133 int ret;
134
135 ✗ ret = parse_header(s);
136 ✗ if (ret < 0)
137 ✗ return ret;
138
139 ✗ st = avformat_new_stream(s, NULL);
140 ✗ if (!st)
141 ✗ return AVERROR(ENOMEM);
142
143 ✗ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
144 ✗ st->codecpar->codec_id = AV_CODEC_ID_H264;
145 ✗ st->codecpar->width = ifv->width;
146 ✗ st->codecpar->height = ifv->height;
147 ✗ st->start_time = 0;
148 ✗ ifv->video_stream_index = st->index;
149
150 ✗ avpriv_set_pts_info(st, 32, 1, 1000);
151
152 ✗ if (ifv->is_audio_present) {
153 ✗ st = avformat_new_stream(s, NULL);
154 ✗ if (!st)
155 ✗ return AVERROR(ENOMEM);
156
157 ✗ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
158 ✗ st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
159 ✗ st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
160 ✗ st->codecpar->sample_rate = ifv->sample_rate;
161 ✗ ifv->audio_stream_index = st->index;
162
163 ✗ avpriv_set_pts_info(st, 32, 1, 1000);
164 }
165
166 /*read video index*/
167 ✗ avio_seek(s->pb, 0xf8, SEEK_SET);
168
169 ✗ ret = read_index(s, AVMEDIA_TYPE_VIDEO, 0);
170 ✗ if (ret < 0)
171 ✗ return ret;
172
173 ✗ if (ifv->is_audio_present) {
174 /*read audio index*/
175 ✗ avio_seek(s->pb, 0x14918, SEEK_SET);
176
177 ✗ ret = read_index(s, AVMEDIA_TYPE_AUDIO, 0);
178 ✗ if (ret < 0)
179 ✗ return ret;
180 }
181
182 ✗ ifv->next_video_index = 0;
183 ✗ ifv->next_audio_index = 0;
184
185 ✗ return 0;
186 }
187
188 ✗ static int ifv_read_packet(AVFormatContext *s, AVPacket *pkt)
189 {
190 ✗ IFVContext *ifv = s->priv_data;
191 AVIndexEntry *ev, *ea, *e_next;
192 int ret;
193
194 ✗ ev = ea = e_next = NULL;
195
196 ✗ if (ifv->next_video_index < ifv->total_vframes) {
197 ✗ AVStream *const st = s->streams[ifv->video_stream_index];
198 ✗ FFStream *const sti = ffstream(st);
199
200 ✗ if (ifv->next_video_index < sti->nb_index_entries)
201 ✗ e_next = ev = &sti->index_entries[ifv->next_video_index];
202 }
203
204 ✗ if (ifv->is_audio_present &&
205 ✗ ifv->next_audio_index < ifv->total_aframes) {
206 ✗ AVStream *const st = s->streams[ifv->audio_stream_index];
207 ✗ FFStream *const sti = ffstream(st);
208
209 ✗ if (ifv->next_audio_index < sti->nb_index_entries) {
210 ✗ ea = &sti->index_entries[ifv->next_audio_index];
211 ✗ if (!ev || ea->timestamp < ev->timestamp)
212 ✗ e_next = ea;
213 }
214 }
215
216 ✗ if (!ev) {
217 uint64_t vframes, aframes;
218 ✗ if (ifv->is_audio_present && !ea) {
219 /*read new video and audio indexes*/
220
221 ✗ ifv->next_video_index = ifv->total_vframes;
222 ✗ ifv->next_audio_index = ifv->total_aframes;
223
224 ✗ avio_skip(s->pb, 0x1c);
225 ✗ vframes = ifv->total_vframes + (uint64_t)avio_rl32(s->pb);
226 ✗ aframes = ifv->total_aframes + (uint64_t)avio_rl32(s->pb);
227 ✗ if (vframes > INT_MAX || aframes > INT_MAX)
228 ✗ return AVERROR_INVALIDDATA;
229 ✗ ifv->total_vframes = vframes;
230 ✗ ifv->total_aframes = aframes;
231 ✗ avio_skip(s->pb, 0xc);
232
233 ✗ if (avio_feof(s->pb))
234 ✗ return AVERROR_EOF;
235
236 ✗ ret = read_index(s, AVMEDIA_TYPE_VIDEO, ifv->next_video_index);
237 ✗ if (ret < 0)
238 ✗ return ret;
239
240 ✗ ret = read_index(s, AVMEDIA_TYPE_AUDIO, ifv->next_audio_index);
241 ✗ if (ret < 0)
242 ✗ return ret;
243
244 ✗ return 0;
245
246 ✗ } else if (!ifv->is_audio_present) {
247 /*read new video index*/
248
249 ✗ ifv->next_video_index = ifv->total_vframes;
250
251 ✗ avio_skip(s->pb, 0x1c);
252 ✗ vframes = ifv->total_vframes + (uint64_t)avio_rl32(s->pb);
253 ✗ if (vframes > INT_MAX)
254 ✗ return AVERROR_INVALIDDATA;
255 ✗ ifv->total_vframes = vframes;
256 ✗ avio_skip(s->pb, 0x10);
257
258 ✗ if (avio_feof(s->pb))
259 ✗ return AVERROR_EOF;
260
261 ✗ ret = read_index(s, AVMEDIA_TYPE_VIDEO, ifv->next_video_index);
262 ✗ if (ret < 0)
263 ✗ return ret;
264
265 ✗ return 0;
266 }
267 }
268
269 ✗ if (!e_next) return AVERROR_EOF;
270
271 ✗ avio_seek(s->pb, e_next->pos, SEEK_SET);
272 ✗ ret = av_get_packet(s->pb, pkt, e_next->size);
273 ✗ if (ret < 0)
274 ✗ return ret;
275
276 ✗ if (e_next == ev) {
277 ✗ ifv->next_video_index++;
278 ✗ pkt->stream_index = ifv->video_stream_index;
279 } else {
280 ✗ ifv->next_audio_index++;
281 ✗ pkt->stream_index = ifv->audio_stream_index;
282 }
283
284 ✗ pkt->pts = e_next->timestamp;
285 ✗ pkt->pos = e_next->pos;
286
287 ✗ return 0;
288 }
289
290 ✗ static int ifv_read_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags)
291 {
292 ✗ IFVContext *ifv = s->priv_data;
293
294 ✗ for (unsigned i = 0; i < s->nb_streams; i++) {
295 ✗ int index = av_index_search_timestamp(s->streams[i], ts, AVSEEK_FLAG_ANY);
296 ✗ if (index < 0) {
297 ✗ ifv->next_video_index = ifv->total_vframes - 1;
298 ✗ ifv->next_audio_index = ifv->total_aframes - 1;
299 ✗ return 0;
300 }
301
302 ✗ if (i == ifv->video_stream_index) {
303 ✗ ifv->next_video_index = index;
304 } else {
305 ✗ ifv->next_audio_index = index;
306 }
307 }
308
309 ✗ return 0;
310 }
311
312 const FFInputFormat ff_ifv_demuxer = {
313 .p.name = "ifv",
314 .p.long_name = NULL_IF_CONFIG_SMALL("IFV CCTV DVR"),
315 .p.extensions = "ifv",
316 .priv_data_size = sizeof(IFVContext),
317 .read_probe = ifv_probe,
318 .read_header = ifv_read_header,
319 .read_packet = ifv_read_packet,
320 .read_seek = ifv_read_seek,
321 };
322