FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/ifv.c
Date: 2025-06-01 09:29:47
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 7235 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 7235 times.
7235 if (!memcmp(p->buf, ifv_magic, sizeof(ifv_magic)))
49 return AVPROBE_SCORE_MAX;
50
51 7235 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