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