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

Submitted by swarajhota353@gmail.com on May 25, 2019, 10:21 a.m.

Details

Message ID 20190525102131.26071-1-swarajhota353@gmail.com
State New
Headers show

Commit Message

swarajhota353@gmail.com May 25, 2019, 10:21 a.m.
Fixes ticket #2956.

Signed-off-by: Swaraj Hota <swarajhota353@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

Comments

Paul B Mahol May 25, 2019, 11:59 a.m.
On 5/25/19, Swaraj Hota <swarajhota353@gmail.com> wrote:
> Fixes ticket #2956.
>
> Signed-off-by: Swaraj Hota <swarajhota353@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;

I do not think this will work.
The video and audio index are not guaranteed to be same
for same timestamp.

> +
> +    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
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Peter Ross May 25, 2019, 1:01 p.m.
On Sat, May 25, 2019 at 01:59:44PM +0200, Paul B Mahol wrote:
> On 5/25/19, Swaraj Hota <swarajhota353@gmail.com> wrote:
> > Fixes ticket #2956.
> >
> > Signed-off-by: Swaraj Hota <swarajhota353@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.

more comments below.

> > ---
> >  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);

add space between symobl and ?

> > +    }
> > +
> > +    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);

avpriv_request_sample: no need for for \n newline

> > +
> > +    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);

same

> > +    }
> > +
> > +    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;
> > +        }

the audio-present and no-audio-present cases are almost identical.
could easily be combined.

this perhaps a style issue, but why have nb_new_a/vframes. the bits can be read and incremented one line,

> > +    }
> > +
> > +    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) {

the (e_next == ea) case always evaluates to true

> > +        ifv->next_audio_index++;
> > +        pkt->stream_index = ifv->audio_stream_index;
> > +    }
> > +
> > +    pkt->pts = e_next->timestamp;
> > +    pkt->pos = e_next->pos;
> > +
> > +    return ret;

here you just need to return 0;

> > +}
> > +
> > +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;
> 
> I do not think this will work.
> The video and audio index are not guaranteed to be same
> for same timestamp.
> 
> > +
> > +    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);
> > +    }

freep is not required i think. avformat/utils does this automatically.

> > +    return 0;
> > +}
> > +
> > +AVInputFormat ff_ifv_demuxer = {
> > +    .name           = "ifv",
> > +    .long_name      = NULL_IF_CONFIG_SMALL("IFV CCTV DVR"),

if IFV has a meaning, please expand the acronym and put it in long_name.

> > +    .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,
> > +};

cheers,

-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)

Patch hide | download patch | download mbox

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, \