• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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