diff mbox series

[FFmpeg-devel,v2,1/2] avcodec/s302m: enable non-PCM decoding

Message ID 20240127103854.9971-1-ffmpeg@gyani.pro
State New
Headers show
Series [FFmpeg-devel,v2,1/2] avcodec/s302m: enable non-PCM decoding | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Gyan Doshi Jan. 27, 2024, 10:38 a.m. UTC
Set up framework for non-PCM decoding in-place and
add support for Dolby-E decoding.

Useful for direct transcoding of non-PCM audio in live inputs.
---
 configure          |   1 +
 doc/decoders.texi  |  40 +++
 libavcodec/s302m.c | 596 +++++++++++++++++++++++++++++++++++++--------
 3 files changed, 530 insertions(+), 107 deletions(-)

v2:
switch to pointer for non-pcm sync search
use intreadwrite macro for writing frame data
remove unneeded free of non-pcm dec opts

Comments

Gyan Doshi Feb. 13, 2024, 4:40 p.m. UTC | #1
On 2024-01-27 04:08 pm, Gyan Doshi wrote:
> Set up framework for non-PCM decoding in-place and
> add support for Dolby-E decoding.
>
> Useful for direct transcoding of non-PCM audio in live inputs.

Plan to push in 3 days, barring objections.

Regards,
Gyan


