[FFmpeg-devel] [PATCH v4] avformat/ifv: added support for ifv cctv files

Swaraj Hota swarajhota353 at gmail.com
Sat May 25 13:21:32 EEST 2019


Fixes ticket #2956.

Signed-off-by: Swaraj Hota <swarajhota353 at gmail.com>
---
Changes made based on previous discussions.

Now the demuxer is working pretty much as the original dvr player does.
Framerate is based on timestamps (hence correct). Seeking is working for
all files without any issue.
---
 Changelog                |   1 +
 libavformat/Makefile     |   1 +
 libavformat/allformats.c |   1 +
 libavformat/ifv.c        | 318 +++++++++++++++++++++++++++++++++++++++
 libavformat/version.h    |   4 +-
 5 files changed, 323 insertions(+), 2 deletions(-)
 create mode 100644 libavformat/ifv.c

diff --git a/Changelog b/Changelog
index e6b209ae0a..e0b27657d7 100644
--- a/Changelog
+++ b/Changelog
@@ -30,6 +30,7 @@ version <next>:
 - colorhold filter
 - xmedian filter
 - asr filter
+- IFV demuxer
 
 
 version 4.1:
diff --git a/libavformat/Makefile b/libavformat/Makefile
index df87c54a58..a434b005a4 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -231,6 +231,7 @@ OBJS-$(CONFIG_ICO_MUXER)                 += icoenc.o
 OBJS-$(CONFIG_IDCIN_DEMUXER)             += idcin.o
 OBJS-$(CONFIG_IDF_DEMUXER)               += bintext.o sauce.o
 OBJS-$(CONFIG_IFF_DEMUXER)               += iff.o
+OBJS-$(CONFIG_IFV_DEMUXER)               += ifv.o
 OBJS-$(CONFIG_ILBC_DEMUXER)              += ilbc.o
 OBJS-$(CONFIG_ILBC_MUXER)                += ilbc.o
 OBJS-$(CONFIG_IMAGE2_DEMUXER)            += img2dec.o img2.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index d316a0529a..cd00834807 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -188,6 +188,7 @@ extern AVOutputFormat ff_ico_muxer;
 extern AVInputFormat  ff_idcin_demuxer;
 extern AVInputFormat  ff_idf_demuxer;
 extern AVInputFormat  ff_iff_demuxer;
+extern AVInputFormat  ff_ifv_demuxer;
 extern AVInputFormat  ff_ilbc_demuxer;
 extern AVOutputFormat ff_ilbc_muxer;
 extern AVInputFormat  ff_image2_demuxer;
