[FFmpeg-devel] [PATCH 1/3] avformat: add moflex demuxer

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Tue Aug 25 13:38:28 EEST 2020


Paul B Mahol:
> On 8/25/20, Andreas Rheinhardt <andreas.rheinhardt at gmail.com> wrote:
>> Paul B Mahol:
>>> Signed-off-by: Paul B Mahol <onemda at gmail.com>
>>> ---
>>> [WIP] Missing video decoder.
>>> ---
>>>  libavformat/Makefile     |   1 +
>>>  libavformat/allformats.c |   1 +
>>>  libavformat/moflex.c     | 291 +++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 293 insertions(+)
>>>  create mode 100644 libavformat/moflex.c
>>>
>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>> index cbb33fe37c..1e0ac317e5 100644
>>> --- a/libavformat/Makefile
>>> +++ b/libavformat/Makefile
>>> @@ -319,6 +319,7 @@ OBJS-$(CONFIG_MLV_DEMUXER)               += mlvdec.o
>>> riffdec.o
>>>  OBJS-$(CONFIG_MM_DEMUXER)                += mm.o
>>>  OBJS-$(CONFIG_MMF_DEMUXER)               += mmf.o
>>>  OBJS-$(CONFIG_MMF_MUXER)                 += mmf.o rawenc.o
>>> +OBJS-$(CONFIG_MOFLEX_DEMUXER)            += moflex.o
>>>  OBJS-$(CONFIG_MOV_DEMUXER)               += mov.o mov_chan.o mov_esds.o
>>> replaygain.o
>>>  OBJS-$(CONFIG_MOV_MUXER)                 += movenc.o av1.o avc.o hevc.o
>>> vpcc.o \
>>>                                              movenchint.o mov_chan.o rtp.o
>>> \
>>> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
>>> index 0aa9dd7198..28331facb9 100644
>>> --- a/libavformat/allformats.c
>>> +++ b/libavformat/allformats.c
>>> @@ -249,6 +249,7 @@ extern AVInputFormat  ff_mlv_demuxer;
>>>  extern AVInputFormat  ff_mm_demuxer;
>>>  extern AVInputFormat  ff_mmf_demuxer;
>>>  extern AVOutputFormat ff_mmf_muxer;
>>> +extern AVInputFormat  ff_moflex_demuxer;
>>>  extern AVInputFormat  ff_mov_demuxer;
>>>  extern AVOutputFormat ff_mov_muxer;
>>>  extern AVOutputFormat ff_mp2_muxer;
>>> diff --git a/libavformat/moflex.c b/libavformat/moflex.c
>>> new file mode 100644
>>> index 0000000000..b3728b0e49
>>> --- /dev/null
>>> +++ b/libavformat/moflex.c
>>> @@ -0,0 +1,291 @@
>>> +/*
>>> + * MOFLEX demuxer
>>> + * Copyright (c) 2020 Paul B Mahol
>>> + *
>>> + * 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"
>>> +
>>> +typedef struct BitReader {
>>> +    unsigned last;
>>> +    unsigned pos;
>>> +} BitReader;
>>> +
>>> +typedef struct MOFLEXDemuxContext {
>>> +    unsigned size;
>>> +    int64_t pos;
>>> +    int64_t ts;
>>> +    int flags;
>>> +    int in_block;
>>> +
>>> +    BitReader br;
>>> +} MOFLEXDemuxContext;
>>> +
>>> +static int pop(BitReader *br, AVIOContext *pb)
>>> +{
>>> +    if (avio_feof(pb))
>>> +        return AVERROR_EOF;
>>> +
>>> +    if ((br->pos & 7) == 0)
>>> +        br->last = (unsigned)avio_r8(pb) << 24U;
>>> +    else
>>> +        br->last <<= 1;
>>> +
>>> +    br->pos++;
>>> +    return !!(br->last & 0x80000000);
>>> +}
>>> +
>>> +static int pop_int(BitReader *br, AVIOContext *pb, int n)
>>> +{
>>> +    int value = 0;
>>> +
>>> +    for (int i = 0; i < n; i++) {
>>> +        int ret = pop(br, pb);
>>> +
>>> +        if (ret < 0)
>>> +            return ret;
>>> +        value = 2 * value + ret;
>>> +    }
>>> +
>>> +    return value;
>>> +}
>>> +
>>> +static int pop_length(BitReader *br, AVIOContext *pb)
>>> +{
>>> +    int n = 1;
>>> +
>>> +    while (!pop(br, pb))
>>> +        n++;
>>> +
>>> +    return n;
>>> +}
>>> +
>>> +static int read_var_byte(AVFormatContext *s, unsigned *out)
>>> +{
>>> +    AVIOContext *pb = s->pb;
>>> +    unsigned value = 0, data;
>>> +
>>> +    data = avio_r8(pb);
>>> +    if (!(data & 0x80)) {
>>> +        *out = data;
>>> +        return 0;
>>> +    }
>>> +
>>> +    value = (data & 0x7F) << 7;
>>> +    data = avio_r8(pb);
>>> +    if (!(data & 0x80)) {
>>> +        value |= data;
>>> +        *out = value;
>>> +        return 0;
>>> +    }
>>> +
>>> +    value = ((data & 0x7F) | value) << 7;
>>> +    data = avio_r8(pb);
>>> +    if (!(data & 0x80)) {
>>> +        value |= data;
>>> +        *out = value;
>>> +        return 0;
>>> +    }
>>> +
>>> +    value = (((data & 0x7F) | value) << 7) | avio_r8(pb);
>>> +    *out = value;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int moflex_probe(const AVProbeData *p)
>>> +{
>>> +    if (p->buf[0] != 0x4C && p->buf[1] != 0x32)
>>> +        return 0;
>>> +
>>> +    return AVPROBE_SCORE_MAX / 3;
>>> +}
>>> +
>>> +static int moflex_read_sync(AVFormatContext *s)
>>> +{
>>> +    MOFLEXDemuxContext *m = s->priv_data;
>>> +    AVIOContext *pb = s->pb;
>>> +    unsigned v10;
>>> +
>>> +    if (avio_rb16(pb) != 0x4C32) {
>>> +        avio_seek(pb, -2, SEEK_CUR);
>>> +        return 1;
>>> +    }
>>> +
>>> +    v10 = avio_rb16(pb);
>>> +    m->ts = avio_rb64(pb);
>>> +    m->size = avio_rb16(pb) + 1;
>>> +
>>> +    while (!avio_feof(pb)) {
>>> +        unsigned type, ssize, codec_id = 0;
>>> +        unsigned codec_type, width = 0, height = 0, sample_rate = 0,
>>> channels = 0;
>>> +        int stream_index = -1;
>>> +        int format;
>>> +        AVRational fps;
>>> +
>>> +        read_var_byte(s, &type);
>>> +        read_var_byte(s, &ssize);
>>> +
>>> +        switch (type) {
>>> +        case 0:
>>> +            if (ssize > 0)
>>> +                avio_skip(pb, ssize);
>>> +            return 0;
>>> +        case 2:
>>> +            codec_type = AVMEDIA_TYPE_AUDIO;
>>> +            stream_index = avio_r8(pb);
>>> +            codec_id = avio_r8(pb);
>>> +            switch (codec_id) {
>>> +            case 0: codec_id = AV_CODEC_ID_FASTAUDIO; break;
>>> +            case 1: codec_id = AV_CODEC_ID_ADPCM_IMA_MOFLEX; break;
>>> +            }
>>> +            sample_rate = avio_rb24(pb) + 1;
>>> +            channels = avio_r8(pb) + 1;
>>> +            break;
>>> +        case 1:
>>> +        case 3:
>>> +            codec_type = AVMEDIA_TYPE_VIDEO;
>>> +            stream_index = avio_r8(pb);
>>> +            codec_id = avio_r8(pb);
>>> +            fps.num = avio_rb16(pb);
>>> +            fps.den = avio_rb16(pb);
>>> +            width = avio_rb16(pb);
>>> +            height = avio_rb16(pb);
>>> +            format = AV_PIX_FMT_YUV420P;
>>> +            avio_skip(pb, type == 3 ? 3 : 2);
>>> +            break;
>>> +        case 4:
>>> +            codec_type = AVMEDIA_TYPE_DATA;
>>> +            stream_index = avio_r8(pb);
>>> +            avio_skip(pb, 1);
>>> +            break;
>>> +        }
>>> +
>>> +        if (stream_index == s->nb_streams) {
>>> +            AVStream *st = avformat_new_stream(s, NULL);
>>> +
>>
>> Missing allocation check.
>>
>>> +            st->codecpar->codec_type = codec_type;
>>> +            st->codecpar->codec_id   = codec_id;
>>> +            st->codecpar->width      = width;
>>> +            st->codecpar->height     = height;
>>> +            st->codecpar->sample_rate= sample_rate;
>>> +            st->codecpar->channels   = channels;
>>> +            st->codecpar->format     = format;
>>> +            st->priv_data            = av_packet_alloc();
>>
>> If you store a packet in an AVStream's priv_data, it will be av_freep'ed
>> and not av_packet_free'ed at the end. This can lead to leaks.
> 
> I checked with valgrind, no leaks.
> What alternative you propose?
> 
It will lead to leaks if the decoder is not properly cleaned up; e.g.
consider the scenario in which a partial packet in the loop below is
read and not returned; if the next iteration of the loop encounters
invalid data or EOF, the packet's contents are never freed.

The solution is easy: Add a read_close() function.

(And while we are just at it: If a block contains data for a stream, but
no endframe for that stream, it is invalid data, isn't it? Right now the
data would be kept and returned when the next endframe is encountered.
If no endframe is ever encountered, the data leaks.)

>>
>>> +            if (!st->priv_data)
>>> +                return AVERROR(ENOMEM);
>>> +
>>> +            if (sample_rate)
>>> +                avpriv_set_pts_info(st, 63, 1, sample_rate);
>>> +            else
>>> +                avpriv_set_pts_info(st, 63, fps.den, fps.num);
>>> +        }
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int moflex_read_header(AVFormatContext *s)
>>> +{
>>> +    int ret;
>>> +
>>> +    ret = moflex_read_sync(s);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    avio_seek(s->pb, 0, SEEK_SET);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int moflex_read_packet(AVFormatContext *s, AVPacket *pkt)
>>> +{
>>> +    MOFLEXDemuxContext *m = s->priv_data;
>>> +    AVIOContext *pb = s->pb;
>>> +    BitReader *br = &m->br;
>>> +    int ret;
>>> +
>>> +    while (!avio_feof(pb)) {
>>> +        if (!m->in_block) {
>>> +            m->pos = avio_tell(pb);
>>> +
>>> +            ret = moflex_read_sync(s);
>>
>> This may add new streams (is this even intended at this point?), yet you
>> did not set the AVFMTCTX_NOHEADER flag.
>>
>>> +            if (ret < 0)
>>> +                return ret;
>>> +
>>> +            m->flags = avio_r8(pb);
>>> +            if (m->flags & 2)
>>> +                avio_skip(pb, 2);
>>> +        }
>>> +
>>> +        while ((avio_tell(pb) < m->pos + m->size) && !avio_feof(pb) &&
>>> avio_r8(pb)) {
>>> +            int stream_index, pkt_size, endframe;
>>> +            AVPacket *packet;
>>> +
>>> +            m->in_block = 1;
>>> +
>>> +            avio_seek(pb, -1, SEEK_CUR);
>>> +            br->pos = br->last = 0;
>>> +
>>> +            stream_index = pop_int(br, pb, pop_length(br, pb));
>>> +            if (stream_index < 0)
>>> +                return stream_index;
>>> +            if (stream_index >= s->nb_streams)
>>> +                return AVERROR_INVALIDDATA;
>>> +
>>> +            endframe = pop(br, pb);
>>> +            if (endframe < 0)
>>> +                return endframe;
>>> +            if (endframe) {
>>> +                pop_int(br, pb, pop_length(br, pb));
>>> +                pop(br, pb);
>>> +                pop_int(br, pb, pop_length(br, pb) * 2 + 26);
>>> +            }
>>> +
>>> +            pkt_size = pop_int(br, pb, 13) + 1;
>>> +            packet   = s->streams[stream_index]->priv_data;
>>> +
>>> +            ret = av_append_packet(pb, packet, pkt_size);
>>> +            if (endframe) {
>>> +                av_packet_move_ref(pkt, packet);
>>> +                pkt->pos = m->pos;
>>> +                pkt->stream_index = stream_index;
>>> +                return ret;
>>> +            }
>>> +        }
>>> +
>>> +        m->in_block = 0;
>>> +
>>> +        if (m->flags % 2 == 0)
>>> +            avio_seek(pb, m->pos + m->size, SEEK_SET);
>>> +    }
>>> +
>>> +    return AVERROR_EOF;
>>> +}
>>> +
>>> +AVInputFormat ff_moflex_demuxer = {
>>> +    .name           = "moflex",
>>> +    .long_name      = NULL_IF_CONFIG_SMALL("MobiClip MOFLEX"),
>>> +    .priv_data_size = sizeof(MOFLEXDemuxContext),
>>> +    .read_probe     = moflex_probe,
>>> +    .read_header    = moflex_read_header,
>>> +    .read_packet    = moflex_read_packet,
>>> +    .extensions     = "moflex",
>>> +    .flags          = AVFMT_GENERIC_INDEX,
>>> +};
>>>
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel at ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
> 



More information about the ffmpeg-devel mailing list