> ---
>   configure          |   1 +
>   doc/decoders.texi  |  40 +++
>   libavcodec/s302m.c | 596 +++++++++++++++++++++++++++++++++++++--------
>   3 files changed, 530 insertions(+), 107 deletions(-)
>
> v2:
> switch to pointer for non-pcm sync search
> use intreadwrite macro for writing frame data
> remove unneeded free of non-pcm dec opts
>
> diff --git a/configure b/configure
> index 21663000f8..4d7cd83247 100755
> --- a/configure
> +++ b/configure
> @@ -2980,6 +2980,7 @@ rv20_decoder_select="h263_decoder"
>   rv20_encoder_select="h263_encoder"
>   rv30_decoder_select="golomb h264pred h264qpel mpegvideodec rv34dsp"
>   rv40_decoder_select="golomb h264pred h264qpel mpegvideodec rv34dsp"
> +s302m_decoder_select="dolby_e_decoder"
>   screenpresso_decoder_deps="zlib"
>   shorten_decoder_select="bswapdsp"
>   sipr_decoder_select="lsp"
> diff --git a/doc/decoders.texi b/doc/decoders.texi
> index 293c82c2ba..9f85c876bf 100644
> --- a/doc/decoders.texi
> +++ b/doc/decoders.texi
> @@ -347,6 +347,46 @@ configuration. You need to explicitly configure the build with
>   An FFmpeg native decoder for Opus exists, so users can decode Opus
>   without this library.
>   
> +@section s302m
> +
> +SMPTE ST 302 decoder.
> +
> +SMPTE ST 302 is a method for storing AES3 data format within an MPEG Transport
> +Stream. AES3 streams can contain LPCM streams of 2, 4, 6 or 8 channels with a
> +bit depth of 16, 20 or 24-bits at a sample rate of 48 kHz.
> +They can also contain non-PCM codec streams such as AC-3 or Dolby-E.
> +
> +Decoding non-PCM streams directly requires that the necessary stream decoder be
> +present in the build. At present, only Dolby-E decoding is supported.
> +
> +@subsection Options
> +
> +The following options are supported by the s302m decoder.
> +
> +@table @option
> +@item non_pcm_mode @var{mode}
> +Specify how to process non-PCM streams
> +
> +@table @samp
> +@item copy
> +Treat data as if it were LPCM.
> +@item drop
> +Discard the stream.
> +@item decode_copy
> +Decode if possible eise treat the same as @code{copy}.
> +@item decode_drop
> +Decode if possible eise treat the same as @code{drop}.
> +@end table
> +
> +The default value is @code{decode_drop}. This option does not affect the processing of
> +LPCM streams.
> +
> +@item non_pcm_options @var{options}
> +Set options for non-PCM decoder using a list of key=value pairs separated by ":".
> +Consult the docs for the non-PCM decoder for its options.
> +
> +@end table
> +
>   @c man end AUDIO DECODERS
>   
>   @chapter Subtitles Decoders
> diff --git a/libavcodec/s302m.c b/libavcodec/s302m.c
> index f1b41608f3..d890b6f117 100644
> --- a/libavcodec/s302m.c
> +++ b/libavcodec/s302m.c
> @@ -24,21 +24,264 @@
>   #include "libavutil/intreadwrite.h"
>   #include "libavutil/opt.h"
>   #include "libavutil/log.h"
> +#include "libavutil/dict.h"
>   #include "libavutil/reverse.h"
>   #include "avcodec.h"
>   #include "codec_internal.h"
> +#include "get_bits.h"
>   #include "decode.h"
>   
>   #define AES3_HEADER_LEN 4
>   
> +#define NONPCMSYNC_16MARKER      0x4E1F0F8720
> +#define NONPCMSYNC_20MARKER      0x4E1F60F872A0
> +#define NONPCMSYNC_24MARKER      0x7E1F690F872A50
> +
> +#define NONPCMSYNC_16_IN_20MARKER      0x04E1F00F8720
> +#define NONPCMSYNC_20_IN_24MARKER      0x04E1F600F872A0
> +
> +#define IS_NONPCMSYNC_16(state)   ((state & 0xFFFF0FFFF0)     == NONPCMSYNC_16MARKER)
> +#define IS_NONPCMSYNC_20(state)   ((state & 0xFFFFF0FFFFF0)   == NONPCMSYNC_20MARKER)
> +#define IS_NONPCMSYNC_24(state)   ((state & 0xFFFFFF0FFFFFF0) == NONPCMSYNC_24MARKER)
> +
> +#define IS_NONPCMSYNC_16_IN_20(state)   ((state & 0x0FFFF00FFFF0)   == NONPCMSYNC_16_IN_20MARKER)
> +#define IS_NONPCMSYNC_20_IN_24(state)   ((state & 0x0FFFFF00FFFFF0) == NONPCMSYNC_20_IN_24MARKER)
> +
> +#define IS_NONPCMSYNC(bit,state)  ( ((bit == 16) &&  IS_NONPCMSYNC_16(state)) || \
> +                                    ((bit == 20) && (IS_NONPCMSYNC_20(state) || IS_NONPCMSYNC_16_IN_20(state))) || \
> +                                    ((bit == 24) && (IS_NONPCMSYNC_24(state) || IS_NONPCMSYNC_20_IN_24(state))) \
> +                                  )
> +
> +enum non_pcm_modes {
> +    NON_PCM_COPY,
> +    NON_PCM_DROP,
> +    NON_PCM_DEC_ELSE_COPY,
> +    NON_PCM_DEC_ELSE_DROP,
> +};
> +
>   typedef struct S302Context {
>       AVClass *class;
> +
> +    int avctx_props_set;
> +
> +    int channels;
> +    int bits;
> +
>       int non_pcm_mode;
> +    int non_pcm_data_type;
> +    int non_pcm_bits;
> +    int non_pcm_dec;
> +
> +    AVCodecContext *non_pcm_ctx;
> +    AVDictionary   *non_pcm_opts;
> +    AVPacket *packet;
> +    AVFrame  *frame;
>   } S302Context;
>   
> +static av_cold int s302m_init(AVCodecContext *avctx)
> +{
> +    S302Context *s = avctx->priv_data;
> +
> +    s->non_pcm_data_type = -1;
> +
> +    return 0;
> +}
> +
> +static int s302m_non_pcm_inspect(AVCodecContext *avctx, const uint8_t *buf, int buf_size,
> +                                  int *offset, int *length)
> +{
> +    S302Context *s = avctx->priv_data;
> +    const uint8_t *pos = buf;
> +    int ret, aes_frm_size, data_type, length_code = 0, pkt_left = buf_size;
> +    uint64_t state = 0;
> +    uint32_t size;
> +
> +    if (s->channels != 2) {
> +        goto end;
> +    }
> +
> +    aes_frm_size = (s->bits + 4) * 2 / 8;
> +    if (pkt_left < aes_frm_size * 2)  // not enough to contain data_type & length_code
> +        return AVERROR_INVALIDDATA;
> +
> +    while (!IS_NONPCMSYNC(s->bits,state) && pkt_left-- >= 1)
> +        state = (state << 8) | *pos++;
> +
> +    if (IS_NONPCMSYNC(s->bits,state)) {
> +        GetBitContext gb;
> +
> +        if (pkt_left < aes_frm_size * 8) {
> +            av_log(avctx, AV_LOG_ERROR, "truncated non-pcm frame detected\n");
> +            return AVERROR_INVALIDDATA;
> +        }
> +
> +        ret = init_get_bits8(&gb, pos, aes_frm_size);
> +        if (ret < 0)
> +            return ret;
> +
> +        if (s->bits == 16) {
> +            s->non_pcm_bits = 16;
> +        } else if (s->bits == 20) {
> +            if (IS_NONPCMSYNC_16_IN_20(state))
> +                s->non_pcm_bits = 16;
> +            else
> +                s->non_pcm_bits = 20;
> +        } else if (s->bits == 24) {
> +            if (IS_NONPCMSYNC_20_IN_24(state))
> +                s->non_pcm_bits = 20;
> +            else
> +                s->non_pcm_bits = 24;
> +        }
> +
> +        skip_bits(&gb, s->bits - 16);
> +
> +        data_type = ff_reverse[(uint8_t)get_bits(&gb, 5)] >> 3;
> +
> +        if (s->non_pcm_data_type == -1) {
> +            s->non_pcm_data_type = data_type;
> +            av_log(avctx, AV_LOG_INFO, "stream has non-pcm data of type %d with %d-bit words in aes3 payload of size %d bits\n", data_type, s->non_pcm_bits, s->bits);
> +        } else if (s->non_pcm_data_type != data_type) {
> +            av_log(avctx, AV_LOG_DEBUG, "type mismatch of non-pcm type in packet %d vs stream %d. Dropping.\n", data_type, s->non_pcm_data_type);
> +            return AVERROR_INVALIDDATA;
> +        }
> +
> +        if (s->non_pcm_mode == NON_PCM_COPY)
> +            return 0;
> +        else if (s->non_pcm_mode == NON_PCM_DROP)
> +            return AVERROR_INVALIDDATA;
> +
> +        skip_bits(&gb, 15);
> +
> +        size = get_bits(&gb, s->bits);
> +
> +        length_code = ((ff_reverse[(uint8_t)((size & 0xFF)          )] << 16) |
> +                       (ff_reverse[(uint8_t)((size & 0xFF00)   >> 8 )] << 8 ) |
> +                       (ff_reverse[(uint8_t)((size & 0xFF0000) >> 16)]      ) ) >> (24 - s->non_pcm_bits);
> +
> +        skip_bits(&gb, 4);
> +
> +        *offset = get_bits_count(&gb)/8 + buf_size - pkt_left;
> +        *length = length_code;
> +
> +        av_log(avctx, AV_LOG_TRACE, "located non-pcm packet at offset %d length code %d.\n", AES3_HEADER_LEN + *offset, length_code);
> +
> +        return data_type;
> +    }
> +
> +end:
> +    if (s->non_pcm_data_type == -1) {
> +        s->non_pcm_data_type = 32;  // indicate stream should be treated as LPCM
> +        return 0;
> +    } else
> +        return AVERROR_INVALIDDATA;
> +}
> +
> +static int s302m_setup_non_pcm_handling(AVCodecContext *avctx, const uint8_t *buf, int buf_size)
> +{
> +    S302Context *s = avctx->priv_data;
> +    const AVCodec *codec;
> +    enum AVCodecID codec_id;
> +    AVDictionary *dec_opts = NULL;
> +    int ret;
> +
> +    if (s->non_pcm_mode > NON_PCM_DROP) {
> +        switch (s->non_pcm_data_type) {
> +        case 0x1C:
> +            codec_id = AV_CODEC_ID_DOLBY_E;
> +            break;
> +        default:
> +            avpriv_report_missing_feature(avctx, "decode of non-pcm data type %d", s->non_pcm_data_type);
> +            ret = AVERROR_PATCHWELCOME;
> +            goto fail;
> +        }
> +
> +        codec = avcodec_find_decoder(codec_id);
> +        if (!codec) {
> +            ret = AVERROR_DECODER_NOT_FOUND;
> +            goto fail;
> +        }
> +
> +        s->non_pcm_ctx = avcodec_alloc_context3(codec);
> +        if (!s->non_pcm_ctx) {
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +
> +        av_dict_copy(&dec_opts, s->non_pcm_opts, 0);
> +
> +        ret = avcodec_open2(s->non_pcm_ctx, codec, &dec_opts);
> +        av_dict_free(&dec_opts);
> +        if (ret < 0)
> +            goto fail;
> +
> +        s->packet = av_packet_alloc();
> +        if (!s->packet) {
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +
> +        s->frame = av_frame_alloc();
> +        if (!s->frame) {
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +    } else
> +        return 0;
> +
> +    s->non_pcm_dec = 1;
> +    return 0;
> +
> +fail:
> +    avcodec_free_context(&s->non_pcm_ctx);
> +    av_packet_free(&s->packet);
> +    av_frame_free(&s->frame);
> +
> +    if (s->non_pcm_mode == NON_PCM_DEC_ELSE_COPY)
> +        s->non_pcm_mode = NON_PCM_COPY;
> +    else if (s->non_pcm_mode == NON_PCM_DEC_ELSE_DROP)
> +        s->non_pcm_mode = NON_PCM_DROP;
> +
> +    return ret;
> +}
> +
> +static int s302m_get_non_pcm_pkt_size(AVCodecContext *avctx, int buf_size, int offset,
> +                                      int length_code, int *dec_pkt_size)
> +{
> +    S302Context *s = avctx->priv_data;
> +    int nb_words, word_size, aesframe_size, s302m_read_size;
> +
> +    if (offset < 0 || offset >= buf_size)
> +        return AVERROR_INVALIDDATA;
> +
> +    switch (s->non_pcm_data_type) {
> +    case 0x1C:
> +        goto dolby_e;
> +    default:
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +dolby_e:
> +    {
> +    nb_words = length_code / s->non_pcm_bits;
> +    nb_words += nb_words & 1;
> +
> +    word_size = s->non_pcm_bits + 7 >> 3;
> +    aesframe_size = (s->bits + 4) * 2 / 8;  // 2 subframes, each with payload + VUCF bits
> +
> +    *dec_pkt_size = nb_words * word_size;
> +    s302m_read_size = aesframe_size * nb_words/2;
> +
> +    if (offset + s302m_read_size > buf_size)
> +        return AVERROR_INVALIDDATA;
> +
> +    return s302m_read_size;
> +    }
> +}
> +
>   static int s302m_parse_frame_header(AVCodecContext *avctx, const uint8_t *buf,
>                                       int buf_size)
>   {
> +    S302Context *s = avctx->priv_data;
>       uint32_t h;
>       int frame_size, channels, bits;
>   
> @@ -66,33 +309,15 @@ static int s302m_parse_frame_header(AVCodecContext *avctx, const uint8_t *buf,
>           return AVERROR_INVALIDDATA;
>       }
>   
> -    /* Set output properties */
> -    avctx->bits_per_raw_sample = bits;
> -    if (bits > 16)
> -        avctx->sample_fmt = AV_SAMPLE_FMT_S32;
> -    else
> -        avctx->sample_fmt = AV_SAMPLE_FMT_S16;
> -
> -    av_channel_layout_uninit(&avctx->ch_layout);
> -    switch(channels) {
> -        case 2:
> -            avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
> -            break;
> -        case 4:
> -            avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_QUAD;
> -            break;
> -        case 6:
> -            avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1_BACK;
> -            break;
> -        case 8:
> -            av_channel_layout_from_mask(&avctx->ch_layout,
> -                                        AV_CH_LAYOUT_5POINT1_BACK | AV_CH_LAYOUT_STEREO_DOWNMIX);
> -            break;
> -        default:
> -            avctx->ch_layout.order       = AV_CHANNEL_ORDER_UNSPEC;
> -            avctx->ch_layout.nb_channels = channels;
> -            break;
> -    }
> +    if (!s->channels)
> +        s->channels = channels;
> +    else if (s->channels != channels)
> +        return AVERROR_INVALIDDATA;
> +
> +    if (!s->bits)
> +        s->bits = bits;
> +    else if (s->bits != bits)
> +        return AVERROR_INVALIDDATA;
>   
>       return frame_size;
>   }
> @@ -103,119 +328,273 @@ static int s302m_decode_frame(AVCodecContext *avctx, AVFrame *frame,
>       S302Context *s = avctx->priv_data;
>       const uint8_t *buf = avpkt->data;
>       int buf_size       = avpkt->size;
> -    int block_size, ret, channels;
> -    int i;
> -    int non_pcm_data_type = -1;
> +    int block_size, ret, channels, frame_size;
> +    int non_pcm_offset = -1, non_pcm_length = 0;
> +    int dec_pkt_size = 0;
>   
> -    int frame_size = s302m_parse_frame_header(avctx, buf, buf_size);
> +    if (s->non_pcm_mode == NON_PCM_DROP && s->non_pcm_data_type != -1 && s->non_pcm_data_type != 32)
> +        return avpkt->size;
> +
> +    frame_size = s302m_parse_frame_header(avctx, buf, buf_size);
>       if (frame_size < 0)
>           return frame_size;
>   
>       buf_size -= AES3_HEADER_LEN;
>       buf      += AES3_HEADER_LEN;
>   
> -    /* get output buffer */
> -    block_size = (avctx->bits_per_raw_sample + 4) / 4;
> -    channels = avctx->ch_layout.nb_channels;
> -    frame->nb_samples = 2 * (buf_size / block_size) / channels;
> -    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
> -        return ret;
> +    // set non-pcm status if not determined
> +    // else extract offset and length if non-pcm can be decoded
> +    if (s->non_pcm_data_type == -1 || s->non_pcm_dec) {
> +        ret = s302m_non_pcm_inspect(avctx, buf, buf_size, &non_pcm_offset, &non_pcm_length);
> +        if (ret >= 0 && s->non_pcm_data_type != 32 && !s->non_pcm_dec)
> +            ret = s302m_setup_non_pcm_handling(avctx, buf, buf_size);
> +        else if (ret < 0)
> +            return avpkt->size;
> +    }
> +
> +    if (s->non_pcm_data_type == -1)
> +        return AVERROR_INVALIDDATA;  // we should know data type in order to proceed with output
> +
> +    if (!s->non_pcm_dec && !s->avctx_props_set) {
> +        /* Set output properties */
> +        avctx->bits_per_raw_sample = s->non_pcm_bits ? s->non_pcm_bits : s->bits;
> +        if (avctx->bits_per_raw_sample > 16)
> +            avctx->sample_fmt = AV_SAMPLE_FMT_S32;
> +        else
> +            avctx->sample_fmt = AV_SAMPLE_FMT_S16;
>   
> -    avctx->bit_rate = 48000 * channels * (avctx->bits_per_raw_sample + 4) +
> -                      32 * 48000 / frame->nb_samples;
> -    buf_size = (frame->nb_samples * channels / 2) * block_size;
> +        av_channel_layout_uninit(&avctx->ch_layout);
> +        switch(s->channels) {
> +            case 2:
> +                avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
> +                break;
> +            case 4:
> +                avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_QUAD;
> +                break;
> +            case 6:
> +                avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1_BACK;
> +                break;
> +            case 8:
> +                av_channel_layout_from_mask(&avctx->ch_layout,
> +                                            AV_CH_LAYOUT_5POINT1_BACK | AV_CH_LAYOUT_STEREO_DOWNMIX);
> +                break;
> +            default:
> +                avctx->ch_layout.order       = AV_CHANNEL_ORDER_UNSPEC;
> +                avctx->ch_layout.nb_channels = channels;
> +                break;
> +        }
> +
> +        avctx->sample_rate = 48000;
> +        s->avctx_props_set = 1;
> +    }
> +
> +    if (s->non_pcm_dec) {
> +        buf_size = s302m_get_non_pcm_pkt_size(avctx, buf_size, non_pcm_offset, non_pcm_length, &dec_pkt_size);
> +        if (buf_size < 0)
> +            return AVERROR_INVALIDDATA;
> +        buf += non_pcm_offset;
> +
> +        if (dec_pkt_size > s->packet->size) {
> +            ret = av_grow_packet(s->packet, dec_pkt_size - s->packet->size);
> +            if (ret < 0)
> +                return ret;
> +        }
> +        ret = av_packet_make_writable(s->packet);
> +        if (ret < 0)
> +            return ret;
> +        memset(s->packet->data, 0, s->packet->size);
> +
> +        ret = av_packet_copy_props(s->packet, avpkt);
> +        if (ret < 0)
> +            return ret;
> +    } else {
> +        /* get output buffer */
> +        block_size = (s->bits + 4) / 4;
> +        channels = s->channels;
> +        frame->nb_samples = 2 * (buf_size / block_size) / channels;
> +        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
> +            return ret;
> +
> +        buf_size = (frame->nb_samples * channels / 2) * block_size;
> +
> +        avctx->bit_rate = 48000 * s->channels * ((s->non_pcm_bits ? s->non_pcm_bits : s->bits) + 4) +
> +                          32 * 48000 / frame->nb_samples;
> +    }
> +
> +    if (s->bits == 24) {
> +        uint8_t  *p   = NULL;
> +        uint32_t *f32 = NULL;
> +
> +        if (s->non_pcm_dec)
> +            p = (uint8_t *)s->packet->data;
> +        else
> +            f32 = (uint32_t *)frame->data[0];
>   
> -    if (avctx->bits_per_raw_sample == 24) {
> -        uint32_t *o = (uint32_t *)frame->data[0];
>           for (; buf_size > 6; buf_size -= 7) {
> -            *o++ = ((unsigned)ff_reverse[buf[2]]        << 24) |
> -                   (ff_reverse[buf[1]]        << 16) |
> -                   (ff_reverse[buf[0]]        <<  8);
> -            *o++ = ((unsigned)ff_reverse[buf[6] & 0xf0] << 28) |
> -                   (ff_reverse[buf[5]]        << 20) |
> -                   (ff_reverse[buf[4]]        << 12) |
> -                   (ff_reverse[buf[3] & 0x0f] <<  4);
> +            uint8_t b[6];
> +
> +            b[0] = (ff_reverse[buf[2]       ]      ) ;
> +            b[1] = (ff_reverse[buf[1]       ]      ) ;
> +            b[2] = (ff_reverse[buf[0]       ]      ) ;
> +            b[3] = (ff_reverse[buf[6] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[5] & 0x0f] >>  4) ;
> +            b[4] = (ff_reverse[buf[5] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[4] & 0x0f] >>  4) ;
> +            b[5] = (ff_reverse[buf[4] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[3] & 0x0f] >>  4) ;
> +
> +            if (s->non_pcm_bits == 20) {
> +                b[2] &= 0xf0;
> +                b[5] &= 0xf0;
> +            }
> +
> +            if (s->non_pcm_dec)
> +                for (int i = 0; i < 6; i++)
> +                    *p++ = b[i];
> +            else {
> +                *f32++ = AV_RB24(&b[0]) << 8;
> +                *f32++ = AV_RB24(&b[3]) << 8;
> +            }
>               buf += 7;
>           }
> -        o = (uint32_t *)frame->data[0];
> -        if (channels == 2)
> -            for (i=0; i<frame->nb_samples * 2 - 6; i+=2) {
> -                if (o[i] || o[i+1] || o[i+2] || o[i+3])
> -                    break;
> -                if (o[i+4] == 0x96F87200U && o[i+5] == 0xA54E1F00) {
> -                    non_pcm_data_type = (o[i+6] >> 16) & 0x1F;
> -                    break;
> +    } else if (s->bits == 20) {
> +        uint8_t  *p   = NULL;
> +        uint16_t *f16 = NULL;
> +        uint32_t *f32 = NULL;
> +
> +        if (s->non_pcm_dec)
> +            p = (uint8_t *)s->packet->data;
> +        else if (s->non_pcm_bits == 16)
> +            f16 = (uint16_t *)frame->data[0];
> +        else
> +            f32 = (uint32_t *)frame->data[0];
> +
> +        for (; buf_size > 5; buf_size -= 6) {
> +            uint8_t b[6];
> +
> +            b[0] = (ff_reverse[buf[2] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[1] & 0x0f] >>  4) ;
> +            b[1] = (ff_reverse[buf[1] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[0] & 0x0f] >>  4) ;
> +            b[2] = (ff_reverse[buf[0] & 0xf0] <<  4) ;
> +            b[3] = (ff_reverse[buf[5] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[4] & 0x0f] >>  4) ;
> +            b[4] = (ff_reverse[buf[4] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[3] & 0x0f] >>  4) ;
> +            b[5] = (ff_reverse[buf[3] & 0xf0] <<  4) ;
> +
> +            if (s->non_pcm_dec) {
> +                for (int i = 0; i < 6; i++) {
> +                    if (s->non_pcm_bits == 16 && (i % 3 == 2))
> +                        continue;
> +                    *p++ = b[i];
>                   }
> +            } else if (s->non_pcm_bits == 16) {
> +                *f16++ = AV_RB16(&b[0]);
> +                *f16++ = AV_RB16(&b[3]);
> +            } else {
> +                *f32++ = AV_RB24(&b[0]) << 8;
> +                *f32++ = AV_RB24(&b[3]) << 8;
>               }
> -    } else if (avctx->bits_per_raw_sample == 20) {
> -        uint32_t *o = (uint32_t *)frame->data[0];
> -        for (; buf_size > 5; buf_size -= 6) {
> -            *o++ = ((unsigned)ff_reverse[buf[2] & 0xf0] << 28) |
> -                   (ff_reverse[buf[1]]        << 20) |
> -                   (ff_reverse[buf[0]]        << 12);
> -            *o++ = ((unsigned)ff_reverse[buf[5] & 0xf0] << 28) |
> -                   (ff_reverse[buf[4]]        << 20) |
> -                   (ff_reverse[buf[3]]        << 12);
>               buf += 6;
>           }
> -        o = (uint32_t *)frame->data[0];
> -        if (channels == 2)
> -            for (i=0; i<frame->nb_samples * 2 - 6; i+=2) {
> -                if (o[i] || o[i+1] || o[i+2] || o[i+3])
> -                    break;
> -                if (o[i+4] == 0x6F872000U && o[i+5] == 0x54E1F000) {
> -                    non_pcm_data_type = (o[i+6] >> 16) & 0x1F;
> -                    break;
> -                }
> -            }
>       } else {
> -        uint16_t *o = (uint16_t *)frame->data[0];
> +        uint8_t  *p   = NULL;
> +        uint16_t *f16 = NULL;
> +
> +        if (s->non_pcm_dec)
> +            p = (uint8_t *)s->packet->data;
> +        else
> +            f16 = (uint16_t *)frame->data[0];
> +
>           for (; buf_size > 4; buf_size -= 5) {
> -            *o++ = (ff_reverse[buf[1]]        <<  8) |
> -                    ff_reverse[buf[0]];
> -            *o++ = (ff_reverse[buf[4] & 0xf0] << 12) |
> -                   (ff_reverse[buf[3]]        <<  4) |
> -                   (ff_reverse[buf[2]]        >>  4);
> +            uint8_t b[4];
> +
> +            b[0] = (ff_reverse[buf[1]       ]      ) ;
> +            b[1] = (ff_reverse[buf[0]       ]      ) ;
> +            b[2] = (ff_reverse[buf[4] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[3] & 0x0f] >>  4) ;
> +            b[3] = (ff_reverse[buf[3] & 0xf0] <<  4) |
> +                   (ff_reverse[buf[2] & 0x0f] >>  4) ;
> +
> +            if (s->non_pcm_dec)
> +                for (int i = 0; i < 4; i++)
> +                    *p++ = b[i];
> +            else {
> +                *f16++ = AV_RB16(&b[0]);
> +                *f16++ = AV_RB16(&b[2]);
> +            }
>               buf += 5;
>           }
> -        o = (uint16_t *)frame->data[0];
> -        if (channels == 2)
> -            for (i=0; i<frame->nb_samples * 2 - 6; i+=2) {
> -                if (o[i] || o[i+1] || o[i+2] || o[i+3])
> -                    break;
> -                if (o[i+4] == 0xF872U && o[i+5] == 0x4E1F) {
> -                    non_pcm_data_type = (o[i+6] & 0x1F);
> -                    break;
> -                }
> -            }
>       }
>   
> -    if (non_pcm_data_type != -1) {
> -        if (s->non_pcm_mode == 3) {
> -            av_log(avctx, AV_LOG_ERROR,
> -                   "S302 non PCM mode with data type %d not supported\n",
> -                   non_pcm_data_type);
> -            return AVERROR_PATCHWELCOME;
> +    if (s->non_pcm_dec) {
> +        ret = avcodec_send_packet(s->non_pcm_ctx, s->packet);
> +        if (ret < 0) {
> +            av_log(avctx, AV_LOG_ERROR, "error %d submitting non-pcm packet with pts %"PRId64" for decoding\n", ret, s->packet->pts);
> +            return ret;
>           }
> -        if (s->non_pcm_mode & 1) {
> -            return avpkt->size;
> +        ret = avcodec_receive_frame(s->non_pcm_ctx, s->frame);
> +        if (ret < 0) {
> +            av_log(avctx, AV_LOG_ERROR, "error %d receiving non-pcm decoded frame for packet with pts %"PRId64"\n", ret, s->packet->pts);
> +            return ret;
>           }
> -    }
>   
> -    avctx->sample_rate = 48000;
> +        if (!s->avctx_props_set) {
> +            avctx->sample_fmt  = s->non_pcm_ctx->sample_fmt;
> +            avctx->sample_rate = s->non_pcm_ctx->sample_rate;
> +
> +            av_channel_layout_uninit(&avctx->ch_layout);
> +            ret = av_channel_layout_copy(&avctx->ch_layout, &s->non_pcm_ctx->ch_layout);
> +            if (ret < 0) {
> +                av_log(avctx, AV_LOG_ERROR, "error %d when copying channel layout from non-pcm decoder context to parent context.\n", ret);
> +                return ret;
> +            }
> +            s->avctx_props_set = 1;
> +        }
> +
> +        frame->nb_samples = s->frame->nb_samples;
> +        ret = ff_get_buffer(avctx, frame, 0);
> +        if (ret < 0)
> +            return ret;
> +
> +        for (int ch = 0; ch < s->frame->ch_layout.nb_channels; ch++)
> +            memcpy(frame->extended_data[ch], s->frame->extended_data[ch],
> +                   av_get_bytes_per_sample(s->non_pcm_ctx->sample_fmt) * s->frame->nb_samples);
> +    }
>   
>       *got_frame_ptr = 1;
>   
>       return avpkt->size;
>   }
>   
> +static void s302m_flush(AVCodecContext *avctx)
> +{
> +    S302Context *s = avctx->priv_data;
> +
> +    if (s->non_pcm_dec && s->non_pcm_ctx)
> +        avcodec_flush_buffers(s->non_pcm_ctx);
> +}
> +
> +static av_cold int s302m_close(AVCodecContext *avctx)
> +{
> +    S302Context *s = avctx->priv_data;
> +
> +    avcodec_free_context(&s->non_pcm_ctx);
> +    av_packet_free(&s->packet);
> +    av_frame_free(&s->frame);
> +
> +    return 0;
> +}
> +
>   #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_DECODING_PARAM
>   static const AVOption s302m_options[] = {
> -    {"non_pcm_mode", "Chooses what to do with NON-PCM", offsetof(S302Context, non_pcm_mode), AV_OPT_TYPE_INT, {.i64 = 3}, 0, 3, FLAGS, "non_pcm_mode"},
> -    {"copy"        , "Pass NON-PCM through unchanged"     , 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 3, FLAGS, "non_pcm_mode"},
> -    {"drop"        , "Drop NON-PCM"                       , 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 3, FLAGS, "non_pcm_mode"},
> -    {"decode_copy" , "Decode if possible else passthrough", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 3, FLAGS, "non_pcm_mode"},
> -    {"decode_drop" , "Decode if possible else drop"       , 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 3, FLAGS, "non_pcm_mode"},
> +    {"non_pcm_mode", "Chooses what to do with NON-PCM", offsetof(S302Context, non_pcm_mode), AV_OPT_TYPE_INT, {.i64 = NON_PCM_DEC_ELSE_DROP}, NON_PCM_COPY, NON_PCM_DEC_ELSE_DROP, FLAGS, "non_pcm_mode"},
> +    {"copy"        , "Pass NON-PCM through unchanged"     , 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_COPY},          0, 3, FLAGS, "non_pcm_mode"},
> +    {"drop"        , "Drop NON-PCM"                       , 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_DROP},          0, 3, FLAGS, "non_pcm_mode"},
> +    {"decode_copy" , "Decode if possible else passthrough", 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_DEC_ELSE_COPY}, 0, 3, FLAGS, "non_pcm_mode"},
> +    {"decode_drop" , "Decode if possible else drop"       , 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_DEC_ELSE_DROP}, 0, 3, FLAGS, "non_pcm_mode"},
> +    {"non_pcm_options", "Set options for non-pcm decoder",  offsetof(S302Context, non_pcm_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
>       {NULL}
>   };
>   
> @@ -231,6 +610,9 @@ const FFCodec ff_s302m_decoder = {
>       CODEC_LONG_NAME("SMPTE 302M"),
>       .p.type         = AVMEDIA_TYPE_AUDIO,
>       .p.id           = AV_CODEC_ID_S302M,
> +    .init           = s302m_init,
> +    .close          = s302m_close,
> +    .flush          = s302m_flush,
>       .p.priv_class   = &s302m_class,
>       .priv_data_size = sizeof(S302Context),
>       FF_CODEC_DECODE_CB(s302m_decode_frame),
diff mbox series

Patch

diff --git a/configure b/configure
index 21663000f8..4d7cd83247 100755
--- a/configure
+++ b/configure
@@ -2980,6 +2980,7 @@  rv20_decoder_select="h263_decoder"
 rv20_encoder_select="h263_encoder"
 rv30_decoder_select="golomb h264pred h264qpel mpegvideodec rv34dsp"
 rv40_decoder_select="golomb h264pred h264qpel mpegvideodec rv34dsp"
+s302m_decoder_select="dolby_e_decoder"
 screenpresso_decoder_deps="zlib"
 shorten_decoder_select="bswapdsp"
 sipr_decoder_select="lsp"
diff --git a/doc/decoders.texi b/doc/decoders.texi
index 293c82c2ba..9f85c876bf 100644
--- a/doc/decoders.texi
+++ b/doc/decoders.texi
@@ -347,6 +347,46 @@  configuration. You need to explicitly configure the build with
 An FFmpeg native decoder for Opus exists, so users can decode Opus
 without this library.
 
+@section s302m
+
+SMPTE ST 302 decoder.
+
+SMPTE ST 302 is a method for storing AES3 data format within an MPEG Transport
+Stream. AES3 streams can contain LPCM streams of 2, 4, 6 or 8 channels with a
+bit depth of 16, 20 or 24-bits at a sample rate of 48 kHz.
+They can also contain non-PCM codec streams such as AC-3 or Dolby-E.
+
+Decoding non-PCM streams directly requires that the necessary stream decoder be
+present in the build. At present, only Dolby-E decoding is supported.
+
+@subsection Options
+
+The following options are supported by the s302m decoder.
+
+@table @option
+@item non_pcm_mode @var{mode}
+Specify how to process non-PCM streams
+
+@table @samp
+@item copy
+Treat data as if it were LPCM.
+@item drop
+Discard the stream.
+@item decode_copy
+Decode if possible eise treat the same as @code{copy}.
+@item decode_drop
+Decode if possible eise treat the same as @code{drop}.
+@end table
+
+The default value is @code{decode_drop}. This option does not affect the processing of
+LPCM streams.
+
+@item non_pcm_options @var{options}
+Set options for non-PCM decoder using a list of key=value pairs separated by ":".
+Consult the docs for the non-PCM decoder for its options.
+
+@end table
+
 @c man end AUDIO DECODERS
 
 @chapter Subtitles Decoders
diff --git a/libavcodec/s302m.c b/libavcodec/s302m.c
index f1b41608f3..d890b6f117 100644
--- a/libavcodec/s302m.c
+++ b/libavcodec/s302m.c
@@ -24,21 +24,264 @@ 
 #include "libavutil/intreadwrite.h"
 #include "libavutil/opt.h"
 #include "libavutil/log.h"
+#include "libavutil/dict.h"
 #include "libavutil/reverse.h"
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "get_bits.h"
 #include "decode.h"
 
 #define AES3_HEADER_LEN 4
 
+#define NONPCMSYNC_16MARKER      0x4E1F0F8720
+#define NONPCMSYNC_20MARKER      0x4E1F60F872A0
+#define NONPCMSYNC_24MARKER      0x7E1F690F872A50
+
+#define NONPCMSYNC_16_IN_20MARKER      0x04E1F00F8720
+#define NONPCMSYNC_20_IN_24MARKER      0x04E1F600F872A0
+
+#define IS_NONPCMSYNC_16(state)   ((state & 0xFFFF0FFFF0)     == NONPCMSYNC_16MARKER)
+#define IS_NONPCMSYNC_20(state)   ((state & 0xFFFFF0FFFFF0)   == NONPCMSYNC_20MARKER)
+#define IS_NONPCMSYNC_24(state)   ((state & 0xFFFFFF0FFFFFF0) == NONPCMSYNC_24MARKER)
+
+#define IS_NONPCMSYNC_16_IN_20(state)   ((state & 0x0FFFF00FFFF0)   == NONPCMSYNC_16_IN_20MARKER)
+#define IS_NONPCMSYNC_20_IN_24(state)   ((state & 0x0FFFFF00FFFFF0) == NONPCMSYNC_20_IN_24MARKER)
+
+#define IS_NONPCMSYNC(bit,state)  ( ((bit == 16) &&  IS_NONPCMSYNC_16(state)) || \
+                                    ((bit == 20) && (IS_NONPCMSYNC_20(state) || IS_NONPCMSYNC_16_IN_20(state))) || \
+                                    ((bit == 24) && (IS_NONPCMSYNC_24(state) || IS_NONPCMSYNC_20_IN_24(state))) \
+                                  )
+
+enum non_pcm_modes {
+    NON_PCM_COPY,
+    NON_PCM_DROP,
+    NON_PCM_DEC_ELSE_COPY,
+    NON_PCM_DEC_ELSE_DROP,
+};
+
 typedef struct S302Context {
     AVClass *class;
+
+    int avctx_props_set;
+
+    int channels;
+    int bits;
+
     int non_pcm_mode;
+    int non_pcm_data_type;
+    int non_pcm_bits;
+    int non_pcm_dec;
+
+    AVCodecContext *non_pcm_ctx;
+    AVDictionary   *non_pcm_opts;
+    AVPacket *packet;
+    AVFrame  *frame;
 } S302Context;
 
+static av_cold int s302m_init(AVCodecContext *avctx)
+{
+    S302Context *s = avctx->priv_data;
+
+    s->non_pcm_data_type = -1;
+
+    return 0;
+}
+
+static int s302m_non_pcm_inspect(AVCodecContext *avctx, const uint8_t *buf, int buf_size,
+                                  int *offset, int *length)
+{
+    S302Context *s = avctx->priv_data;
+    const uint8_t *pos = buf;
+    int ret, aes_frm_size, data_type, length_code = 0, pkt_left = buf_size;
+    uint64_t state = 0;
+    uint32_t size;
+
+    if (s->channels != 2) {
+        goto end;
+    }
+
+    aes_frm_size = (s->bits + 4) * 2 / 8;
+    if (pkt_left < aes_frm_size * 2)  // not enough to contain data_type & length_code
+        return AVERROR_INVALIDDATA;
+
+    while (!IS_NONPCMSYNC(s->bits,state) && pkt_left-- >= 1)
+        state = (state << 8) | *pos++;
+
+    if (IS_NONPCMSYNC(s->bits,state)) {
+        GetBitContext gb;
+
+        if (pkt_left < aes_frm_size * 8) {
+            av_log(avctx, AV_LOG_ERROR, "truncated non-pcm frame detected\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        ret = init_get_bits8(&gb, pos, aes_frm_size);
+        if (ret < 0)
+            return ret;
+
+        if (s->bits == 16) {
+            s->non_pcm_bits = 16;
+        } else if (s->bits == 20) {
+            if (IS_NONPCMSYNC_16_IN_20(state))
+                s->non_pcm_bits = 16;
+            else
+                s->non_pcm_bits = 20;
+        } else if (s->bits == 24) {
+            if (IS_NONPCMSYNC_20_IN_24(state))
+                s->non_pcm_bits = 20;
+            else
+                s->non_pcm_bits = 24;
+        }
+
+        skip_bits(&gb, s->bits - 16);
+
+        data_type = ff_reverse[(uint8_t)get_bits(&gb, 5)] >> 3;
+
+        if (s->non_pcm_data_type == -1) {
+            s->non_pcm_data_type = data_type;
+            av_log(avctx, AV_LOG_INFO, "stream has non-pcm data of type %d with %d-bit words in aes3 payload of size %d bits\n", data_type, s->non_pcm_bits, s->bits);
+        } else if (s->non_pcm_data_type != data_type) {
+            av_log(avctx, AV_LOG_DEBUG, "type mismatch of non-pcm type in packet %d vs stream %d. Dropping.\n", data_type, s->non_pcm_data_type);
+            return AVERROR_INVALIDDATA;
+        }
+
+        if (s->non_pcm_mode == NON_PCM_COPY)
+            return 0;
+        else if (s->non_pcm_mode == NON_PCM_DROP)
+            return AVERROR_INVALIDDATA;
+
+        skip_bits(&gb, 15);
+
+        size = get_bits(&gb, s->bits);
+
+        length_code = ((ff_reverse[(uint8_t)((size & 0xFF)          )] << 16) |
+                       (ff_reverse[(uint8_t)((size & 0xFF00)   >> 8 )] << 8 ) |
+                       (ff_reverse[(uint8_t)((size & 0xFF0000) >> 16)]      ) ) >> (24 - s->non_pcm_bits);
+
+        skip_bits(&gb, 4);
+
+        *offset = get_bits_count(&gb)/8 + buf_size - pkt_left;
+        *length = length_code;
+
+        av_log(avctx, AV_LOG_TRACE, "located non-pcm packet at offset %d length code %d.\n", AES3_HEADER_LEN + *offset, length_code);
+
+        return data_type;
+    }
+
+end:
+    if (s->non_pcm_data_type == -1) {
+        s->non_pcm_data_type = 32;  // indicate stream should be treated as LPCM
+        return 0;
+    } else
+        return AVERROR_INVALIDDATA;
+}
+
+static int s302m_setup_non_pcm_handling(AVCodecContext *avctx, const uint8_t *buf, int buf_size)
+{
+    S302Context *s = avctx->priv_data;
+    const AVCodec *codec;
+    enum AVCodecID codec_id;
+    AVDictionary *dec_opts = NULL;
+    int ret;
+
+    if (s->non_pcm_mode > NON_PCM_DROP) {
+        switch (s->non_pcm_data_type) {
+        case 0x1C:
+            codec_id = AV_CODEC_ID_DOLBY_E;
+            break;
+        default:
+            avpriv_report_missing_feature(avctx, "decode of non-pcm data type %d", s->non_pcm_data_type);
+            ret = AVERROR_PATCHWELCOME;
+            goto fail;
+        }
+
+        codec = avcodec_find_decoder(codec_id);
+        if (!codec) {
+            ret = AVERROR_DECODER_NOT_FOUND;
+            goto fail;
+        }
+
+        s->non_pcm_ctx = avcodec_alloc_context3(codec);
+        if (!s->non_pcm_ctx) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        av_dict_copy(&dec_opts, s->non_pcm_opts, 0);
+
+        ret = avcodec_open2(s->non_pcm_ctx, codec, &dec_opts);
+        av_dict_free(&dec_opts);
+        if (ret < 0)
+            goto fail;
+
+        s->packet = av_packet_alloc();
+        if (!s->packet) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        s->frame = av_frame_alloc();
+        if (!s->frame) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+    } else
+        return 0;
+
+    s->non_pcm_dec = 1;
+    return 0;
+
+fail:
+    avcodec_free_context(&s->non_pcm_ctx);
+    av_packet_free(&s->packet);
+    av_frame_free(&s->frame);
+
+    if (s->non_pcm_mode == NON_PCM_DEC_ELSE_COPY)
+        s->non_pcm_mode = NON_PCM_COPY;
+    else if (s->non_pcm_mode == NON_PCM_DEC_ELSE_DROP)
+        s->non_pcm_mode = NON_PCM_DROP;
+
+    return ret;
+}
+
+static int s302m_get_non_pcm_pkt_size(AVCodecContext *avctx, int buf_size, int offset,
+                                      int length_code, int *dec_pkt_size)
+{
+    S302Context *s = avctx->priv_data;
+    int nb_words, word_size, aesframe_size, s302m_read_size;
+
+    if (offset < 0 || offset >= buf_size)
+        return AVERROR_INVALIDDATA;
+
+    switch (s->non_pcm_data_type) {
+    case 0x1C:
+        goto dolby_e;
+    default:
+        return AVERROR_INVALIDDATA;
+    }
+
+dolby_e:
+    {
+    nb_words = length_code / s->non_pcm_bits;
+    nb_words += nb_words & 1;
+
+    word_size = s->non_pcm_bits + 7 >> 3;
+    aesframe_size = (s->bits + 4) * 2 / 8;  // 2 subframes, each with payload + VUCF bits
+
+    *dec_pkt_size = nb_words * word_size;
+    s302m_read_size = aesframe_size * nb_words/2;
+
+    if (offset + s302m_read_size > buf_size)
+        return AVERROR_INVALIDDATA;
+
+    return s302m_read_size;
+    }
+}
+
 static int s302m_parse_frame_header(AVCodecContext *avctx, const uint8_t *buf,
                                     int buf_size)
 {
+    S302Context *s = avctx->priv_data;
     uint32_t h;
     int frame_size, channels, bits;
 
@@ -66,33 +309,15 @@  static int s302m_parse_frame_header(AVCodecContext *avctx, const uint8_t *buf,
         return AVERROR_INVALIDDATA;
     }
 
-    /* Set output properties */
-    avctx->bits_per_raw_sample = bits;
-    if (bits > 16)
-        avctx->sample_fmt = AV_SAMPLE_FMT_S32;
-    else
-        avctx->sample_fmt = AV_SAMPLE_FMT_S16;
-
-    av_channel_layout_uninit(&avctx->ch_layout);
-    switch(channels) {
-        case 2:
-            avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
-            break;
-        case 4:
-            avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_QUAD;
-            break;
-        case 6:
-            avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1_BACK;
-            break;
-        case 8:
-            av_channel_layout_from_mask(&avctx->ch_layout,
-                                        AV_CH_LAYOUT_5POINT1_BACK | AV_CH_LAYOUT_STEREO_DOWNMIX);
-            break;
-        default:
-            avctx->ch_layout.order       = AV_CHANNEL_ORDER_UNSPEC;
-            avctx->ch_layout.nb_channels = channels;
-            break;
-    }
+    if (!s->channels)
+        s->channels = channels;
+    else if (s->channels != channels)
+        return AVERROR_INVALIDDATA;
+
+    if (!s->bits)
+        s->bits = bits;
+    else if (s->bits != bits)
+        return AVERROR_INVALIDDATA;
 
     return frame_size;
 }
@@ -103,119 +328,273 @@  static int s302m_decode_frame(AVCodecContext *avctx, AVFrame *frame,
     S302Context *s = avctx->priv_data;
     const uint8_t *buf = avpkt->data;
     int buf_size       = avpkt->size;
-    int block_size, ret, channels;
-    int i;
-    int non_pcm_data_type = -1;
+    int block_size, ret, channels, frame_size;
+    int non_pcm_offset = -1, non_pcm_length = 0;
+    int dec_pkt_size = 0;
 
-    int frame_size = s302m_parse_frame_header(avctx, buf, buf_size);
+    if (s->non_pcm_mode == NON_PCM_DROP && s->non_pcm_data_type != -1 && s->non_pcm_data_type != 32)
+        return avpkt->size;
+
+    frame_size = s302m_parse_frame_header(avctx, buf, buf_size);
     if (frame_size < 0)
         return frame_size;
 
     buf_size -= AES3_HEADER_LEN;
     buf      += AES3_HEADER_LEN;
 
-    /* get output buffer */
-    block_size = (avctx->bits_per_raw_sample + 4) / 4;
-    channels = avctx->ch_layout.nb_channels;
-    frame->nb_samples = 2 * (buf_size / block_size) / channels;
-    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
-        return ret;
+    // set non-pcm status if not determined
+    // else extract offset and length if non-pcm can be decoded
+    if (s->non_pcm_data_type == -1 || s->non_pcm_dec) {
+        ret = s302m_non_pcm_inspect(avctx, buf, buf_size, &non_pcm_offset, &non_pcm_length);
+        if (ret >= 0 && s->non_pcm_data_type != 32 && !s->non_pcm_dec)
+            ret = s302m_setup_non_pcm_handling(avctx, buf, buf_size);
+        else if (ret < 0)
+            return avpkt->size;
+    }
+
+    if (s->non_pcm_data_type == -1)
+        return AVERROR_INVALIDDATA;  // we should know data type in order to proceed with output
+
+    if (!s->non_pcm_dec && !s->avctx_props_set) {
+        /* Set output properties */
+        avctx->bits_per_raw_sample = s->non_pcm_bits ? s->non_pcm_bits : s->bits;
+        if (avctx->bits_per_raw_sample > 16)
+            avctx->sample_fmt = AV_SAMPLE_FMT_S32;
+        else
+            avctx->sample_fmt = AV_SAMPLE_FMT_S16;
 
-    avctx->bit_rate = 48000 * channels * (avctx->bits_per_raw_sample + 4) +
-                      32 * 48000 / frame->nb_samples;
-    buf_size = (frame->nb_samples * channels / 2) * block_size;
+        av_channel_layout_uninit(&avctx->ch_layout);
+        switch(s->channels) {
+            case 2:
+                avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
+                break;
+            case 4:
+                avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_QUAD;
+                break;
+            case 6:
+                avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1_BACK;
+                break;
+            case 8:
+                av_channel_layout_from_mask(&avctx->ch_layout,
+                                            AV_CH_LAYOUT_5POINT1_BACK | AV_CH_LAYOUT_STEREO_DOWNMIX);
+                break;
+            default:
+                avctx->ch_layout.order       = AV_CHANNEL_ORDER_UNSPEC;
+                avctx->ch_layout.nb_channels = channels;
+                break;
+        }
+
+        avctx->sample_rate = 48000;
+        s->avctx_props_set = 1;
+    }
+
+    if (s->non_pcm_dec) {
+        buf_size = s302m_get_non_pcm_pkt_size(avctx, buf_size, non_pcm_offset, non_pcm_length, &dec_pkt_size);
+        if (buf_size < 0)
+            return AVERROR_INVALIDDATA;
+        buf += non_pcm_offset;
+
+        if (dec_pkt_size > s->packet->size) {
+            ret = av_grow_packet(s->packet, dec_pkt_size - s->packet->size);
+            if (ret < 0)
+                return ret;
+        }
+        ret = av_packet_make_writable(s->packet);
+        if (ret < 0)
+            return ret;
+        memset(s->packet->data, 0, s->packet->size);
+
+        ret = av_packet_copy_props(s->packet, avpkt);
+        if (ret < 0)
+            return ret;
+    } else {
+        /* get output buffer */
+        block_size = (s->bits + 4) / 4;
+        channels = s->channels;
+        frame->nb_samples = 2 * (buf_size / block_size) / channels;
+        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
+            return ret;
+
+        buf_size = (frame->nb_samples * channels / 2) * block_size;
+
+        avctx->bit_rate = 48000 * s->channels * ((s->non_pcm_bits ? s->non_pcm_bits : s->bits) + 4) +
+                          32 * 48000 / frame->nb_samples;
+    }
+
+    if (s->bits == 24) {
+        uint8_t  *p   = NULL;
+        uint32_t *f32 = NULL;
+
+        if (s->non_pcm_dec)
+            p = (uint8_t *)s->packet->data;
+        else
+            f32 = (uint32_t *)frame->data[0];
 
-    if (avctx->bits_per_raw_sample == 24) {
-        uint32_t *o = (uint32_t *)frame->data[0];
         for (; buf_size > 6; buf_size -= 7) {
-            *o++ = ((unsigned)ff_reverse[buf[2]]        << 24) |
-                   (ff_reverse[buf[1]]        << 16) |
-                   (ff_reverse[buf[0]]        <<  8);
-            *o++ = ((unsigned)ff_reverse[buf[6] & 0xf0] << 28) |
-                   (ff_reverse[buf[5]]        << 20) |
-                   (ff_reverse[buf[4]]        << 12) |
-                   (ff_reverse[buf[3] & 0x0f] <<  4);
+            uint8_t b[6];
+
+            b[0] = (ff_reverse[buf[2]       ]      ) ;
+            b[1] = (ff_reverse[buf[1]       ]      ) ;
+            b[2] = (ff_reverse[buf[0]       ]      ) ;
+            b[3] = (ff_reverse[buf[6] & 0xf0] <<  4) |
+                   (ff_reverse[buf[5] & 0x0f] >>  4) ;
+            b[4] = (ff_reverse[buf[5] & 0xf0] <<  4) |
+                   (ff_reverse[buf[4] & 0x0f] >>  4) ;
+            b[5] = (ff_reverse[buf[4] & 0xf0] <<  4) |
+                   (ff_reverse[buf[3] & 0x0f] >>  4) ;
+
+            if (s->non_pcm_bits == 20) {
+                b[2] &= 0xf0;
+                b[5] &= 0xf0;
+            }
+
+            if (s->non_pcm_dec)
+                for (int i = 0; i < 6; i++)
+                    *p++ = b[i];
+            else {
+                *f32++ = AV_RB24(&b[0]) << 8;
+                *f32++ = AV_RB24(&b[3]) << 8;
+            }
             buf += 7;
         }
-        o = (uint32_t *)frame->data[0];
-        if (channels == 2)
-            for (i=0; i<frame->nb_samples * 2 - 6; i+=2) {
-                if (o[i] || o[i+1] || o[i+2] || o[i+3])
-                    break;
-                if (o[i+4] == 0x96F87200U && o[i+5] == 0xA54E1F00) {
-                    non_pcm_data_type = (o[i+6] >> 16) & 0x1F;
-                    break;
+    } else if (s->bits == 20) {
+        uint8_t  *p   = NULL;
+        uint16_t *f16 = NULL;
+        uint32_t *f32 = NULL;
+
+        if (s->non_pcm_dec)
+            p = (uint8_t *)s->packet->data;
+        else if (s->non_pcm_bits == 16)
+            f16 = (uint16_t *)frame->data[0];
+        else
+            f32 = (uint32_t *)frame->data[0];
+
+        for (; buf_size > 5; buf_size -= 6) {
+            uint8_t b[6];
+
+            b[0] = (ff_reverse[buf[2] & 0xf0] <<  4) |
+                   (ff_reverse[buf[1] & 0x0f] >>  4) ;
+            b[1] = (ff_reverse[buf[1] & 0xf0] <<  4) |
+                   (ff_reverse[buf[0] & 0x0f] >>  4) ;
+            b[2] = (ff_reverse[buf[0] & 0xf0] <<  4) ;
+            b[3] = (ff_reverse[buf[5] & 0xf0] <<  4) |
+                   (ff_reverse[buf[4] & 0x0f] >>  4) ;
+            b[4] = (ff_reverse[buf[4] & 0xf0] <<  4) |
+                   (ff_reverse[buf[3] & 0x0f] >>  4) ;
+            b[5] = (ff_reverse[buf[3] & 0xf0] <<  4) ;
+
+            if (s->non_pcm_dec) {
+                for (int i = 0; i < 6; i++) {
+                    if (s->non_pcm_bits == 16 && (i % 3 == 2))
+                        continue;
+                    *p++ = b[i];
                 }
+            } else if (s->non_pcm_bits == 16) {
+                *f16++ = AV_RB16(&b[0]);
+                *f16++ = AV_RB16(&b[3]);
+            } else {
+                *f32++ = AV_RB24(&b[0]) << 8;
+                *f32++ = AV_RB24(&b[3]) << 8;
             }
-    } else if (avctx->bits_per_raw_sample == 20) {
-        uint32_t *o = (uint32_t *)frame->data[0];
-        for (; buf_size > 5; buf_size -= 6) {
-            *o++ = ((unsigned)ff_reverse[buf[2] & 0xf0] << 28) |
-                   (ff_reverse[buf[1]]        << 20) |
-                   (ff_reverse[buf[0]]        << 12);
-            *o++ = ((unsigned)ff_reverse[buf[5] & 0xf0] << 28) |
-                   (ff_reverse[buf[4]]        << 20) |
-                   (ff_reverse[buf[3]]        << 12);
             buf += 6;
         }
-        o = (uint32_t *)frame->data[0];
-        if (channels == 2)
-            for (i=0; i<frame->nb_samples * 2 - 6; i+=2) {
-                if (o[i] || o[i+1] || o[i+2] || o[i+3])
-                    break;
-                if (o[i+4] == 0x6F872000U && o[i+5] == 0x54E1F000) {
-                    non_pcm_data_type = (o[i+6] >> 16) & 0x1F;
-                    break;
-                }
-            }
     } else {
-        uint16_t *o = (uint16_t *)frame->data[0];
+        uint8_t  *p   = NULL;
+        uint16_t *f16 = NULL;
+
+        if (s->non_pcm_dec)
+            p = (uint8_t *)s->packet->data;
+        else
+            f16 = (uint16_t *)frame->data[0];
+
         for (; buf_size > 4; buf_size -= 5) {
-            *o++ = (ff_reverse[buf[1]]        <<  8) |
-                    ff_reverse[buf[0]];
-            *o++ = (ff_reverse[buf[4] & 0xf0] << 12) |
-                   (ff_reverse[buf[3]]        <<  4) |
-                   (ff_reverse[buf[2]]        >>  4);
+            uint8_t b[4];
+
+            b[0] = (ff_reverse[buf[1]       ]      ) ;
+            b[1] = (ff_reverse[buf[0]       ]      ) ;
+            b[2] = (ff_reverse[buf[4] & 0xf0] <<  4) |
+                   (ff_reverse[buf[3] & 0x0f] >>  4) ;
+            b[3] = (ff_reverse[buf[3] & 0xf0] <<  4) |
+                   (ff_reverse[buf[2] & 0x0f] >>  4) ;
+
+            if (s->non_pcm_dec)
+                for (int i = 0; i < 4; i++)
+                    *p++ = b[i];
+            else {
+                *f16++ = AV_RB16(&b[0]);
+                *f16++ = AV_RB16(&b[2]);
+            }
             buf += 5;
         }
-        o = (uint16_t *)frame->data[0];
-        if (channels == 2)
-            for (i=0; i<frame->nb_samples * 2 - 6; i+=2) {
-                if (o[i] || o[i+1] || o[i+2] || o[i+3])
-                    break;
-                if (o[i+4] == 0xF872U && o[i+5] == 0x4E1F) {
-                    non_pcm_data_type = (o[i+6] & 0x1F);
-                    break;
-                }
-            }
     }
 
-    if (non_pcm_data_type != -1) {
-        if (s->non_pcm_mode == 3) {
-            av_log(avctx, AV_LOG_ERROR,
-                   "S302 non PCM mode with data type %d not supported\n",
-                   non_pcm_data_type);
-            return AVERROR_PATCHWELCOME;
+    if (s->non_pcm_dec) {
+        ret = avcodec_send_packet(s->non_pcm_ctx, s->packet);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "error %d submitting non-pcm packet with pts %"PRId64" for decoding\n", ret, s->packet->pts);
+            return ret;
         }
-        if (s->non_pcm_mode & 1) {
-            return avpkt->size;
+        ret = avcodec_receive_frame(s->non_pcm_ctx, s->frame);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "error %d receiving non-pcm decoded frame for packet with pts %"PRId64"\n", ret, s->packet->pts);
+            return ret;
         }
-    }
 
-    avctx->sample_rate = 48000;
+        if (!s->avctx_props_set) {
+            avctx->sample_fmt  = s->non_pcm_ctx->sample_fmt;
+            avctx->sample_rate = s->non_pcm_ctx->sample_rate;
+
+            av_channel_layout_uninit(&avctx->ch_layout);
+            ret = av_channel_layout_copy(&avctx->ch_layout, &s->non_pcm_ctx->ch_layout);
+            if (ret < 0) {
+                av_log(avctx, AV_LOG_ERROR, "error %d when copying channel layout from non-pcm decoder context to parent context.\n", ret);
+                return ret;
+            }
+            s->avctx_props_set = 1;
+        }
+
+        frame->nb_samples = s->frame->nb_samples;
+        ret = ff_get_buffer(avctx, frame, 0);
+        if (ret < 0)
+            return ret;
+
+        for (int ch = 0; ch < s->frame->ch_layout.nb_channels; ch++)
+            memcpy(frame->extended_data[ch], s->frame->extended_data[ch],
+                   av_get_bytes_per_sample(s->non_pcm_ctx->sample_fmt) * s->frame->nb_samples);
+    }
 
     *got_frame_ptr = 1;
 
     return avpkt->size;
 }
 
+static void s302m_flush(AVCodecContext *avctx)
+{
+    S302Context *s = avctx->priv_data;
+
+    if (s->non_pcm_dec && s->non_pcm_ctx)
+        avcodec_flush_buffers(s->non_pcm_ctx);
+}
+
+static av_cold int s302m_close(AVCodecContext *avctx)
+{
+    S302Context *s = avctx->priv_data;
+
+    avcodec_free_context(&s->non_pcm_ctx);
+    av_packet_free(&s->packet);
+    av_frame_free(&s->frame);
+
+    return 0;
+}
+
 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_DECODING_PARAM
 static const AVOption s302m_options[] = {
-    {"non_pcm_mode", "Chooses what to do with NON-PCM", offsetof(S302Context, non_pcm_mode), AV_OPT_TYPE_INT, {.i64 = 3}, 0, 3, FLAGS, "non_pcm_mode"},
-    {"copy"        , "Pass NON-PCM through unchanged"     , 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 3, FLAGS, "non_pcm_mode"},
-    {"drop"        , "Drop NON-PCM"                       , 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 3, FLAGS, "non_pcm_mode"},
-    {"decode_copy" , "Decode if possible else passthrough", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 3, FLAGS, "non_pcm_mode"},
-    {"decode_drop" , "Decode if possible else drop"       , 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 3, FLAGS, "non_pcm_mode"},
+    {"non_pcm_mode", "Chooses what to do with NON-PCM", offsetof(S302Context, non_pcm_mode), AV_OPT_TYPE_INT, {.i64 = NON_PCM_DEC_ELSE_DROP}, NON_PCM_COPY, NON_PCM_DEC_ELSE_DROP, FLAGS, "non_pcm_mode"},
+    {"copy"        , "Pass NON-PCM through unchanged"     , 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_COPY},          0, 3, FLAGS, "non_pcm_mode"},
+    {"drop"        , "Drop NON-PCM"                       , 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_DROP},          0, 3, FLAGS, "non_pcm_mode"},
+    {"decode_copy" , "Decode if possible else passthrough", 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_DEC_ELSE_COPY}, 0, 3, FLAGS, "non_pcm_mode"},
+    {"decode_drop" , "Decode if possible else drop"       , 0, AV_OPT_TYPE_CONST, {.i64 = NON_PCM_DEC_ELSE_DROP}, 0, 3, FLAGS, "non_pcm_mode"},
+    {"non_pcm_options", "Set options for non-pcm decoder",  offsetof(S302Context, non_pcm_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
     {NULL}
 };
 
@@ -231,6 +610,9 @@  const FFCodec ff_s302m_decoder = {
     CODEC_LONG_NAME("SMPTE 302M"),
     .p.type         = AVMEDIA_TYPE_AUDIO,
     .p.id           = AV_CODEC_ID_S302M,
+    .init           = s302m_init,
+    .close          = s302m_close,
+    .flush          = s302m_flush,
     .p.priv_class   = &s302m_class,
     .priv_data_size = sizeof(S302Context),
     FF_CODEC_DECODE_CB(s302m_decode_frame),