[FFmpeg-devel] [PATCH] Add support for Opus in MPEG-TS

Michael Niedermayer michaelni at gmx.at
Sat Jul 26 20:15:52 CEST 2014


On Sat, Jul 26, 2014 at 03:06:14PM +0100, Kieran Kunhya wrote:
> ---
>  libavcodec/opus.c        |   41 +++++++++++++++----
>  libavcodec/opus.h        |   11 +++++
>  libavcodec/opus_parser.c |  101 ++++++++++++++++++++++++++++++++++++++++------
>  libavcodec/opusdec.c     |    4 +-
>  libavformat/mpegts.c     |   52 +++++++++++++++++++++++-
>  5 files changed, 186 insertions(+), 23 deletions(-)
> 
> diff --git a/libavcodec/opus.c b/libavcodec/opus.c
> index 91021ce..e230597 100644
> --- a/libavcodec/opus.c
> +++ b/libavcodec/opus.c
> @@ -290,10 +290,6 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
>                                      OpusContext *s)
>  {
>      static const uint8_t default_channel_map[2] = { 0, 1 };
> -    uint8_t default_extradata[19] = {
> -        'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
> -        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> -    };
>  
>      int (*channel_reorder)(int, int) = channel_reorder_unknown;
>  
> @@ -308,9 +304,8 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
>                     "Multichannel configuration without extradata.\n");
>              return AVERROR(EINVAL);
>          }
> -        default_extradata[9] = (avctx->channels == 1) ? 1 : 2;
> -        extradata      = default_extradata;
> -        extradata_size = sizeof(default_extradata);
> +        extradata      = av_opus_default_extradata;
> +        extradata_size = sizeof(av_opus_default_extradata);
>      } else {
>          extradata = avctx->extradata;
>          extradata_size = avctx->extradata_size;
> @@ -330,7 +325,7 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
>  
>      avctx->delay = AV_RL16(extradata + 10);
>  
> -    channels = extradata[9];
> +    channels = avctx->extradata ? extradata[9] : (avctx->channels == 1) ? 1 : 2;
>      if (!channels) {
>          av_log(avctx, AV_LOG_ERROR, "Zero channel count specified in the extadata\n");
>          return AVERROR_INVALIDDATA;
> @@ -426,3 +421,33 @@ av_cold int ff_opus_parse_extradata(AVCodecContext *avctx,
>  
>      return 0;
>  }
> +
> +uint8_t *ff_parse_opus_ts_header(uint8_t *start, int *payload_len){
> +    uint8_t *buf = start + 1;
> +    int start_trim_flag, end_trim_flag, control_extension_flag, control_extension_length, i;
> +    
> +    start_trim_flag = (buf[0] >> 4) & 1;
> +    end_trim_flag = (buf[0] >> 3) & 1;
> +    control_extension_flag = (buf[0] >> 2) & 1;
> +    buf++;
> +    
> +    *payload_len = 0;

> +    while (buf[0] == 0xff){
> +        *payload_len += buf[0];
> +        buf++;
> +    }

for saftey this should check the buf size


> +    *payload_len += buf[0];
> +    buf++;    
> +        
> +    if (start_trim_flag)
> +        buf += 2;
> +    if (end_trim_flag)
> +        buf += 2;
> +    if (control_extension_flag){

> +        control_extension_length = buf[0];
> +        for (i = 0; i < control_extension_length; i++)
> +             buf++;

this needs a length check too


> +    }
> +    
> +    return buf;
> +}
> diff --git a/libavcodec/opus.h b/libavcodec/opus.h
> index c2fac06..16e5b39 100644
> --- a/libavcodec/opus.h
> +++ b/libavcodec/opus.h
> @@ -61,6 +61,15 @@
>  #define ROUND_MUL16(a,b)  ((MUL16(a, b) + 16384) >> 15)
>  #define opus_ilog(i) (av_log2(i) + !!(i))
>  
> +#define OPUS_TS_HEADER     0x7FE0        // 0x3ff (11 bits)
> +#define OPUS_TS_MASK       0x7FF0        // top 11 bits
> +
> +static const uint8_t av_opus_default_extradata[30] = {
> +    'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
> +    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +};
> +
>  enum OpusMode {
>      OPUS_MODE_SILK,
>      OPUS_MODE_HYBRID,

> @@ -383,6 +392,8 @@ int ff_opus_parse_packet(OpusPacket *pkt, const uint8_t *buf, int buf_size,
>  
>  int ff_opus_parse_extradata(AVCodecContext *avctx, OpusContext *s);
>  
> +uint8_t *ff_parse_opus_ts_header(uint8_t *start, int *payload_len);

being a non static function, this maybe should get some doxy
but then it seems only used from one place


> +
>  int ff_silk_init(AVCodecContext *avctx, SilkContext **ps, int output_channels);
>  void ff_silk_free(SilkContext **ps);
>  void ff_silk_flush(SilkContext *s);
> diff --git a/libavcodec/opus_parser.c b/libavcodec/opus_parser.c
> index 8a2bc22..356882c 100644
> --- a/libavcodec/opus_parser.c
> +++ b/libavcodec/opus_parser.c
> @@ -27,49 +27,124 @@
>  
>  #include "avcodec.h"
>  #include "opus.h"
> +#include "parser.h"
> +#include "bytestream.h"
>  
>  typedef struct OpusParseContext {
>      OpusContext ctx;
>      OpusPacket pkt;
>      int extradata_parsed;
> +    ParseContext pc;
> +    int ts_framing;
>  } OpusParseContext;
>  
> -static int opus_parse(AVCodecParserContext *ctx, AVCodecContext *avctx,
> -                      const uint8_t **poutbuf, int *poutbuf_size,
> -                      const uint8_t *buf, int buf_size)
> +/**
> + * Find the end of the current frame in the bitstream.
> + * @return the position of the first byte of the next frame, or -1
> + */
> +static int opus_find_frame_end(AVCodecParserContext *ctx, AVCodecContext *avctx,
> +                               const uint8_t *buf, int buf_size, int *header_len)
>  {
>      OpusParseContext *s = ctx->priv_data;
> -    int ret;
> +    ParseContext *pc    = &s->pc;
> +    int ret, pic_found, i = 0, payload_len = 0;
> +    uint8_t *payload;
> +    uint32_t state;
> +    uint16_t hdr;
> +    *header_len = 0;
>  
>      if (!buf_size)
>          return 0;
>  
> +    pic_found = pc->frame_start_found;
> +    state = pc->state;
> +    payload = buf;
> +
> +    /* Check if we're using Opus in MPEG-TS framing */
> +    if (!s->ts_framing){
> +        hdr = AV_RB16(buf);
> +        if((hdr & OPUS_TS_MASK) == OPUS_TS_HEADER)
> +            s->ts_framing = 1;

parsers in general should work with arbitrary sized chunks of data
so buf may be just 1 byte while this reads 2.
Thats unless iam missing something


> +    }
> +
> +    if (s->ts_framing && !pic_found){
> +        for (i = 0; i < buf_size-2; i++){
> +            state = (state << 8) | payload[i];
> +            if((state & OPUS_TS_MASK) == OPUS_TS_HEADER){
> +                payload = ff_parse_opus_ts_header(payload, &payload_len);          
> +                *header_len = payload - buf;
> +                pic_found = 1;
> +                break;
> +            }
> +        }    
> +    }
> +    
> +    if(!s->ts_framing)
> +        payload_len = buf_size;
> +
>      if (avctx->extradata && !s->extradata_parsed) {
>          ret = ff_opus_parse_extradata(avctx, &s->ctx);
>          if (ret < 0) {
>              av_log(avctx, AV_LOG_ERROR, "Error parsing Ogg extradata.\n");
> -            goto fail;
> +            return AVERROR_INVALIDDATA;
>          }
>          av_freep(&s->ctx.channel_maps);
>          s->extradata_parsed = 1;
>      }
>  
> -    ret = ff_opus_parse_packet(&s->pkt, buf, buf_size, s->ctx.nb_streams > 1);
> -    if (ret < 0) {
> -        av_log(avctx, AV_LOG_ERROR, "Error parsing Opus packet header.\n");
> -        goto fail;
> +    if (payload_len <= buf_size){
> +        s->ctx.nb_streams = 1;
> +        ret = ff_opus_parse_packet(&s->pkt, payload, payload_len, s->ctx.nb_streams > 1);
> +        if (ret < 0) {
> +            av_log(avctx, AV_LOG_ERROR, "Error parsing Opus packet header.\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +
> +        ctx->duration = s->pkt.frame_count * s->pkt.frame_duration;
>      }
>  
> -    ctx->duration = s->pkt.frame_count * s->pkt.frame_duration;
> +    if (s->ts_framing){        
> +        if(pic_found){
> +            if (payload_len + *header_len <= buf_size){
> +                pc->frame_start_found = 0;
> +                pc->state             = -1;
> +                return payload_len + *header_len;
> +            }
> +        }
> +            
> +        pc->frame_start_found = pic_found;
> +        pc->state = state;    
> +        return END_NOT_FOUND;
> +    }
>  
> -fail:
> -    *poutbuf = buf;
> -    *poutbuf_size = buf_size;
>      return buf_size;
>  }
>  
> +static int opus_parse(AVCodecParserContext *ctx, AVCodecContext *avctx,
> +                       const uint8_t **poutbuf, int *poutbuf_size,
> +                       const uint8_t *buf, int buf_size)
> +{
> +    OpusParseContext *s = ctx->priv_data;
> +    ParseContext *pc    = &s->pc;
> +    int next, header_len;
> +
> +    next = opus_find_frame_end(ctx, avctx, buf, buf_size, &header_len);
> +
> +    if (s->ts_framing && next != AVERROR_INVALIDDATA &&
> +        ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
> +        *poutbuf      = NULL;
> +        *poutbuf_size = 0;
> +        return buf_size;
> +    }
> +        
> +    *poutbuf      = buf + header_len;
> +    *poutbuf_size = buf_size - header_len;
> +    return next;
> +}
> +
>  AVCodecParser ff_opus_parser = {
>      .codec_ids      = { AV_CODEC_ID_OPUS },
>      .priv_data_size = sizeof(OpusParseContext),
>      .parser_parse   = opus_parse,
> +    .parser_close   = ff_parse_close
>  };
> diff --git a/libavcodec/opusdec.c b/libavcodec/opusdec.c
> index bf3a54b..5a5858b 100644
> --- a/libavcodec/opusdec.c
> +++ b/libavcodec/opusdec.c
> @@ -452,10 +452,11 @@ static int opus_decode_packet(AVCodecContext *avctx, void *data,
>      int coded_samples   = 0;
>      int decoded_samples = 0;
>      int i, ret;
> +    OpusPacket *pkt;
>  
>      /* decode the header of the first sub-packet to find out the sample count */
>      if (buf) {
> -        OpusPacket *pkt = &c->streams[0].packet;
> +        pkt = &c->streams[0].packet;
>          ret = ff_opus_parse_packet(pkt, buf, buf_size, c->nb_streams > 1);
>          if (ret < 0) {
>              av_log(avctx, AV_LOG_ERROR, "Error parsing the packet header.\n");
> @@ -505,6 +506,7 @@ static int opus_decode_packet(AVCodecContext *avctx, void *data,
>  
>          ret = opus_decode_subpacket(&c->streams[i], buf,
>                                      s->packet.data_size, coded_samples);
> +                                    
>          if (ret < 0)
>              return ret;
>          if (decoded_samples && ret != decoded_samples) {
> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> index fb58e1f..655e3d9 100644
> --- a/libavformat/mpegts.c
> +++ b/libavformat/mpegts.c
> @@ -28,6 +28,7 @@
>  #include "libavutil/opt.h"
>  #include "libavcodec/bytestream.h"
>  #include "libavcodec/get_bits.h"
> +#include "libavcodec/opus.h"
>  #include "avformat.h"
>  #include "mpegts.h"
>  #include "internal.h"
> @@ -595,6 +596,7 @@ static const StreamType REGD_types[] = {
>      { MKTAG('D', 'T', 'S', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS   },
>      { MKTAG('H', 'E', 'V', 'C'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC  },
>      { MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1   },
> +    { MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS  },
>      { 0 },
>  };
>  
> @@ -1296,15 +1298,31 @@ static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section,
>          av_free(mp4_descr[i].dec_config_descr);
>  }
>  
> +static const uint8_t opus_coupled_stream_cnt[9] = {
> +    1, 0, 1, 1, 2, 2, 2, 3, 3
> +};
> +
> +static const uint8_t opus_channel_map[8][8] = {
> +    { 0 },
> +    { 0,1 },
> +    { 0,2,1 },
> +    { 0,1,2,3 },
> +    { 0,4,1,2,3 },
> +    { 0,4,1,2,3,5 },
> +    { 0,4,1,2,3,5,6 },
> +    { 0,6,1,2,3,4,5,7 },
> +};
> +
>  int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type,
>                                const uint8_t **pp, const uint8_t *desc_list_end,
>                                Mp4Descr *mp4_descr, int mp4_descr_count, int pid,
>                                MpegTSContext *ts)
>  {
>      const uint8_t *desc_end;
> -    int desc_len, desc_tag, desc_es_id;
> +    int desc_len, desc_tag, desc_es_id, ext_desc_tag, channels;
>      char language[252];
>      int i;
> +    uint8_t channel_config_code;
>  
>      desc_tag = get8(pp, desc_list_end);
>      if (desc_tag < 0)
> @@ -1423,6 +1441,38 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
>          if (st->codec->codec_id == AV_CODEC_ID_NONE)
>              mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types);
>          break;
> +    case 0x7f: /* DVB extension descriptor */
> +        ext_desc_tag = bytestream_get_byte(pp);
> +        if (st->codec->codec_id == AV_CODEC_ID_OPUS &&
> +            ext_desc_tag  == 0x80){ /* User defined (provisional Opus) */

> +            if (st->codec->extradata)
> +                av_freep(&st->codec->extradata);

the if() is not needed for av_freep()
but what exactly is this intended to do ?
extradata cannot be updated like this mid stream, if thats what its
intended to do.
it would need AV_PKT_DATA_NEW_EXTRADATA sidedata


> +            
> +            st->codec->extradata = av_malloc(sizeof(av_opus_default_extradata)
> +                                             + FF_INPUT_BUFFER_PADDING_SIZE);

the padding should be initialized to avoid use of uninitialized
memory, av_mallocz() is the simplerst way


> +            if (st->codec->extradata) {
> +                st->codec->extradata_size = sizeof(av_opus_default_extradata);
> +                memcpy(st->codec->extradata, av_opus_default_extradata, sizeof(av_opus_default_extradata));
> +            }
> +            channel_config_code = bytestream_get_byte(pp);
> +            if (channel_config_code <= 0x8){
> +                st->codec->extradata[9] = channels = channel_config_code ? channel_config_code : 2;
> +                st->codec->extradata[18] = channels > 2;

this is dereferecing a pointer that might be NULL



> +                st->codec->extradata[19] = channel_config_code;
> +                if (channel_config_code == 0){ /* Dual Mono */
> +                    st->codec->extradata[18] = 255; /* Mapping */
> +                    st->codec->extradata[19] = 2;   /* Stream Count */
> +                }                    
> +                st->codec->extradata[20] = opus_coupled_stream_cnt[channel_config_code];
> +                memcpy(&st->codec->extradata[21], opus_channel_map[channels-1], channels);
> +            } else {
> +                avpriv_request_sample(fc, "opus in ts - channel_config_code > 0x8");
> +            }

> +            st->codec->sample_rate = 48000;

is this really needed ?
doesnt opus store the sample rate somewhere so the parser and
decoder set it correctly


> +            st->codec->sample_fmt = AV_SAMPLE_FMT_FLTP;

this shouldnt be set by the demuxer



> +            st->need_parsing      = AVSTREAM_PARSE_FULL;
> +        }
> +        break;
>      default:
>          break;
>      }
> -- 
> 1.7.9.5
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 

-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

What does censorship reveal? It reveals fear. -- Julian Assange
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20140726/c7fa6a0d/attachment.asc>


More information about the ffmpeg-devel mailing list