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

Submitted by swarajhota353@gmail.com on May 25, 2019, 8:16 p.m.

Details

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

Commit Message

swarajhota353@gmail.com May 25, 2019, 8:16 p.m.
Fixes ticket #2956.

Signed-off-by: Swaraj Hota <swarajhota353@gmail.com>
---
Minor changes based on previous discussions.
Seeking is fixed.
---
 Changelog                |   1 +
 libavformat/Makefile     |   1 +
 libavformat/allformats.c |   1 +
 libavformat/ifv.c        | 304 +++++++++++++++++++++++++++++++++++++++
 libavformat/version.h    |   4 +-
 5 files changed, 309 insertions(+), 2 deletions(-)
 create mode 100644 libavformat/ifv.c

Comments

Reimar Döffinger May 26, 2019, 7:44 p.m.
On Sun, May 26, 2019 at 01:46:32AM +0530, Swaraj Hota wrote:
> +    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;

I suspect that it would have been ok to
just assume/assert this will always be 0.

> +static int ifv_read_packet(AVFormatContext *s, AVPacket *pkt)

As far as I can tell, you just choose between audio
and video by the closest timestamp.
While this might be ok considering the limited importance
and uses of the format, it is rather simplistic.
I do not know if it follows the latest best practices,
but mov_find_next_sample is an example of a more
sophisticated way.
It takes into account whether the underlying transport
has issues seeking (like http, or even worse piped input)
and in that case prefers file position over timestamp
position.
It also avoids wasting time reading streams marked as
AVDISCARD_ALL (in mov_read_packet is that check).
Maybe others following the project more closely
can give additional/better best practice tips.
swarajhota353@gmail.com May 29, 2019, 5:51 p.m.
On Tue, May 28, 2019 at 11:30:13PM +0200, Reimar Döffinger wrote:
> Hi!
> Did you intentionally not send to the list?

Okay I just realised I have been replying personally to all comments
whereas I should have sent them to the list ':D

> 
> On 28.05.2019, at 17:32, Swaraj Hota <swarajhota353@gmail.com> wrote:
> 
> > On Sun, May 26, 2019 at 09:44:35PM +0200, Reimar Döffinger wrote:
> >> On Sun, May 26, 2019 at 01:46:32AM +0530, Swaraj Hota wrote:
> >>> +    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;
> >> 
> >> I suspect that it would have been ok to
> >> just assume/assert this will always be 0.
> >> 
> > 
> > Okay, but I didn't get what exact change are you suggesting? Should 
> > I replace st->index with 0 here? Or should I get rid of
> > ifv->video/audio_stream_index variables and instead use 0 and 1
> > everywhere?
> 
> I think it's fine, now that you already wrote the code.
> Just saying that you probably didn't really have to implement quite as much.
> 

Will keep that in mind :)

> >>> +static int ifv_read_packet(AVFormatContext *s, AVPacket *pkt)
> >> 
> >> As far as I can tell, you just choose between audio
> >> and video by the closest timestamp.
> > 
> > Yes.
> > 
> >> While this might be ok considering the limited importance
> >> and uses of the format, it is rather simplistic.
> >> I do not know if it follows the latest best practices,
> >> but mov_find_next_sample is an example of a more
> >> sophisticated way.
> >> It takes into account whether the underlying transport
> >> has issues seeking (like http, or even worse piped input)
> >> and in that case prefers file position over timestamp
> >> position.
> >> It also avoids wasting time reading streams marked as
> >> AVDISCARD_ALL (in mov_read_packet is that check).
> >> Maybe others following the project more closely
> >> can give additional/better best practice tips.
> > 
> > Okay, I get that, but as you said considering the limited importance and
> > uses of the format, we must also consider if it is worth all the effort,
> > cuz it can also get more messy ':D Besides I have been working on this
> > format since over a month or two now, and understanding, writing and debugging 
> > this change might take quite some time ':D
> > 
> > I have been trying to comply with all good practices but I think a working 
> > demuxer is quite enough for a minority format like this (e.g. even the
> > dhav format is quite simplistic), but again I can't say that, 
> > if others agree that it is a necessity I will work on it.
> 
> Entirely depends on the purpose.
> If the aim is to have a working demuxer, I think it is acceptable from what I looked at.
> I guess the only concern might be that not supporting piped input would be a bit of a regression from the first patch.

