diff mbox series

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

Message ID 20200824232858.18938-1-onemda@gmail.com
State Accepted
Headers show
Series [FFmpeg-devel,1/3] avformat: add moflex demuxer
Related show

Checks

Context Check Description
andriy/default pending
andriy/make fail Make failed

Commit Message

Paul B Mahol Aug. 24, 2020, 11:28 p.m. UTC
Signed-off-by: Paul B Mahol <onemda@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

Comments

Andreas Rheinhardt Aug. 24, 2020, 11:55 p.m. UTC | #1
Paul B Mahol:
> Signed-off-by: Paul B Mahol <onemda@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.

> +            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,
> +};
>
Paul B Mahol Aug. 25, 2020, 10:24 a.m. UTC | #2
On 8/25/20, Andreas Rheinhardt <andreas.rheinhardt@gmail.com> wrote:
> Paul B Mahol:
>> Signed-off-by: Paul B Mahol <onemda@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?

>
>> +            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@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Andreas Rheinhardt Aug. 25, 2020, 10:38 a.m. UTC | #3
Paul B Mahol:
> On 8/25/20, Andreas Rheinhardt <andreas.rheinhardt@gmail.com> wrote:
>> Paul B Mahol:
>>> Signed-off-by: Paul B Mahol <onemda@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@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> 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".
>
Michael Niedermayer Aug. 25, 2020, 7:09 p.m. UTC | #4
On Tue, Aug 25, 2020 at 01:28:56AM +0200, Paul B Mahol wrote:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
> [WIP] Missing video decoder.
[...]

> +static int moflex_probe(const AVProbeData *p)
> +{
> +    if (p->buf[0] != 0x4C && p->buf[1] != 0x32)
> +        return 0;
> +
> +    return AVPROBE_SCORE_MAX / 3;
> +}

It would be better to check more of the file (and maybe then also have a
higher score)
This as it is fails probetest
tools/probetest 256 4096
testing size=1
Failure of moflex probing code with score=33 type=0 p=53F size=1

thx

[...]
diff mbox series

Patch

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