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