Is there another simpler way to support piped input? Can you explain a
bit?

> If the aim is to learn and possibly tackle more involved formats it might be useful. But even then maybe rather as a separate patch later...
> 

Actually another reason is that I now have to focus on my gsoc project,
so yeah, a separate patch later seems like a good idea to me :)

Thank you.

Swaraj
Reimar Döffinger May 30, 2019, 6:33 p.m.
On 29.05.2019, at 19:51, Swaraj Hota <swarajhota353@gmail.com> wrote:
>> 
>> Entirely depends on the purpose.
>> If the aim is to have a working demuxer, I think it is acceptable from what I looked at.
>> I guess the only concern might be that not supporting piped input would be a bit of a regression from the first patch.
> 
> Is there another simpler way to support piped input? Can you explain a
> bit?

The mov demuxer code isn't really all that complicated.
The basic idea is: if the input is not seekable, decided whether to read audio or video based on which one has the lowest file position (a bit like your original patch, but using it only for the "audio or video next" decision.
If input is seekable, pick the stream with the lowest next timestamp as your patch does now.
It should be not that complicated in principle, but the details can make it a pain sometimes.
swarajhota353@gmail.com May 31, 2019, 9:12 a.m.
On Thu, May 30, 2019 at 08:33:35PM +0200, Reimar Döffinger wrote:
> 
> 
> On 29.05.2019, at 19:51, Swaraj Hota <swarajhota353@gmail.com> wrote:
> >> 
> >> Entirely depends on the purpose.
> >> If the aim is to have a working demuxer, I think it is acceptable from what I looked at.
> >> I guess the only concern might be that not supporting piped input would be a bit of a regression from the first patch.
> > 
> > Is there another simpler way to support piped input? Can you explain a
> > bit?
> 
> The mov demuxer code isn't really all that complicated.
> The basic idea is: if the input is not seekable, decided whether to read audio or video based on which one has the lowest file position (a bit like your original patch, but using it only for the "audio or video next" decision.
> If input is seekable, pick the stream with the lowest next timestamp as your patch does now.
> It should be not that complicated in principle, but the details can make it a pain sometimes.

Okay, I think I get the basic idea now. I will surely try to implement
this later in another patch. Thanx a lot!

Swaraj
swarajhota353@gmail.com June 2, 2019, 10:36 p.m.
On Sun, May 26, 2019 at 01:46:32AM +0530, Swaraj Hota wrote:
> Fixes ticket #2956.
> 
> Signed-off-by: Swaraj Hota <swarajhota353@gmail.com>
> ---
> Minor changes based on previous discussions.
> Seeking is fixed.
> ---
>  Changelog                |   1 +
>  libavformat/Makefile     |   1 +
>  libavformat/allformats.c |   1 +
>  libavformat/ifv.c        | 304 +++++++++++++++++++++++++++++++++++++++
>  libavformat/version.h    |   4 +-
>  5 files changed, 309 insertions(+), 2 deletions(-)
>  create mode 100644 libavformat/ifv.c
> 

Is the patch okay now? Can it be merged?
Peter Ross June 9, 2019, 11:36 a.m.
On Mon, Jun 03, 2019 at 04:06:10AM +0530, Swaraj Hota wrote:
> On Sun, May 26, 2019 at 01:46:32AM +0530, Swaraj Hota wrote:
> > Fixes ticket #2956.
> > 
> > Signed-off-by: Swaraj Hota <swarajhota353 at gmail.com>
> > ---
> > Minor changes based on previous discussions.
> > Seeking is fixed.
> > ---
> >  Changelog                |   1 +
> >  libavformat/Makefile     |   1 +
> >  libavformat/allformats.c |   1 +
> >  libavformat/ifv.c        | 304 +++++++++++++++++++++++++++++++++++++++
> >  libavformat/version.h    |   4 +-
> >  5 files changed, 309 insertions(+), 2 deletions(-)
> >  create mode 100644 libavformat/ifv.c
> > 
> 
> Is the patch okay now? Can it be merged?

i don't have any further comments. recommend commit.

-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)
Peter Ross June 9, 2019, 11:39 a.m.
On Sun, Jun 09, 2019 at 09:36:33PM +1000, Peter Ross wrote:
> On Mon, Jun 03, 2019 at 04:06:10AM +0530, Swaraj Hota wrote:
> > On Sun, May 26, 2019 at 01:46:32AM +0530, Swaraj Hota wrote:
> > > Fixes ticket #2956.
> > > 
> > > Signed-off-by: Swaraj Hota <swarajhota353 at gmail.com>
> > > ---
> > > Minor changes based on previous discussions.
> > > Seeking is fixed.
> > > ---
> > >  Changelog                |   1 +
> > >  libavformat/Makefile     |   1 +
> > >  libavformat/allformats.c |   1 +
> > >  libavformat/ifv.c        | 304 +++++++++++++++++++++++++++++++++++++++
> > >  libavformat/version.h    |   4 +-
> > >  5 files changed, 309 insertions(+), 2 deletions(-)
> > >  create mode 100644 libavformat/ifv.c
> > > 
> > 
> > Is the patch okay now? Can it be merged?
> 
> i don't have any further comments. recommend commit.

scrub that, an entry for the IFV format in missing in 'doc/general.texi'.

-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)
swarajhota353@gmail.com June 9, 2019, 1:46 p.m.
On Sun 9 Jun, 2019, 5:09 PM Peter Ross, <pross@xvid.org> wrote:

> On Sun, Jun 09, 2019 at 09:36:33PM +1000, Peter Ross wrote:
> > On Mon, Jun 03, 2019 at 04:06:10AM +0530, Swaraj Hota wrote:
> > > On Sun, May 26, 2019 at 01:46:32AM +0530, Swaraj Hota wrote:
> > > > Fixes ticket #2956.
> > > >
> > > > Signed-off-by: Swaraj Hota <swarajhota353 at gmail.com>
> > > > ---
> > > > Minor changes based on previous discussions.
> > > > Seeking is fixed.
> > > > ---
> > > >  Changelog                |   1 +
> > > >  libavformat/Makefile     |   1 +
> > > >  libavformat/allformats.c |   1 +
> > > >  libavformat/ifv.c        | 304
> +++++++++++++++++++++++++++++++++++++++
> > > >  libavformat/version.h    |   4 +-
> > > >  5 files changed, 309 insertions(+), 2 deletions(-)
> > > >  create mode 100644 libavformat/ifv.c
> > > >
> > >
> > > Is the patch okay now? Can it be merged?
> >
> > i don't have any further comments. recommend commit.
>
> scrub that, an entry for the IFV format in missing in 'doc/general.texi'.
>

Sorry I didn't know it was required. I'll add that. Mention if any other
changes are required.

Thank you!

Swaraj

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..03c682bb9d
--- /dev/null
+++ b/libavformat/ifv.c
@@ -0,0 +1,304 @@ 
+/*
+ * 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", 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", 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;
+    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*/
+
+            ifv->next_video_index = ifv->total_vframes;
+            ifv->next_audio_index = ifv->total_aframes;
+
+            avio_skip(s->pb, 0x1c);
+            ifv->total_vframes += avio_rl32(s->pb);
+            ifv->total_aframes += avio_rl32(s->pb);
+            avio_skip(s->pb, 0xc);
+
+            if (avio_feof(s->pb))
+                return AVERROR_EOF;
+
+            ret = read_index(s, AVMEDIA_TYPE_VIDEO, ifv->next_video_index);
+            if (ret < 0)
+                return ret;
+
+            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*/
+
+            ifv->next_video_index = ifv->total_vframes;
+
+            avio_skip(s->pb, 0x1c);
+            ifv->total_vframes += avio_rl32(s->pb);
+            avio_skip(s->pb, 0x10);
+
+            if (avio_feof(s->pb))
+                return AVERROR_EOF;
+
+            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 {
+        ifv->next_audio_index++;
+        pkt->stream_index = ifv->audio_stream_index;
+    }
+
+    pkt->pts = e_next->timestamp;
+    pkt->pos = e_next->pos;
+
+    return 0;
+}
+
+static int ifv_read_seek(AVFormatContext *s, int stream_index, int64_t ts, int flags)
+{
+    IFVContext *ifv = s->priv_data;
+
+    for (unsigned i = 0; i < s->nb_streams; i++) {
+        int index = av_index_search_timestamp(s->streams[i], 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;
+        }
+
+        if (i == ifv->video_stream_index) {
+            ifv->next_video_index = index;
+        } else {
+            ifv->next_audio_index = index;
+        }
+    }
+
+    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,
+};
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, \