diff --git a/libavformat/ifv.c b/libavformat/ifv.c
new file mode 100644
index 0000000000..517f0252f5
--- /dev/null
+++ b/libavformat/ifv.c
@@ -0,0 +1,318 @@
+/*
+ * IFV demuxer
+ *
+ * Copyright (c) 2019 Swaraj Hota
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "internal.h"
+#include "avio_internal.h"
+
+
+typedef struct IFVContext {
+    uint32_t next_video_index;
+    uint32_t next_audio_index;
+    uint32_t total_vframes;
+    uint32_t total_aframes;
+
+    int width, height;
+    int is_audio_present;
+    int sample_rate;
+
+    int video_stream_index;
+    int audio_stream_index;
+} IFVContext;
+
+static int ifv_probe(const AVProbeData *p)
+{
+    static const uint8_t ifv_magic[] = {0x11, 0xd2, 0xd3, 0xab, 0xba, 0xa9,
+        0xcf, 0x11, 0x8e, 0xe6, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65, 0x44};
+
+    if (!memcmp(p->buf, ifv_magic, sizeof(ifv_magic)))
+        return AVPROBE_SCORE_MAX;
+
+    return 0;
+}
+
+static int read_index(AVFormatContext *s,
+                      enum AVMediaType frame_type,
+                      uint32_t start_index)
+{
+    IFVContext *ifv = s->priv_data;
+    AVStream *st;
+    int64_t pos, size, timestamp;
+    uint32_t end_index, i;
+    int ret;
+
+    if (frame_type == AVMEDIA_TYPE_VIDEO) {
+        end_index = ifv->total_vframes;
+        st = s->streams[ifv->video_stream_index];
+    } else {
+        end_index = ifv->total_aframes;
+        st = s->streams[ifv->audio_stream_index];
+    }
+
+    for (i = start_index; i < end_index; i++) {
+        pos = avio_rl32(s->pb);
+        size = avio_rl32(s->pb);
+
+        avio_skip(s->pb, 8);
+        timestamp = avio_rl32(s->pb);
+
+        ret = av_add_index_entry(st, pos, timestamp, size, 0, 0);
+        if (ret < 0)
+            return ret;
+
+        avio_skip(s->pb, frame_type == AVMEDIA_TYPE_VIDEO? 8: 4);
+    }
+
+    return 0;
+}
+
+static int parse_header(AVFormatContext *s)
+{
+    IFVContext *ifv = s->priv_data;
+    uint32_t aud_magic;
+    uint32_t vid_magic;
+
+    avio_skip(s->pb, 0x5c);
+    ifv->width = avio_rl16(s->pb);
+    ifv->height = avio_rl16(s->pb);
+
+    avio_skip(s->pb, 0x8);
+    vid_magic = avio_rl32(s->pb);
+
+    if (vid_magic != MKTAG('H','2','6','4'))
+        avpriv_request_sample(s, "Unknown video codec %x\n", vid_magic);
+
+    avio_skip(s->pb, 0x2c);
+    ifv->sample_rate = avio_rl32(s->pb);
+    aud_magic = avio_rl32(s->pb);
+
+    if (aud_magic == MKTAG('G','R','A','W')) {
+        ifv->is_audio_present = 1;
+    } else if (aud_magic == MKTAG('P','C','M','U')) {
+        ifv->is_audio_present = 0;
+    } else {
+        avpriv_request_sample(s, "Unknown audio codec %x\n", aud_magic);
+    }
+
+    avio_skip(s->pb, 0x44);
+    ifv->total_vframes = avio_rl32(s->pb);
+    ifv->total_aframes = avio_rl32(s->pb);
+
+    return 0;
+}
+
+static int ifv_read_header(AVFormatContext *s)
+{
+    IFVContext *ifv = s->priv_data;
+    AVStream *st;
+    int ret;
+
+    ret = parse_header(s);
+    if (ret < 0)
+        return ret;
+
+    st = avformat_new_stream(s, NULL);
+    if (!st)
+        return AVERROR(ENOMEM);
+
+    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+    st->codecpar->codec_id = AV_CODEC_ID_H264;
+    st->codecpar->width = ifv->width;
+    st->codecpar->height = ifv->height;
+    st->start_time = 0;
+    ifv->video_stream_index = st->index;
+
+    avpriv_set_pts_info(st, 32, 1, 1000);
+
+    if (ifv->is_audio_present) {
+        st = avformat_new_stream(s, NULL);
+        if (!st)
+            return AVERROR(ENOMEM);
+
+        st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+        st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
+        st->codecpar->channels = 1;
+        st->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+        st->codecpar->sample_rate = ifv->sample_rate;
+        ifv->audio_stream_index = st->index;
+
+        avpriv_set_pts_info(st, 32, 1, 1000);
+    }
+
+    /*read video index*/
+    avio_seek(s->pb, 0xf8, SEEK_SET);
+
+    ret = read_index(s, AVMEDIA_TYPE_VIDEO, 0);
+    if (ret < 0)
+        return ret;
+
+    if (ifv->is_audio_present) {
+        /*read audio index*/
+        avio_seek(s->pb, 0x14918, SEEK_SET);
+
+        ret = read_index(s, AVMEDIA_TYPE_AUDIO, 0);
+        if (ret < 0)
+            return ret;
+    }
+
+    ifv->next_video_index = 0;
+    ifv->next_audio_index = 0;
+
+    return 0;
+}
+
+static int ifv_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    IFVContext *ifv = s->priv_data;
+    AVStream *st;
+    AVIndexEntry *ev, *ea, *e_next;
+    uint32_t nb_new_vframes, nb_new_aframes;
+    int ret;
+
+    ev = ea = e_next = NULL;
+
+    if (ifv->next_video_index < ifv->total_vframes) {
+        st = s->streams[ifv->video_stream_index];
+        if (ifv->next_video_index < st->nb_index_entries)
+            e_next = ev = &st->index_entries[ifv->next_video_index];
+    }
+
+    if (ifv->is_audio_present &&
+        ifv->next_audio_index < ifv->total_aframes) {
+        st = s->streams[ifv->audio_stream_index];
+        if (ifv->next_audio_index < st->nb_index_entries) {
+            ea = &st->index_entries[ifv->next_audio_index];
+            if (!ev || ea->timestamp < ev->timestamp)
+                e_next = ea;
+        }
+    }
+
+    if (!ev) {
+        if (ifv->is_audio_present && !ea) {
+            /*read new video and audio indexes*/
+
+            avio_skip(s->pb, 0x1c);
+            nb_new_vframes = avio_rl32(s->pb);
+            nb_new_aframes = avio_rl32(s->pb);
+            avio_skip(s->pb, 0xc);
+
+            if (avio_feof(s->pb))
+                return AVERROR_EOF;
+
+            ifv->next_video_index = ifv->total_vframes;
+            ifv->total_vframes += nb_new_vframes;
+
+            ret = read_index(s, AVMEDIA_TYPE_VIDEO, ifv->next_video_index);
+            if (ret < 0)
+                return ret;
+
+            ifv->next_audio_index = ifv->total_aframes;
+            ifv->total_aframes += nb_new_aframes;
+
+            ret = read_index(s, AVMEDIA_TYPE_AUDIO, ifv->next_audio_index);
+            if (ret < 0)
+                return ret;
+
+            return 0;
+
+        } else if (!ifv->is_audio_present) {
+            /*read new video index*/
+
+            avio_skip(s->pb, 0x1c);
+            nb_new_vframes = avio_rl32(s->pb);
+            avio_skip(s->pb, 0x10);
+
+            if (avio_feof(s->pb))
+                return AVERROR_EOF;
+
+            ifv->next_video_index = ifv->total_vframes;
+            ifv->total_vframes += nb_new_vframes;
+
+            ret = read_index(s, AVMEDIA_TYPE_VIDEO, ifv->next_video_index);
+            if (ret < 0)
+                return ret;
+
+            return 0;
+        }
+    }
+
+    if (!e_next) return AVERROR_EOF;
+
+    avio_seek(s->pb, e_next->pos, SEEK_SET);
+    ret = av_get_packet(s->pb, pkt, e_next->size);
+    if (ret < 0)
+        return ret;
+
+    if (e_next == ev) {
+        ifv->next_video_index++;
+        pkt->stream_index = ifv->video_stream_index;
+    } else if (e_next == ea) {
+        ifv->next_audio_index++;
+        pkt->stream_index = ifv->audio_stream_index;
+    }
+
+    pkt->pts = e_next->timestamp;
+    pkt->pos = e_next->pos;
+
+    return ret;
+}
+
+static int ifv_read_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags)
+{
+    IFVContext *ifv = s->priv_data;
+    AVStream *st = s->streams[0];
+
+    int index = av_index_search_timestamp(st, ts, AVSEEK_FLAG_ANY);
+    if (index < 0) {
+        ifv->next_video_index = ifv->total_vframes - 1;
+        ifv->next_audio_index = ifv->total_aframes - 1;
+        return 0;
+    }
+
+    ifv->next_video_index = index;
+    ifv->next_audio_index = index;
+
+    return 0;
+}
+
+static int ifv_read_close(AVFormatContext *s)
+{
+    AVStream *st;
+    unsigned int i;
+    for (i = 0; i < s->nb_streams; i++) {
+        st = s->streams[i];
+        av_freep(&st->index_entries);
+    }
+    return 0;
+}
+
+AVInputFormat ff_ifv_demuxer = {
+    .name           = "ifv",
+    .long_name      = NULL_IF_CONFIG_SMALL("IFV CCTV DVR"),
+    .priv_data_size = sizeof(IFVContext),
+    .extensions     = "ifv",
+    .read_probe     = ifv_probe,
+    .read_header    = ifv_read_header,
+    .read_packet    = ifv_read_packet,
+    .read_seek      = ifv_read_seek,
+    .read_close     = ifv_read_close,
+};
diff --git a/libavformat/version.h b/libavformat/version.h
index 150a72e27d..52dd95f5c6 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,8 +32,8 @@
 // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
 // Also please add any ticket numbers that you believe might be affected here
 #define LIBAVFORMAT_VERSION_MAJOR  58
-#define LIBAVFORMAT_VERSION_MINOR  27
-#define LIBAVFORMAT_VERSION_MICRO 103
+#define LIBAVFORMAT_VERSION_MINOR  28
+#define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
-- 
2.21.0



More information about the ffmpeg-devel mailing list