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