[FFmpeg-devel] [PATCH 2/3] avformat/rtpdec_mpeg12: add robust MPEG audio depacketization (RFC 5219)

Thomas Volkert silvo at gmx.net
Sat Feb 14 17:56:49 CET 2015


On 02/13/2015 10:51 PM, Gilles Chanteperdrix wrote:
> Signed-off-by: Gilles Chanteperdrix <gilles.chanteperdrix at xenomai.org>
> ---
>   Changelog                       |   1 +
>   libavcodec/mpegaudio_parser.c   |  14 ++-
>   libavcodec/mpegaudiodecheader.c |   3 +-
>   libavformat/rtpdec.c            |   1 +
>   libavformat/rtpdec_formats.h    |   1 +
>   libavformat/rtpdec_mpeg12.c     | 204 ++++++++++++++++++++++++++++++++++++++++
>   libavformat/version.h           |   4 +-
>   7 files changed, 223 insertions(+), 5 deletions(-)
>
> diff --git a/Changelog b/Changelog
> index c663d5e..775fc79 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -23,6 +23,7 @@ version <next>:
>   - Changed default DNxHD colour range in QuickTime .mov derivatives to mpeg range
>   - ported softpulldown filter from libmpcodecs as repeatfields filter
>   - dcshift filter
> +- RTP parser for loss tolerant payload format for MP3 audio (RFC 5219)
>   
>   
>   version 2.5:
> diff --git a/libavcodec/mpegaudio_parser.c b/libavcodec/mpegaudio_parser.c
> index 79dbf63..42f4706 100644
> --- a/libavcodec/mpegaudio_parser.c
> +++ b/libavcodec/mpegaudio_parser.c
> @@ -64,7 +64,7 @@ static int mpegaudio_parse(AVCodecParserContext *s1,
>           }else{
>               while(i<buf_size){
>                   int ret, sr, channels, bit_rate, frame_size;
> -                enum AVCodecID codec_id;
> +                enum AVCodecID codec_id = avctx->codec_id;
>   
>                   state= (state<<8) + buf[i++];
>   
> @@ -90,6 +90,16 @@ static int mpegaudio_parse(AVCodecParserContext *s1,
>                               avctx->bit_rate += (bit_rate - avctx->bit_rate) / (s->header_count - header_threshold);
>                           }
>                       }
> +
> +                    if (s1->flags & PARSER_FLAG_COMPLETE_FRAMES) {
> +                        s->frame_size = 0;
> +                        next = buf_size;
> +                    } else if (codec_id == AV_CODEC_ID_MP3ADU) {
> +                        av_log(avctx, AV_LOG_ERROR,
> +                            "MP3ADU full parser not implemented");
> +                        return AVERROR_PATCHWELCOME;
> +                    }
> +
>                       break;
>                   }
>               }
> @@ -110,7 +120,7 @@ static int mpegaudio_parse(AVCodecParserContext *s1,
>   
>   
>   AVCodecParser ff_mpegaudio_parser = {
> -    .codec_ids      = { AV_CODEC_ID_MP1, AV_CODEC_ID_MP2, AV_CODEC_ID_MP3 },
> +    .codec_ids      = { AV_CODEC_ID_MP1, AV_CODEC_ID_MP2, AV_CODEC_ID_MP3, AV_CODEC_ID_MP3ADU },
>       .priv_data_size = sizeof(MpegAudioParseContext),
>       .parser_parse   = mpegaudio_parse,
>       .parser_close   = ff_parse_close,
> diff --git a/libavcodec/mpegaudiodecheader.c b/libavcodec/mpegaudiodecheader.c
> index 5db1957..6af6e4b 100644
> --- a/libavcodec/mpegaudiodecheader.c
> +++ b/libavcodec/mpegaudiodecheader.c
> @@ -134,7 +134,8 @@ int avpriv_mpa_decode_header2(uint32_t head, int *sample_rate, int *channels, in
>           break;
>       default:
>       case 3:
> -        *codec_id = AV_CODEC_ID_MP3;
> +        if (*codec_id != AV_CODEC_ID_MP3ADU)
> +            *codec_id = AV_CODEC_ID_MP3;
>           if (s->lsf)
>               *frame_size = 576;
>           else
> diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c
> index 4ff209c..c632340 100644
> --- a/libavformat/rtpdec.c
> +++ b/libavformat/rtpdec.c
> @@ -89,6 +89,7 @@ void ff_register_rtp_dynamic_payload_handlers(void)
>       ff_register_dynamic_payload_handler(&ff_mp4a_latm_dynamic_handler);
>       ff_register_dynamic_payload_handler(&ff_mp4v_es_dynamic_handler);
>       ff_register_dynamic_payload_handler(&ff_mpeg_audio_dynamic_handler);
> +    ff_register_dynamic_payload_handler(&ff_mpeg_audio_robust_dynamic_handler);
>       ff_register_dynamic_payload_handler(&ff_mpeg_video_dynamic_handler);
>       ff_register_dynamic_payload_handler(&ff_mpeg4_generic_dynamic_handler);
>       ff_register_dynamic_payload_handler(&ff_mpegts_dynamic_handler);
> diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h
> index 87e316f..da22d70 100644
> --- a/libavformat/rtpdec_formats.h
> +++ b/libavformat/rtpdec_formats.h
> @@ -52,6 +52,7 @@ extern RTPDynamicProtocolHandler ff_jpeg_dynamic_handler;
>   extern RTPDynamicProtocolHandler ff_mp4a_latm_dynamic_handler;
>   extern RTPDynamicProtocolHandler ff_mp4v_es_dynamic_handler;
>   extern RTPDynamicProtocolHandler ff_mpeg_audio_dynamic_handler;
> +extern RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler;
>   extern RTPDynamicProtocolHandler ff_mpeg_video_dynamic_handler;
>   extern RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler;
>   extern RTPDynamicProtocolHandler ff_mpegts_dynamic_handler;
> diff --git a/libavformat/rtpdec_mpeg12.c b/libavformat/rtpdec_mpeg12.c
> index d73ff1e..d99c507 100644
> --- a/libavformat/rtpdec_mpeg12.c
> +++ b/libavformat/rtpdec_mpeg12.c
> @@ -2,6 +2,9 @@
>    * Common code for the RTP depacketization of MPEG-1/2 formats.
>    * Copyright (c) 2002 Fabrice Bellard
>    *
> + * RTP parser for loss tolerant payload format for MP3 audio (RFC 5219)
> + * Copyright (c) 2015 Gilles Chanteperdrix <gch at xenomai.org>
> + *
>    * This file is part of FFmpeg.
>    *
>    * FFmpeg is free software; you can redistribute it and/or
> @@ -23,6 +26,8 @@
>   #include "libavutil/intreadwrite.h"
>   #include "rtpdec_formats.h"
>   
> +#define RTP_MPA_PAYLOAD_HEADER_SIZE 1
> +
>   static av_cold int mpeg_init(AVFormatContext *ctx, int st_index, PayloadContext *data)
>   {
>       if (st_index < 0)
> @@ -71,3 +76,202 @@ RTPDynamicProtocolHandler ff_mpeg_video_dynamic_handler = {
>       .parse_packet      = mpeg_parse_packet,
>       .static_payload_id = 32,
>   };
> +
> +/* MPA-ROBUST, RFC 5219 */
> +struct PayloadContext {
> +    unsigned adu_size;
> +    unsigned cur_size;
> +    uint32_t timestamp;
> +    uint8_t *split_buf;
> +    int split_pos, split_buf_size, split_pkts;
> +    AVIOContext *fragment;
> +};
> +
> +static av_cold int mpa_robust_init(AVFormatContext *ctx, int st_index,
> +                                PayloadContext *data)
> +{
> +    if (st_index < 0)
> +        return 0;
> +    ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_HEADERS;
> +    return 0;
> +}
> +
> +static PayloadContext *mpa_robust_new_context(void)
> +{
> +        return av_mallocz(sizeof(PayloadContext));
> +}
> +
> +static inline void free_fragment_if_needed(PayloadContext *data)
> +{
> +    if (data->fragment) {
> +        uint8_t *p;
> +        avio_close_dyn_buf(data->fragment, &p);
> +        av_free(p);
> +        data->fragment = NULL;
> +    }
> +}
> +
> +static void mpa_robust_free_context(PayloadContext *data)
> +{
> +    free_fragment_if_needed(data);
> +    av_free(data);
> +}
> +
> +static int mpa_robust_parse_rtp_header(AVFormatContext *ctx,
> +                                        const uint8_t *buf, int len,
> +                                        unsigned *adu_size, unsigned *cont)
> +{
> +    unsigned header_size;
> +
> +    if (len < RTP_MPA_PAYLOAD_HEADER_SIZE + 1) {
> +        av_log(ctx, AV_LOG_ERROR, "Invalid %d bytes packet\n", len);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    *cont = !!(buf[0] & 0x80);
> +    if (!(buf[0] & 0x40)) {
> +        header_size = 1;
> +        *adu_size = buf[0] & ~0xc0;
> +    } else {
> +        header_size = 2;
> +        *adu_size = AV_RB16(buf) & ~0xc000;
> +    }
> +
> +    return header_size;
> +}
> +
> +static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data,
> +                                AVStream *st, AVPacket *pkt,
> +                                uint32_t *timestamp, const uint8_t *buf,
> +                                int len, uint16_t seq, int flags)
> +{
> +    unsigned adu_size, continuation;
> +    int err, header_size;
> +
> +    if (!buf) {
> +        buf = &data->split_buf[data->split_pos];
> +        len = data->split_buf_size - data->split_pos;
> +
> +        header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
> +                                                &continuation);
> +        if (header_size < 0) {
> +            av_freep(&data->split_buf);
> +            return header_size;
> +        }
> +        buf += header_size;
> +        len -= header_size;
> +
> +        if (continuation || adu_size > len) {
> +            av_freep(&data->split_buf);
> +            av_log(ctx, AV_LOG_ERROR, "Invalid frame\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +
> +        if (av_new_packet(pkt, adu_size)) {
> +            av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
> +            return AVERROR(ENOMEM);
> +        }
> +
> +        pkt->stream_index = st->index;
> +        memcpy(pkt->data, buf, adu_size);
> +
> +        data->split_pos = (buf - data->split_buf) + adu_size;
> +
> +        if (data->split_pos == data->split_buf_size) {
> +            av_freep(&data->split_buf);
> +            return 0;
> +        }
> +
> +        return 1;
> +    }
> +
> +
> +    header_size = mpa_robust_parse_rtp_header(ctx, buf, len, &adu_size,
> +                                            &continuation);
> +    if (header_size < 0)
> +        return header_size;
> +
> +    buf += header_size;
> +    len -= header_size;
> +
> +    if (!continuation && adu_size <= len) {
> +        /* One or more complete frames */
> +
> +        if (av_new_packet(pkt, adu_size)) {
> +            av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
> +            return AVERROR(ENOMEM);
> +        }
> +
> +        pkt->stream_index = st->index;
> +        memcpy(pkt->data, buf, adu_size);
> +
> +        buf += adu_size;
> +        len -= adu_size;
> +        if (len) {
> +            data->split_buf_size = len;
> +            data->split_buf = av_malloc(data->split_buf_size);
> +            data->split_pos = 0;
> +            if (!data->split_buf) {
> +                av_log(ctx, AV_LOG_ERROR, "Out of memory.\n");
> +                av_free_packet(pkt);
> +                return AVERROR(ENOMEM);
> +            }
> +            memcpy(data->split_buf, buf, data->split_buf_size);
> +            return 1;
> +        }
> +        return 0;
> +    } else if (!continuation) { /* && adu_size > len */
> +        /* First fragment */
> +        free_fragment_if_needed(data);
> +
> +        data->adu_size = adu_size;
> +        data->cur_size = len;
> +        data->timestamp = *timestamp;
> +
> +        err = avio_open_dyn_buf(&data->fragment);
> +        if (err < 0)
> +            return err;
> +
> +        avio_write(data->fragment, buf, len);
> +        return AVERROR(EAGAIN);
> +    }
> +    /* else continuation == 1 */
> +
> +    /* Fragment other than first */
> +    if (!data->fragment) {
> +        av_log(ctx, AV_LOG_WARNING,
> +            "Received packet without a start fragment; dropping.\n");
> +        return AVERROR(EAGAIN);
> +    }
> +    if (adu_size = data->adu_size ||
> +        data->timestamp != *timestamp) {
> +        free_fragment_if_needed(data);
> +        av_log(ctx, AV_LOG_ERROR, "Invalid packet received\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    avio_write(data->fragment, buf, len);
> +    data->cur_size += len;
> +
> +    if (data->cur_size < data->adu_size)
> +        return AVERROR(EAGAIN);
> +
> +    err = ff_rtp_finalize_packet(pkt, &data->fragment, st->index);
> +    if (err < 0) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Error occurred when getting fragment buffer.");
> +        return err;
> +    }
> +
> +    return 0;
> +}
> +
> +RTPDynamicProtocolHandler ff_mpeg_audio_robust_dynamic_handler = {
> +    .codec_type        = AVMEDIA_TYPE_AUDIO,
> +    .codec_id          = AV_CODEC_ID_MP3ADU,
> +    .init              = mpa_robust_init,
> +    .alloc             = mpa_robust_new_context,
> +    .free              = mpa_robust_free_context,
> +    .parse_packet      = mpa_robust_parse_packet,
> +    .enc_name          = "mpa-robust",
> +};
> diff --git a/libavformat/version.h b/libavformat/version.h
> index edad275..84749ec 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -30,8 +30,8 @@
>   #include "libavutil/version.h"
>   

Okay.

>   #define LIBAVFORMAT_VERSION_MAJOR 56
> -#define LIBAVFORMAT_VERSION_MINOR  19
> -#define LIBAVFORMAT_VERSION_MICRO 101
> +#define LIBAVFORMAT_VERSION_MINOR  20
> +#define LIBAVFORMAT_VERSION_MICRO 102
In this case it should be:

+#define LIBAVFORMAT_VERSION_MINOR  20
+#define LIBAVFORMAT_VERSION_MICRO 100

My comment was maybe a bit misleading. But I think this can be fixed when the patch gets committed.


>   
>   #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
>                                                  LIBAVFORMAT_VERSION_MINOR, \

Best regards,
Thomas.


More information about the ffmpeg-devel mailing list