diff mbox series

[FFmpeg-devel,1/2] avformat/mxf: support MCA audio information

Message ID 20211203095742.16108-1-marc-antoine.arnaud@luminvent.com
State New
Headers show
Series [FFmpeg-devel,1/2] avformat/mxf: support MCA audio information | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Marc-Antoine ARNAUD Dec. 3, 2021, 9:57 a.m. UTC
---
 doc/demuxers.texi     |  10 ++
 libavformat/mxf.h     |   3 +
 libavformat/mxfdec.c  | 335 +++++++++++++++++++++++++++++++++++++++++-
 libavformat/version.h |   2 +-
 4 files changed, 343 insertions(+), 7 deletions(-)

Comments

Marc-Antoine ARNAUD Dec. 17, 2021, 4:58 p.m. UTC | #1
Hi all,

Can I have an update on this patch submission ?
Is something required to be done before it can be merged ?

Thanks you,
Marc-Antoine


Le ven. 3 déc. 2021 à 10:57, Marc-Antoine Arnaud
<marc-antoine.arnaud@luminvent.com> a écrit :
>
> ---
>  doc/demuxers.texi     |  10 ++
>  libavformat/mxf.h     |   3 +
>  libavformat/mxfdec.c  | 335 +++++++++++++++++++++++++++++++++++++++++-
>  libavformat/version.h |   2 +-
>  4 files changed, 343 insertions(+), 7 deletions(-)
>
> diff --git a/doc/demuxers.texi b/doc/demuxers.texi
> index cab8a7072c..23b6753602 100644
> --- a/doc/demuxers.texi
> +++ b/doc/demuxers.texi
> @@ -770,6 +770,16 @@ MJPEG stream. Turning this option on by setting it to 1 will result in a stricte
>  of the boundary value.
>  @end table
>
> +@section mxf
> +
> +MXF demuxer.
> +
> +@table @option
> +
> +@item -skip_audio_reordering @var{bool}
> +This option will disable the audio reordering based on Multi-Channel Audio (MCA) labelling (SMPTE ST-377-4).
> +@end table
> +
>  @section rawvideo
>
>  Raw video demuxer.
> diff --git a/libavformat/mxf.h b/libavformat/mxf.h
> index fe9c52732c..d53a16df51 100644
> --- a/libavformat/mxf.h
> +++ b/libavformat/mxf.h
> @@ -50,6 +50,9 @@ enum MXFMetadataSetType {
>      TaggedValue,
>      TapeDescriptor,
>      AVCSubDescriptor,
> +    AudioChannelLabelSubDescriptor,
> +    SoundfieldGroupLabelSubDescriptor,
> +    GroupOfSoundfieldGroupsLabelSubDescriptor,
>  };
>
>  enum MXFFrameLayout {
> diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
> index af9d33f796..6e1da75542 100644
> --- a/libavformat/mxfdec.c
> +++ b/libavformat/mxfdec.c
> @@ -51,11 +51,14 @@
>  #include "libavutil/mastering_display_metadata.h"
>  #include "libavutil/mathematics.h"
>  #include "libavcodec/bytestream.h"
> +#include "libavcodec/internal.h"
> +#include "libavutil/channel_layout.h"
>  #include "libavutil/intreadwrite.h"
>  #include "libavutil/parseutils.h"
>  #include "libavutil/timecode.h"
>  #include "libavutil/opt.h"
>  #include "avformat.h"
> +#include "avlanguage.h"
>  #include "internal.h"
>  #include "mxf.h"
>
> @@ -177,6 +180,8 @@ typedef struct {
>      int body_sid;
>      MXFWrappingScheme wrapping;
>      int edit_units_per_packet; /* how many edit units to read at a time (PCM, ClipWrapped) */
> +    int require_reordering;
> +    int channel_ordering[FF_SANE_NB_CHANNELS];
>  } MXFTrack;
>
>  typedef struct MXFDescriptor {
> @@ -205,6 +210,8 @@ typedef struct MXFDescriptor {
>      unsigned int vert_subsampling;
>      UID *file_descriptors_refs;
>      int file_descriptors_count;
> +    UID *sub_descriptors_refs;
> +    int sub_descriptors_count;
>      int linked_track_id;
>      uint8_t *extradata;
>      int extradata_size;
> @@ -217,6 +224,18 @@ typedef struct MXFDescriptor {
>      size_t coll_size;
>  } MXFDescriptor;
>
> +typedef struct MXFMCASubDescriptor {
> +    MXFMetadataSet meta;
> +    UID uid;
> +    UID mca_link_id;
> +    UID soundfield_group_link_id;
> +    UID *group_of_soundfield_groups_link_id_refs;
> +    int group_of_soundfield_groups_link_id_count;
> +    UID mca_label_dictionary_id;
> +    int mca_channel_id;
> +    char *language;
> +} MXFMCASubDescriptor;
> +
>  typedef struct MXFIndexTableSegment {
>      MXFMetadataSet meta;
>      int edit_unit_byte_count;
> @@ -290,6 +309,7 @@ typedef struct MXFContext {
>      int nb_index_tables;
>      MXFIndexTable *index_tables;
>      int eia608_extract;
> +    int skip_audio_reordering;
>  } MXFContext;
>
>  /* NOTE: klv_offset is not set (-1) for local keys */
> @@ -311,6 +331,7 @@ static const uint8_t mxf_system_item_key_cp[]              = { 0x06,0x0e,0x2b,0x
>  static const uint8_t mxf_system_item_key_gc[]              = { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 };
>  static const uint8_t mxf_klv_key[]                         = { 0x06,0x0e,0x2b,0x34 };
>  static const uint8_t mxf_apple_coll_prefix[]               = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01 };
> +
>  /* complete keys to match */
>  static const uint8_t mxf_crypto_source_container_ul[]      = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 };
>  static const uint8_t mxf_encrypted_triplet_key[]           = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
> @@ -323,6 +344,17 @@ static const uint8_t mxf_indirect_value_utf16be[]          = { 0x42,0x01,0x10,0x
>  static const uint8_t mxf_apple_coll_max_cll[]              = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x01 };
>  static const uint8_t mxf_apple_coll_max_fall[]             = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x02 };
>
> +static const uint8_t mxf_mca_label_dictionary_id[]         = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x01,0x00,0x00,0x00 };
> +static const uint8_t mxf_mca_tag_symbol[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x02,0x00,0x00,0x00 };
> +static const uint8_t mxf_mca_tag_name[]                    = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x03,0x00,0x00,0x00 };
> +static const uint8_t mxf_group_of_soundfield_groups_link_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x04,0x00,0x00,0x00 };
> +static const uint8_t mxf_mca_link_id[]                     = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x05,0x00,0x00,0x00 };
> +static const uint8_t mxf_mca_channel_id[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x04,0x0a,0x00,0x00,0x00,0x00 };
> +static const uint8_t mxf_soundfield_group_link_id[]        = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x06,0x00,0x00,0x00 };
> +static const uint8_t mxf_mca_rfc5646_spoken_language[]     = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0d,0x03,0x01,0x01,0x02,0x03,0x15,0x00,0x00 };
> +
> +static const uint8_t mxf_sub_descriptor[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00 };
> +
>  static const uint8_t mxf_mastering_display_prefix[13]      = { FF_MXF_MasteringDisplay_PREFIX };
>  static const uint8_t mxf_mastering_display_uls[4][16] = {
>      FF_MXF_MasteringDisplayPrimaries,
> @@ -343,6 +375,13 @@ static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx)
>          av_freep(&((MXFDescriptor *)*ctx)->mastering);
>          av_freep(&((MXFDescriptor *)*ctx)->coll);
>          av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs);
> +        av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
> +        break;
> +    case AudioChannelLabelSubDescriptor:
> +    case SoundfieldGroupLabelSubDescriptor:
> +    case GroupOfSoundfieldGroupsLabelSubDescriptor:
> +        av_freep(&((MXFMCASubDescriptor *)*ctx)->language);
> +        av_freep(&((MXFMCASubDescriptor *)*ctx)->group_of_soundfield_groups_link_id_refs);
>          break;
>      case Sequence:
>          av_freep(&((MXFSequence *)*ctx)->structural_components_refs);
> @@ -906,6 +945,30 @@ static int mxf_read_strong_ref_array(AVIOContext *pb, UID **refs, int *count)
>      return 0;
>  }
>
> +static inline int mxf_read_us_ascii_string(AVIOContext *pb, int size, char** str)
> +{
> +    int ret;
> +    size_t buf_size;
> +
> +    if (size < 0 || size > INT_MAX - 1)
> +        return AVERROR(EINVAL);
> +
> +    buf_size = size + 1;
> +    av_free(*str);
> +    *str = av_malloc(buf_size);
> +    if (!*str)
> +        return AVERROR(ENOMEM);
> +
> +    ret = avio_get_str(pb, size, *str, buf_size);
> +
> +    if (ret < 0) {
> +        av_freep(str);
> +        return ret;
> +    }
> +
> +    return ret;
> +}
> +
>  static inline int mxf_read_utf16_string(AVIOContext *pb, int size, char** str, int be)
>  {
>      int ret;
> @@ -1360,11 +1423,40 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
>                  descriptor->coll->MaxFALL = avio_rb16(pb);
>              }
>          }
> +
> +        if (IS_KLV_KEY(uid, mxf_sub_descriptor))
> +            return mxf_read_strong_ref_array(pb, &descriptor->sub_descriptors_refs, &descriptor->sub_descriptors_count);
> +
>          break;
>      }
>      return 0;
>  }
>
> +static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
> +{
> +    MXFMCASubDescriptor *mca_sub_descriptor = arg;
> +
> +    if (IS_KLV_KEY(uid, mxf_mca_label_dictionary_id))
> +        avio_read(pb, mca_sub_descriptor->mca_label_dictionary_id, 16);
> +
> +    if (IS_KLV_KEY(uid, mxf_mca_link_id))
> +        avio_read(pb, mca_sub_descriptor->mca_link_id, 16);
> +
> +    if (IS_KLV_KEY(uid, mxf_soundfield_group_link_id))
> +        avio_read(pb, mca_sub_descriptor->soundfield_group_link_id, 16);
> +
> +    if (IS_KLV_KEY(uid, mxf_group_of_soundfield_groups_link_id))
> +        return mxf_read_strong_ref_array(pb, &mca_sub_descriptor->group_of_soundfield_groups_link_id_refs, &mca_sub_descriptor->group_of_soundfield_groups_link_id_count);
> +
> +    if (IS_KLV_KEY(uid, mxf_mca_channel_id))
> +        mca_sub_descriptor->mca_channel_id = avio_rb32(pb);
> +
> +    if (IS_KLV_KEY(uid, mxf_mca_rfc5646_spoken_language))
> +        return mxf_read_us_ascii_string(pb, size, &mca_sub_descriptor->language);
> +
> +    return 0;
> +}
> +
>  static int mxf_read_indirect_value(void *arg, AVIOContext *pb, int size)
>  {
>      MXFTaggedValue *tagged_value = arg;
> @@ -1494,6 +1586,50 @@ static const MXFCodecUL mxf_data_essence_container_uls[] = {
>      { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  0, AV_CODEC_ID_NONE },
>  };
>
> +typedef struct MXFChannelOrderingUL {
> +    UID uid;
> +    uint64_t layout_mask;
> +    enum AVAudioServiceType service_type;
> +} MXFChannelOrderingUL;
> +
> +static const MXFChannelOrderingUL mxf_channel_ordering[] = {
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Left
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Low Frequency Effects
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, AV_CH_BACK_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, AV_CH_BACK_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT_OF_CENTER,  AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Center
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, // Hearing impaired audio channel
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00 }, AV_CH_TOP_FRONT_LEFT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00 }, AV_CH_TOP_FRONT_RIGHT,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00 }, AV_CH_TOP_FRONT_CENTER,      AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00 }, AV_CH_TOP_BACK_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00 }, AV_CH_TOP_BACK_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00 }, AV_CH_TOP_CENTER,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Top Surround
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00 }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front Subwoofer
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00 }, AV_CH_LOW_FREQUENCY_2,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Front Subwoofer
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00 }, AV_CH_TOP_BACK_CENTER,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear Height
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00 }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00 }, AV_CH_BOTTOM_FRONT_LEFT,     AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00 }, AV_CH_BOTTOM_FRONT_RIGHT,    AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below
> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00 }, AV_CH_BOTTOM_FRONT_CENTER,   AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Below
> +    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0,                           AV_AUDIO_SERVICE_TYPE_NB },
> +};
> +
>  static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul)
>  {
>      int val;
> @@ -2292,6 +2428,156 @@ static enum AVColorRange mxf_get_color_range(MXFContext *mxf, MXFDescriptor *des
>      return AVCOL_RANGE_UNSPECIFIED;
>  }
>
> +static int is_pcm(enum AVCodecID codec_id)
> +{
> +    /* we only care about "normal" PCM codecs until we get samples */
> +    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
> +}
> +
> +static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary **met)
> +{
> +    // language abbr should contain at least 2 chars
> +    if (rfc5646 && strlen(rfc5646) > 1) {
> +        char primary_tag[4] =
> +            {rfc5646[0], rfc5646[1], rfc5646[2] != '-' ? rfc5646[2] : '\0', '\0'};
> +
> +        const char *iso6392       = ff_convert_lang_to(primary_tag,
> +                                                       AV_LANG_ISO639_2_BIBL);
> +        if (iso6392)
> +            return(av_dict_set(met, "language", iso6392, 0));
> +    }
> +    return 0;
> +}
> +
> +static MXFMCASubDescriptor *find_mca_link_id(MXFContext *mxf, enum MXFMetadataSetType type, UID *mca_link_id)
> +{
> +    for (int k = 0; k < mxf->metadata_sets_count; k++) {
> +        MXFMCASubDescriptor *group = (MXFMCASubDescriptor*)mxf->metadata_sets[k];
> +        if (group->meta.type == type && !memcmp(&group->mca_link_id, mca_link_id, 16))
> +            return group;
> +    }
> +    return NULL;
> +}
> +
> +static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescriptor *descriptor, AVStream *st)
> +{
> +    uint64_t routing[FF_SANE_NB_CHANNELS] = {0};
> +    char *language = NULL;
> +    int ambigous_language = 0;
> +    enum AVAudioServiceType service_type = AV_AUDIO_SERVICE_TYPE_NB;
> +    int ambigous_service_type = 0;
> +    int has_channel_label = 0;
> +
> +    for (int i = 0; i < descriptor->sub_descriptors_count; i++) {
> +        char *channel_language;
> +
> +        MXFMCASubDescriptor *label = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[i], AudioChannelLabelSubDescriptor);
> +        if (label == NULL)
> +            continue;
> +
> +        has_channel_label = 1;
> +        for (const MXFChannelOrderingUL* channel_ordering = mxf_channel_ordering; channel_ordering->uid[0]; channel_ordering++) {
> +            if (IS_KLV_KEY(channel_ordering->uid, label->mca_label_dictionary_id)) {
> +                int target_channel = label->mca_channel_id;
> +                if (target_channel == 0 && descriptor->channels == 1)
> +                    target_channel = 1;
> +                if (target_channel <= 0 || target_channel > descriptor->channels) {
> +                    av_log(mxf->fc, AV_LOG_ERROR, "AudioChannelLabelSubDescriptor has invalid MCA channel ID %d\n", target_channel);
> +                    return AVERROR_INVALIDDATA;
> +                }
> +                routing[target_channel - 1] = channel_ordering->layout_mask;
> +                if (service_type == AV_AUDIO_SERVICE_TYPE_NB)
> +                    service_type = channel_ordering->service_type;
> +                else if (service_type != channel_ordering->service_type)
> +                    ambigous_service_type = 1;
> +                break;
> +            }
> +        }
> +
> +        channel_language = label->language;
> +        if (!channel_language) {
> +            MXFMCASubDescriptor *group = find_mca_link_id(mxf, SoundfieldGroupLabelSubDescriptor, &label->soundfield_group_link_id);
> +            if (group) {
> +                channel_language = group->language;
> +                if (!channel_language && group->group_of_soundfield_groups_link_id_count) {
> +                    MXFMCASubDescriptor *supergroup = find_mca_link_id(mxf, GroupOfSoundfieldGroupsLabelSubDescriptor,
> +                                                                       group->group_of_soundfield_groups_link_id_refs);
> +                    if (supergroup)
> +                        channel_language = supergroup->language;
> +                }
> +            }
> +        }
> +        if (channel_language) {
> +            if (language && strcmp(language, channel_language))
> +                ambigous_language = 1;
> +            else
> +                language = channel_language;
> +        }
> +    }
> +
> +    if (language && !ambigous_language) {
> +       int ret = set_language(mxf->fc, language, &st->metadata);
> +       if (ret < 0)
> +           return ret;
> +    }
> +
> +    if (service_type != AV_AUDIO_SERVICE_TYPE_NB && service_type != AV_AUDIO_SERVICE_TYPE_MAIN && !ambigous_service_type) {
> +        enum AVAudioServiceType *ast;
> +        uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast));
> +        if (!side_data)
> +            return AVERROR(ENOMEM);
> +        ast = (enum AVAudioServiceType*)side_data;
> +        *ast = service_type;
> +    }
> +
> +    if (has_channel_label) {
> +        uint64_t channel_layout = 0;
> +        int require_reorder = 0;
> +
> +        for (int i = 0; i < descriptor->channels; i++) {
> +            if (!routing[i]) {
> +                av_log(mxf->fc, AV_LOG_WARNING, "Designation of audio channel %d in stream #%d is unknown or unsupported, "
> +                                                "falling back to unknown channel layout\n", st->index, i);
> +                return 0;
> +            }
> +            if (channel_layout & routing[i]) {
> +                av_log(mxf->fc, AV_LOG_WARNING, "%s audio channel is used multiple times in stream #%d, "
> +                                                "falling back to unknown channel layout\n",
> +                                                av_get_channel_name(routing[i]), st->index);
> +                return 0;
> +            }
> +            if (routing[i] < channel_layout)
> +                require_reorder = 1;
> +            channel_layout |= routing[i];
> +        }
> +
> +        av_assert0(descriptor->channels == av_get_channel_layout_nb_channels(channel_layout));
> +
> +        if (require_reorder) {
> +            if (mxf->skip_audio_reordering)
> +                return 0;
> +            if (!is_pcm(st->codecpar->codec_id)) {
> +                av_log(mxf->fc, AV_LOG_WARNING, "Audio channel reordering for stream #%d is not supported because it is using a non-PCM codec, "
> +                                                "falling back to unknown channel layout\n", st->index);
> +                return 0;
> +            }
> +            av_log(mxf->fc, AV_LOG_VERBOSE, "MCA mapping for stream #%d: ", st->index);
> +            for (int j = 0; j < descriptor->channels; j++) {
> +                int reordered_channel = av_get_channel_layout_channel_index(channel_layout, routing[j]);
> +                av_assert0(reordered_channel >= 0 && reordered_channel < descriptor->channels);
> +                source_track->channel_ordering[j] = reordered_channel;
> +                av_log(mxf->fc, AV_LOG_VERBOSE, "%s%s: %d->%d", j ? ", " : "", av_get_channel_name(routing[j]), j, reordered_channel);
> +            }
> +            av_log(mxf->fc, AV_LOG_VERBOSE, "\n");
> +            source_track->require_reordering = 1;
> +        }
> +
> +        st->codecpar->channel_layout = channel_layout;
> +    }
> +
> +    return 0;
> +}
> +
>  static int mxf_parse_structural_metadata(MXFContext *mxf)
>  {
>      MXFPackage *material_package = NULL;
> @@ -2688,6 +2974,15 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
>                  sti->need_parsing = AVSTREAM_PARSE_FULL;
>              }
>              st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
> +
> +            if (descriptor->channels <= 0 || descriptor->channels >= FF_SANE_NB_CHANNELS) {
> +                av_log(mxf->fc, AV_LOG_ERROR, "Invalid number of channels %d, must be less than %d\n", descriptor->channels, FF_SANE_NB_CHANNELS);
> +                return AVERROR_INVALIDDATA;
> +            }
> +
> +            ret = parse_mca_labels(mxf, source_track, descriptor, st);
> +            if (ret < 0)
> +                return ret;
>          } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
>              enum AVMediaType type;
>              container_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
> @@ -2888,6 +3183,9 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5c,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* VANC/VBI - SMPTE 436M */
>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */
>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x64,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* DC Timed Text Descriptor */
> +    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), AudioChannelLabelSubDescriptor },
> +    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6c,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), SoundfieldGroupLabelSubDescriptor },
> +    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6d,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor },
>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */
>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */
>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent },
> @@ -3187,12 +3485,6 @@ static void mxf_compute_essence_containers(AVFormatContext *s)
>      }
>  }
>
> -static int is_pcm(enum AVCodecID codec_id)
> -{
> -    /* we only care about "normal" PCM codecs until we get samples */
> -    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
> -}
> -
>  static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
>  {
>      int i;
> @@ -3619,6 +3911,25 @@ static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt)
>      return 0;
>  }
>
> +static int mxf_audio_remapping(int* channel_ordering, uint8_t* data, int size, int sample_size, int channels)
> +{
> +    int sample_offset = channels * sample_size;
> +    int number_of_samples = size / sample_offset;
> +    uint8_t tmp[FF_SANE_NB_CHANNELS * 4];
> +    uint8_t* data_ptr = data;
> +
> +    for (int sample = 0; sample < number_of_samples; ++sample) {
> +        memcpy(tmp, data_ptr, sample_offset);
> +
> +        for (int channel = 0; channel < channels; ++channel) {
> +            memcpy(&data_ptr[sample_size * channel_ordering[channel]], &tmp[sample_size * channel], sample_size);
> +        }
> +
> +        data_ptr += sample_offset;
> +    }
> +    return 0;
> +}
> +
>  static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
>  {
>      KLVPacket klv;
> @@ -3733,6 +4044,15 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
>                  return ret;
>              }
>
> +            // for audio, process audio remapping if MCA label requires it
> +            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && track->require_reordering) {
> +                int byte_per_sample = st->codecpar->bits_per_coded_sample / 8;
> +                ret = mxf_audio_remapping(track->channel_ordering, pkt->data, pkt->size, byte_per_sample, st->codecpar->channels);
> +                if (ret < 0) {
> +                    return ret;
> +                }
> +            }
> +
>              /* seek for truncated packets */
>              avio_seek(s->pb, klv.next_klv, SEEK_SET);
>
> @@ -3920,6 +4240,9 @@ static const AVOption options[] = {
>      { "eia608_extract", "extract eia 608 captions from s436m track",
>        offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
>        AV_OPT_FLAG_DECODING_PARAM },
> +    { "skip_audio_reordering", "skip audio reordering based on Multi-Channel Audio labelling",
> +      offsetof(MXFContext, skip_audio_reordering), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
> +      AV_OPT_FLAG_DECODING_PARAM },
>      { NULL },
>  };
>
> diff --git a/libavformat/version.h b/libavformat/version.h
> index 0705ee4112..21ca6ed096 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -33,7 +33,7 @@
>  // Also please add any ticket numbers that you believe might be affected here
>  #define LIBAVFORMAT_VERSION_MAJOR  59
>  #define LIBAVFORMAT_VERSION_MINOR   9
> -#define LIBAVFORMAT_VERSION_MICRO 102
> +#define LIBAVFORMAT_VERSION_MICRO 103
>
>  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
>                                                 LIBAVFORMAT_VERSION_MINOR, \
> --
> 2.33.1
>
Marton Balint Dec. 17, 2021, 6:12 p.m. UTC | #2
On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:

> Hi all,
>
> Can I have an update on this patch submission ?
> Is something required to be done before it can be merged ?

New channel layout API is on its way, which makes in-demuxer channel 
reordering uneeded. Therefore the reordering option should not be added 
as it is in this patch. I can rework the patch after the channel layout 
API is in. (should happen in a couple of weeks at most).

Regards,
Marton

>
> Thanks you,
> Marc-Antoine
>
>
> Le ven. 3 déc. 2021 à 10:57, Marc-Antoine Arnaud
> <marc-antoine.arnaud@luminvent.com> a écrit :
>>
>> ---
>>  doc/demuxers.texi     |  10 ++
>>  libavformat/mxf.h     |   3 +
>>  libavformat/mxfdec.c  | 335 +++++++++++++++++++++++++++++++++++++++++-
>>  libavformat/version.h |   2 +-
>>  4 files changed, 343 insertions(+), 7 deletions(-)
>>
>> diff --git a/doc/demuxers.texi b/doc/demuxers.texi
>> index cab8a7072c..23b6753602 100644
>> --- a/doc/demuxers.texi
>> +++ b/doc/demuxers.texi
>> @@ -770,6 +770,16 @@ MJPEG stream. Turning this option on by setting it to 1 will result in a stricte
>>  of the boundary value.
>>  @end table
>>
>> +@section mxf
>> +
>> +MXF demuxer.
>> +
>> +@table @option
>> +
>> +@item -skip_audio_reordering @var{bool}
>> +This option will disable the audio reordering based on Multi-Channel Audio (MCA) labelling (SMPTE ST-377-4).
>> +@end table
>> +
>>  @section rawvideo
>>
>>  Raw video demuxer.
>> diff --git a/libavformat/mxf.h b/libavformat/mxf.h
>> index fe9c52732c..d53a16df51 100644
>> --- a/libavformat/mxf.h
>> +++ b/libavformat/mxf.h
>> @@ -50,6 +50,9 @@ enum MXFMetadataSetType {
>>      TaggedValue,
>>      TapeDescriptor,
>>      AVCSubDescriptor,
>> +    AudioChannelLabelSubDescriptor,
>> +    SoundfieldGroupLabelSubDescriptor,
>> +    GroupOfSoundfieldGroupsLabelSubDescriptor,
>>  };
>>
>>  enum MXFFrameLayout {
>> diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
>> index af9d33f796..6e1da75542 100644
>> --- a/libavformat/mxfdec.c
>> +++ b/libavformat/mxfdec.c
>> @@ -51,11 +51,14 @@
>>  #include "libavutil/mastering_display_metadata.h"
>>  #include "libavutil/mathematics.h"
>>  #include "libavcodec/bytestream.h"
>> +#include "libavcodec/internal.h"
>> +#include "libavutil/channel_layout.h"
>>  #include "libavutil/intreadwrite.h"
>>  #include "libavutil/parseutils.h"
>>  #include "libavutil/timecode.h"
>>  #include "libavutil/opt.h"
>>  #include "avformat.h"
>> +#include "avlanguage.h"
>>  #include "internal.h"
>>  #include "mxf.h"
>>
>> @@ -177,6 +180,8 @@ typedef struct {
>>      int body_sid;
>>      MXFWrappingScheme wrapping;
>>      int edit_units_per_packet; /* how many edit units to read at a time (PCM, ClipWrapped) */
>> +    int require_reordering;
>> +    int channel_ordering[FF_SANE_NB_CHANNELS];
>>  } MXFTrack;
>>
>>  typedef struct MXFDescriptor {
>> @@ -205,6 +210,8 @@ typedef struct MXFDescriptor {
>>      unsigned int vert_subsampling;
>>      UID *file_descriptors_refs;
>>      int file_descriptors_count;
>> +    UID *sub_descriptors_refs;
>> +    int sub_descriptors_count;
>>      int linked_track_id;
>>      uint8_t *extradata;
>>      int extradata_size;
>> @@ -217,6 +224,18 @@ typedef struct MXFDescriptor {
>>      size_t coll_size;
>>  } MXFDescriptor;
>>
>> +typedef struct MXFMCASubDescriptor {
>> +    MXFMetadataSet meta;
>> +    UID uid;
>> +    UID mca_link_id;
>> +    UID soundfield_group_link_id;
>> +    UID *group_of_soundfield_groups_link_id_refs;
>> +    int group_of_soundfield_groups_link_id_count;
>> +    UID mca_label_dictionary_id;
>> +    int mca_channel_id;
>> +    char *language;
>> +} MXFMCASubDescriptor;
>> +
>>  typedef struct MXFIndexTableSegment {
>>      MXFMetadataSet meta;
>>      int edit_unit_byte_count;
>> @@ -290,6 +309,7 @@ typedef struct MXFContext {
>>      int nb_index_tables;
>>      MXFIndexTable *index_tables;
>>      int eia608_extract;
>> +    int skip_audio_reordering;
>>  } MXFContext;
>>
>>  /* NOTE: klv_offset is not set (-1) for local keys */
>> @@ -311,6 +331,7 @@ static const uint8_t mxf_system_item_key_cp[]              = { 0x06,0x0e,0x2b,0x
>>  static const uint8_t mxf_system_item_key_gc[]              = { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 };
>>  static const uint8_t mxf_klv_key[]                         = { 0x06,0x0e,0x2b,0x34 };
>>  static const uint8_t mxf_apple_coll_prefix[]               = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01 };
>> +
>>  /* complete keys to match */
>>  static const uint8_t mxf_crypto_source_container_ul[]      = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 };
>>  static const uint8_t mxf_encrypted_triplet_key[]           = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
>> @@ -323,6 +344,17 @@ static const uint8_t mxf_indirect_value_utf16be[]          = { 0x42,0x01,0x10,0x
>>  static const uint8_t mxf_apple_coll_max_cll[]              = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x01 };
>>  static const uint8_t mxf_apple_coll_max_fall[]             = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x02 };
>>
>> +static const uint8_t mxf_mca_label_dictionary_id[]         = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x01,0x00,0x00,0x00 };
>> +static const uint8_t mxf_mca_tag_symbol[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x02,0x00,0x00,0x00 };
>> +static const uint8_t mxf_mca_tag_name[]                    = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x03,0x00,0x00,0x00 };
>> +static const uint8_t mxf_group_of_soundfield_groups_link_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x04,0x00,0x00,0x00 };
>> +static const uint8_t mxf_mca_link_id[]                     = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x05,0x00,0x00,0x00 };
>> +static const uint8_t mxf_mca_channel_id[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x04,0x0a,0x00,0x00,0x00,0x00 };
>> +static const uint8_t mxf_soundfield_group_link_id[]        = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x06,0x00,0x00,0x00 };
>> +static const uint8_t mxf_mca_rfc5646_spoken_language[]     = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0d,0x03,0x01,0x01,0x02,0x03,0x15,0x00,0x00 };
>> +
>> +static const uint8_t mxf_sub_descriptor[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00 };
>> +
>>  static const uint8_t mxf_mastering_display_prefix[13]      = { FF_MXF_MasteringDisplay_PREFIX };
>>  static const uint8_t mxf_mastering_display_uls[4][16] = {
>>      FF_MXF_MasteringDisplayPrimaries,
>> @@ -343,6 +375,13 @@ static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx)
>>          av_freep(&((MXFDescriptor *)*ctx)->mastering);
>>          av_freep(&((MXFDescriptor *)*ctx)->coll);
>>          av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs);
>> +        av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
>> +        break;
>> +    case AudioChannelLabelSubDescriptor:
>> +    case SoundfieldGroupLabelSubDescriptor:
>> +    case GroupOfSoundfieldGroupsLabelSubDescriptor:
>> +        av_freep(&((MXFMCASubDescriptor *)*ctx)->language);
>> +        av_freep(&((MXFMCASubDescriptor *)*ctx)->group_of_soundfield_groups_link_id_refs);
>>          break;
>>      case Sequence:
>>          av_freep(&((MXFSequence *)*ctx)->structural_components_refs);
>> @@ -906,6 +945,30 @@ static int mxf_read_strong_ref_array(AVIOContext *pb, UID **refs, int *count)
>>      return 0;
>>  }
>>
>> +static inline int mxf_read_us_ascii_string(AVIOContext *pb, int size, char** str)
>> +{
>> +    int ret;
>> +    size_t buf_size;
>> +
>> +    if (size < 0 || size > INT_MAX - 1)
>> +        return AVERROR(EINVAL);
>> +
>> +    buf_size = size + 1;
>> +    av_free(*str);
>> +    *str = av_malloc(buf_size);
>> +    if (!*str)
>> +        return AVERROR(ENOMEM);
>> +
>> +    ret = avio_get_str(pb, size, *str, buf_size);
>> +
>> +    if (ret < 0) {
>> +        av_freep(str);
>> +        return ret;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>>  static inline int mxf_read_utf16_string(AVIOContext *pb, int size, char** str, int be)
>>  {
>>      int ret;
>> @@ -1360,11 +1423,40 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
>>                  descriptor->coll->MaxFALL = avio_rb16(pb);
>>              }
>>          }
>> +
>> +        if (IS_KLV_KEY(uid, mxf_sub_descriptor))
>> +            return mxf_read_strong_ref_array(pb, &descriptor->sub_descriptors_refs, &descriptor->sub_descriptors_count);
>> +
>>          break;
>>      }
>>      return 0;
>>  }
>>
>> +static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
>> +{
>> +    MXFMCASubDescriptor *mca_sub_descriptor = arg;
>> +
>> +    if (IS_KLV_KEY(uid, mxf_mca_label_dictionary_id))
>> +        avio_read(pb, mca_sub_descriptor->mca_label_dictionary_id, 16);
>> +
>> +    if (IS_KLV_KEY(uid, mxf_mca_link_id))
>> +        avio_read(pb, mca_sub_descriptor->mca_link_id, 16);
>> +
>> +    if (IS_KLV_KEY(uid, mxf_soundfield_group_link_id))
>> +        avio_read(pb, mca_sub_descriptor->soundfield_group_link_id, 16);
>> +
>> +    if (IS_KLV_KEY(uid, mxf_group_of_soundfield_groups_link_id))
>> +        return mxf_read_strong_ref_array(pb, &mca_sub_descriptor->group_of_soundfield_groups_link_id_refs, &mca_sub_descriptor->group_of_soundfield_groups_link_id_count);
>> +
>> +    if (IS_KLV_KEY(uid, mxf_mca_channel_id))
>> +        mca_sub_descriptor->mca_channel_id = avio_rb32(pb);
>> +
>> +    if (IS_KLV_KEY(uid, mxf_mca_rfc5646_spoken_language))
>> +        return mxf_read_us_ascii_string(pb, size, &mca_sub_descriptor->language);
>> +
>> +    return 0;
>> +}
>> +
>>  static int mxf_read_indirect_value(void *arg, AVIOContext *pb, int size)
>>  {
>>      MXFTaggedValue *tagged_value = arg;
>> @@ -1494,6 +1586,50 @@ static const MXFCodecUL mxf_data_essence_container_uls[] = {
>>      { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  0, AV_CODEC_ID_NONE },
>>  };
>>
>> +typedef struct MXFChannelOrderingUL {
>> +    UID uid;
>> +    uint64_t layout_mask;
>> +    enum AVAudioServiceType service_type;
>> +} MXFChannelOrderingUL;
>> +
>> +static const MXFChannelOrderingUL mxf_channel_ordering[] = {
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Left
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Low Frequency Effects
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, AV_CH_BACK_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, AV_CH_BACK_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT_OF_CENTER,  AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Center
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, // Hearing impaired audio channel
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00 }, AV_CH_TOP_FRONT_LEFT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00 }, AV_CH_TOP_FRONT_RIGHT,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00 }, AV_CH_TOP_FRONT_CENTER,      AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00 }, AV_CH_TOP_BACK_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00 }, AV_CH_TOP_BACK_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00 }, AV_CH_TOP_CENTER,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Top Surround
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00 }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front Subwoofer
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00 }, AV_CH_LOW_FREQUENCY_2,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Front Subwoofer
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00 }, AV_CH_TOP_BACK_CENTER,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear Height
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00 }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00 }, AV_CH_BOTTOM_FRONT_LEFT,     AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00 }, AV_CH_BOTTOM_FRONT_RIGHT,    AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below
>> +    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00 }, AV_CH_BOTTOM_FRONT_CENTER,   AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Below
>> +    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0,                           AV_AUDIO_SERVICE_TYPE_NB },
>> +};
>> +
>>  static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul)
>>  {
>>      int val;
>> @@ -2292,6 +2428,156 @@ static enum AVColorRange mxf_get_color_range(MXFContext *mxf, MXFDescriptor *des
>>      return AVCOL_RANGE_UNSPECIFIED;
>>  }
>>
>> +static int is_pcm(enum AVCodecID codec_id)
>> +{
>> +    /* we only care about "normal" PCM codecs until we get samples */
>> +    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
>> +}
>> +
>> +static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary **met)
>> +{
>> +    // language abbr should contain at least 2 chars
>> +    if (rfc5646 && strlen(rfc5646) > 1) {
>> +        char primary_tag[4] =
>> +            {rfc5646[0], rfc5646[1], rfc5646[2] != '-' ? rfc5646[2] : '\0', '\0'};
>> +
>> +        const char *iso6392       = ff_convert_lang_to(primary_tag,
>> +                                                       AV_LANG_ISO639_2_BIBL);
>> +        if (iso6392)
>> +            return(av_dict_set(met, "language", iso6392, 0));
>> +    }
>> +    return 0;
>> +}
>> +
>> +static MXFMCASubDescriptor *find_mca_link_id(MXFContext *mxf, enum MXFMetadataSetType type, UID *mca_link_id)
>> +{
>> +    for (int k = 0; k < mxf->metadata_sets_count; k++) {
>> +        MXFMCASubDescriptor *group = (MXFMCASubDescriptor*)mxf->metadata_sets[k];
>> +        if (group->meta.type == type && !memcmp(&group->mca_link_id, mca_link_id, 16))
>> +            return group;
>> +    }
>> +    return NULL;
>> +}
>> +
>> +static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescriptor *descriptor, AVStream *st)
>> +{
>> +    uint64_t routing[FF_SANE_NB_CHANNELS] = {0};
>> +    char *language = NULL;
>> +    int ambigous_language = 0;
>> +    enum AVAudioServiceType service_type = AV_AUDIO_SERVICE_TYPE_NB;
>> +    int ambigous_service_type = 0;
>> +    int has_channel_label = 0;
>> +
>> +    for (int i = 0; i < descriptor->sub_descriptors_count; i++) {
>> +        char *channel_language;
>> +
>> +        MXFMCASubDescriptor *label = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[i], AudioChannelLabelSubDescriptor);
>> +        if (label == NULL)
>> +            continue;
>> +
>> +        has_channel_label = 1;
>> +        for (const MXFChannelOrderingUL* channel_ordering = mxf_channel_ordering; channel_ordering->uid[0]; channel_ordering++) {
>> +            if (IS_KLV_KEY(channel_ordering->uid, label->mca_label_dictionary_id)) {
>> +                int target_channel = label->mca_channel_id;
>> +                if (target_channel == 0 && descriptor->channels == 1)
>> +                    target_channel = 1;
>> +                if (target_channel <= 0 || target_channel > descriptor->channels) {
>> +                    av_log(mxf->fc, AV_LOG_ERROR, "AudioChannelLabelSubDescriptor has invalid MCA channel ID %d\n", target_channel);
>> +                    return AVERROR_INVALIDDATA;
>> +                }
>> +                routing[target_channel - 1] = channel_ordering->layout_mask;
>> +                if (service_type == AV_AUDIO_SERVICE_TYPE_NB)
>> +                    service_type = channel_ordering->service_type;
>> +                else if (service_type != channel_ordering->service_type)
>> +                    ambigous_service_type = 1;
>> +                break;
>> +            }
>> +        }
>> +
>> +        channel_language = label->language;
>> +        if (!channel_language) {
>> +            MXFMCASubDescriptor *group = find_mca_link_id(mxf, SoundfieldGroupLabelSubDescriptor, &label->soundfield_group_link_id);
>> +            if (group) {
>> +                channel_language = group->language;
>> +                if (!channel_language && group->group_of_soundfield_groups_link_id_count) {
>> +                    MXFMCASubDescriptor *supergroup = find_mca_link_id(mxf, GroupOfSoundfieldGroupsLabelSubDescriptor,
>> +                                                                       group->group_of_soundfield_groups_link_id_refs);
>> +                    if (supergroup)
>> +                        channel_language = supergroup->language;
>> +                }
>> +            }
>> +        }
>> +        if (channel_language) {
>> +            if (language && strcmp(language, channel_language))
>> +                ambigous_language = 1;
>> +            else
>> +                language = channel_language;
>> +        }
>> +    }
>> +
>> +    if (language && !ambigous_language) {
>> +       int ret = set_language(mxf->fc, language, &st->metadata);
>> +       if (ret < 0)
>> +           return ret;
>> +    }
>> +
>> +    if (service_type != AV_AUDIO_SERVICE_TYPE_NB && service_type != AV_AUDIO_SERVICE_TYPE_MAIN && !ambigous_service_type) {
>> +        enum AVAudioServiceType *ast;
>> +        uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast));
>> +        if (!side_data)
>> +            return AVERROR(ENOMEM);
>> +        ast = (enum AVAudioServiceType*)side_data;
>> +        *ast = service_type;
>> +    }
>> +
>> +    if (has_channel_label) {
>> +        uint64_t channel_layout = 0;
>> +        int require_reorder = 0;
>> +
>> +        for (int i = 0; i < descriptor->channels; i++) {
>> +            if (!routing[i]) {
>> +                av_log(mxf->fc, AV_LOG_WARNING, "Designation of audio channel %d in stream #%d is unknown or unsupported, "
>> +                                                "falling back to unknown channel layout\n", st->index, i);
>> +                return 0;
>> +            }
>> +            if (channel_layout & routing[i]) {
>> +                av_log(mxf->fc, AV_LOG_WARNING, "%s audio channel is used multiple times in stream #%d, "
>> +                                                "falling back to unknown channel layout\n",
>> +                                                av_get_channel_name(routing[i]), st->index);
>> +                return 0;
>> +            }
>> +            if (routing[i] < channel_layout)
>> +                require_reorder = 1;
>> +            channel_layout |= routing[i];
>> +        }
>> +
>> +        av_assert0(descriptor->channels == av_get_channel_layout_nb_channels(channel_layout));
>> +
>> +        if (require_reorder) {
>> +            if (mxf->skip_audio_reordering)
>> +                return 0;
>> +            if (!is_pcm(st->codecpar->codec_id)) {
>> +                av_log(mxf->fc, AV_LOG_WARNING, "Audio channel reordering for stream #%d is not supported because it is using a non-PCM codec, "
>> +                                                "falling back to unknown channel layout\n", st->index);
>> +                return 0;
>> +            }
>> +            av_log(mxf->fc, AV_LOG_VERBOSE, "MCA mapping for stream #%d: ", st->index);
>> +            for (int j = 0; j < descriptor->channels; j++) {
>> +                int reordered_channel = av_get_channel_layout_channel_index(channel_layout, routing[j]);
>> +                av_assert0(reordered_channel >= 0 && reordered_channel < descriptor->channels);
>> +                source_track->channel_ordering[j] = reordered_channel;
>> +                av_log(mxf->fc, AV_LOG_VERBOSE, "%s%s: %d->%d", j ? ", " : "", av_get_channel_name(routing[j]), j, reordered_channel);
>> +            }
>> +            av_log(mxf->fc, AV_LOG_VERBOSE, "\n");
>> +            source_track->require_reordering = 1;
>> +        }
>> +
>> +        st->codecpar->channel_layout = channel_layout;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>  static int mxf_parse_structural_metadata(MXFContext *mxf)
>>  {
>>      MXFPackage *material_package = NULL;
>> @@ -2688,6 +2974,15 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
>>                  sti->need_parsing = AVSTREAM_PARSE_FULL;
>>              }
>>              st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
>> +
>> +            if (descriptor->channels <= 0 || descriptor->channels >= FF_SANE_NB_CHANNELS) {
>> +                av_log(mxf->fc, AV_LOG_ERROR, "Invalid number of channels %d, must be less than %d\n", descriptor->channels, FF_SANE_NB_CHANNELS);
>> +                return AVERROR_INVALIDDATA;
>> +            }
>> +
>> +            ret = parse_mca_labels(mxf, source_track, descriptor, st);
>> +            if (ret < 0)
>> +                return ret;
>>          } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
>>              enum AVMediaType type;
>>              container_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
>> @@ -2888,6 +3183,9 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
>>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5c,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* VANC/VBI - SMPTE 436M */
>>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */
>>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x64,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* DC Timed Text Descriptor */
>> +    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), AudioChannelLabelSubDescriptor },
>> +    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6c,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), SoundfieldGroupLabelSubDescriptor },
>> +    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6d,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor },
>>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */
>>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */
>>      { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent },
>> @@ -3187,12 +3485,6 @@ static void mxf_compute_essence_containers(AVFormatContext *s)
>>      }
>>  }
>>
>> -static int is_pcm(enum AVCodecID codec_id)
>> -{
>> -    /* we only care about "normal" PCM codecs until we get samples */
>> -    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
>> -}
>> -
>>  static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
>>  {
>>      int i;
>> @@ -3619,6 +3911,25 @@ static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt)
>>      return 0;
>>  }
>>
>> +static int mxf_audio_remapping(int* channel_ordering, uint8_t* data, int size, int sample_size, int channels)
>> +{
>> +    int sample_offset = channels * sample_size;
>> +    int number_of_samples = size / sample_offset;
>> +    uint8_t tmp[FF_SANE_NB_CHANNELS * 4];
>> +    uint8_t* data_ptr = data;
>> +
>> +    for (int sample = 0; sample < number_of_samples; ++sample) {
>> +        memcpy(tmp, data_ptr, sample_offset);
>> +
>> +        for (int channel = 0; channel < channels; ++channel) {
>> +            memcpy(&data_ptr[sample_size * channel_ordering[channel]], &tmp[sample_size * channel], sample_size);
>> +        }
>> +
>> +        data_ptr += sample_offset;
>> +    }
>> +    return 0;
>> +}
>> +
>>  static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
>>  {
>>      KLVPacket klv;
>> @@ -3733,6 +4044,15 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
>>                  return ret;
>>              }
>>
>> +            // for audio, process audio remapping if MCA label requires it
>> +            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && track->require_reordering) {
>> +                int byte_per_sample = st->codecpar->bits_per_coded_sample / 8;
>> +                ret = mxf_audio_remapping(track->channel_ordering, pkt->data, pkt->size, byte_per_sample, st->codecpar->channels);
>> +                if (ret < 0) {
>> +                    return ret;
>> +                }
>> +            }
>> +
>>              /* seek for truncated packets */
>>              avio_seek(s->pb, klv.next_klv, SEEK_SET);
>>
>> @@ -3920,6 +4240,9 @@ static const AVOption options[] = {
>>      { "eia608_extract", "extract eia 608 captions from s436m track",
>>        offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
>>        AV_OPT_FLAG_DECODING_PARAM },
>> +    { "skip_audio_reordering", "skip audio reordering based on Multi-Channel Audio labelling",
>> +      offsetof(MXFContext, skip_audio_reordering), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
>> +      AV_OPT_FLAG_DECODING_PARAM },
>>      { NULL },
>>  };
>>
>> diff --git a/libavformat/version.h b/libavformat/version.h
>> index 0705ee4112..21ca6ed096 100644
>> --- a/libavformat/version.h
>> +++ b/libavformat/version.h
>> @@ -33,7 +33,7 @@
>>  // Also please add any ticket numbers that you believe might be affected here
>>  #define LIBAVFORMAT_VERSION_MAJOR  59
>>  #define LIBAVFORMAT_VERSION_MINOR   9
>> -#define LIBAVFORMAT_VERSION_MICRO 102
>> +#define LIBAVFORMAT_VERSION_MICRO 103
>>
>>  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
>>                                                 LIBAVFORMAT_VERSION_MINOR, \
>> --
>> 2.33.1
>>
>
>
> -- 
> Marc-Antoine ARNAUD
> CEO & Founder
>
> mobile:  +33 6 84 71 84 45
> email:     arnaud.marc-antoine@luminvent.com
> website:  luminvent.com
> _______________________________________________
> 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".
Marc-Antoine ARNAUD Dec. 21, 2021, 10:44 a.m. UTC | #3
Le ven. 17 déc. 2021 à 19:12, Marton Balint <cus@passwd.hu> a écrit :

>
>
> On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:
>
> > Hi all,
> >
> > Can I have an update on this patch submission ?
> > Is something required to be done before it can be merged ?
>
> New channel layout API is on its way, which makes in-demuxer channel
> reordering uneeded. Therefore the reordering option should not be added
> as it is in this patch. I can rework the patch after the channel layout
> API is in. (should happen in a couple of weeks at most).
>
> Regards,
> Marton
>

So it will happen only after the release 5 of FFMpeg right ?

Is it possible to merge it, and we can rework it after the new API is
released ?
Patches are related to IMF (new format) patches, and if FFmpeg can accept
IMF without MCA support it will generate a lot of errors in audio mapping.
So even if it's not performant for now, is it possible to imagine to merge
patches and rework after ?

Thanks a lot,
Marc-Antoine



>
> >
> > Thanks you,
> > Marc-Antoine
> >
> >
> > Le ven. 3 déc. 2021 à 10:57, Marc-Antoine Arnaud
> > <marc-antoine.arnaud@luminvent.com> a écrit :
> >>
> >> ---
> >>  doc/demuxers.texi     |  10 ++
> >>  libavformat/mxf.h     |   3 +
> >>  libavformat/mxfdec.c  | 335 +++++++++++++++++++++++++++++++++++++++++-
> >>  libavformat/version.h |   2 +-
> >>  4 files changed, 343 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/doc/demuxers.texi b/doc/demuxers.texi
> >> index cab8a7072c..23b6753602 100644
> >> --- a/doc/demuxers.texi
> >> +++ b/doc/demuxers.texi
> >> @@ -770,6 +770,16 @@ MJPEG stream. Turning this option on by setting it
> to 1 will result in a stricte
> >>  of the boundary value.
> >>  @end table
> >>
> >> +@section mxf
> >> +
> >> +MXF demuxer.
> >> +
> >> +@table @option
> >> +
> >> +@item -skip_audio_reordering @var{bool}
> >> +This option will disable the audio reordering based on Multi-Channel
> Audio (MCA) labelling (SMPTE ST-377-4).
> >> +@end table
> >> +
> >>  @section rawvideo
> >>
> >>  Raw video demuxer.
> >> diff --git a/libavformat/mxf.h b/libavformat/mxf.h
> >> index fe9c52732c..d53a16df51 100644
> >> --- a/libavformat/mxf.h
> >> +++ b/libavformat/mxf.h
> >> @@ -50,6 +50,9 @@ enum MXFMetadataSetType {
> >>      TaggedValue,
> >>      TapeDescriptor,
> >>      AVCSubDescriptor,
> >> +    AudioChannelLabelSubDescriptor,
> >> +    SoundfieldGroupLabelSubDescriptor,
> >> +    GroupOfSoundfieldGroupsLabelSubDescriptor,
> >>  };
> >>
> >>  enum MXFFrameLayout {
> >> diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
> >> index af9d33f796..6e1da75542 100644
> >> --- a/libavformat/mxfdec.c
> >> +++ b/libavformat/mxfdec.c
> >> @@ -51,11 +51,14 @@
> >>  #include "libavutil/mastering_display_metadata.h"
> >>  #include "libavutil/mathematics.h"
> >>  #include "libavcodec/bytestream.h"
> >> +#include "libavcodec/internal.h"
> >> +#include "libavutil/channel_layout.h"
> >>  #include "libavutil/intreadwrite.h"
> >>  #include "libavutil/parseutils.h"
> >>  #include "libavutil/timecode.h"
> >>  #include "libavutil/opt.h"
> >>  #include "avformat.h"
> >> +#include "avlanguage.h"
> >>  #include "internal.h"
> >>  #include "mxf.h"
> >>
> >> @@ -177,6 +180,8 @@ typedef struct {
> >>      int body_sid;
> >>      MXFWrappingScheme wrapping;
> >>      int edit_units_per_packet; /* how many edit units to read at a
> time (PCM, ClipWrapped) */
> >> +    int require_reordering;
> >> +    int channel_ordering[FF_SANE_NB_CHANNELS];
> >>  } MXFTrack;
> >>
> >>  typedef struct MXFDescriptor {
> >> @@ -205,6 +210,8 @@ typedef struct MXFDescriptor {
> >>      unsigned int vert_subsampling;
> >>      UID *file_descriptors_refs;
> >>      int file_descriptors_count;
> >> +    UID *sub_descriptors_refs;
> >> +    int sub_descriptors_count;
> >>      int linked_track_id;
> >>      uint8_t *extradata;
> >>      int extradata_size;
> >> @@ -217,6 +224,18 @@ typedef struct MXFDescriptor {
> >>      size_t coll_size;
> >>  } MXFDescriptor;
> >>
> >> +typedef struct MXFMCASubDescriptor {
> >> +    MXFMetadataSet meta;
> >> +    UID uid;
> >> +    UID mca_link_id;
> >> +    UID soundfield_group_link_id;
> >> +    UID *group_of_soundfield_groups_link_id_refs;
> >> +    int group_of_soundfield_groups_link_id_count;
> >> +    UID mca_label_dictionary_id;
> >> +    int mca_channel_id;
> >> +    char *language;
> >> +} MXFMCASubDescriptor;
> >> +
> >>  typedef struct MXFIndexTableSegment {
> >>      MXFMetadataSet meta;
> >>      int edit_unit_byte_count;
> >> @@ -290,6 +309,7 @@ typedef struct MXFContext {
> >>      int nb_index_tables;
> >>      MXFIndexTable *index_tables;
> >>      int eia608_extract;
> >> +    int skip_audio_reordering;
> >>  } MXFContext;
> >>
> >>  /* NOTE: klv_offset is not set (-1) for local keys */
> >> @@ -311,6 +331,7 @@ static const uint8_t mxf_system_item_key_cp[]
>         = { 0x06,0x0e,0x2b,0x
> >>  static const uint8_t mxf_system_item_key_gc[]              = {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 };
> >>  static const uint8_t mxf_klv_key[]                         = {
> 0x06,0x0e,0x2b,0x34 };
> >>  static const uint8_t mxf_apple_coll_prefix[]               = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01
> };
> >> +
> >>  /* complete keys to match */
> >>  static const uint8_t mxf_crypto_source_container_ul[]      = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00
> };
> >>  static const uint8_t mxf_encrypted_triplet_key[]           = {
> 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00
> };
> >> @@ -323,6 +344,17 @@ static const uint8_t mxf_indirect_value_utf16be[]
>         = { 0x42,0x01,0x10,0x
> >>  static const uint8_t mxf_apple_coll_max_cll[]              = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x01
> };
> >>  static const uint8_t mxf_apple_coll_max_fall[]             = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x02
> };
> >>
> >> +static const uint8_t mxf_mca_label_dictionary_id[]         = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x01,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_mca_tag_symbol[]                  = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x02,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_mca_tag_name[]                    = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x03,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_group_of_soundfield_groups_link_id[] = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x04,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_mca_link_id[]                     = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x05,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_mca_channel_id[]                  = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x04,0x0a,0x00,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_soundfield_group_link_id[]        = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x06,0x00,0x00,0x00
> };
> >> +static const uint8_t mxf_mca_rfc5646_spoken_language[]     = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0d,0x03,0x01,0x01,0x02,0x03,0x15,0x00,0x00
> };
> >> +
> >> +static const uint8_t mxf_sub_descriptor[]                  = {
> 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00
> };
> >> +
> >>  static const uint8_t mxf_mastering_display_prefix[13]      = {
> FF_MXF_MasteringDisplay_PREFIX };
> >>  static const uint8_t mxf_mastering_display_uls[4][16] = {
> >>      FF_MXF_MasteringDisplayPrimaries,
> >> @@ -343,6 +375,13 @@ static void mxf_free_metadataset(MXFMetadataSet
> **ctx, int freectx)
> >>          av_freep(&((MXFDescriptor *)*ctx)->mastering);
> >>          av_freep(&((MXFDescriptor *)*ctx)->coll);
> >>          av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs);
> >> +        av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
> >> +        break;
> >> +    case AudioChannelLabelSubDescriptor:
> >> +    case SoundfieldGroupLabelSubDescriptor:
> >> +    case GroupOfSoundfieldGroupsLabelSubDescriptor:
> >> +        av_freep(&((MXFMCASubDescriptor *)*ctx)->language);
> >> +        av_freep(&((MXFMCASubDescriptor
> *)*ctx)->group_of_soundfield_groups_link_id_refs);
> >>          break;
> >>      case Sequence:
> >>          av_freep(&((MXFSequence *)*ctx)->structural_components_refs);
> >> @@ -906,6 +945,30 @@ static int mxf_read_strong_ref_array(AVIOContext
> *pb, UID **refs, int *count)
> >>      return 0;
> >>  }
> >>
> >> +static inline int mxf_read_us_ascii_string(AVIOContext *pb, int size,
> char** str)
> >> +{
> >> +    int ret;
> >> +    size_t buf_size;
> >> +
> >> +    if (size < 0 || size > INT_MAX - 1)
> >> +        return AVERROR(EINVAL);
> >> +
> >> +    buf_size = size + 1;
> >> +    av_free(*str);
> >> +    *str = av_malloc(buf_size);
> >> +    if (!*str)
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    ret = avio_get_str(pb, size, *str, buf_size);
> >> +
> >> +    if (ret < 0) {
> >> +        av_freep(str);
> >> +        return ret;
> >> +    }
> >> +
> >> +    return ret;
> >> +}
> >> +
> >>  static inline int mxf_read_utf16_string(AVIOContext *pb, int size,
> char** str, int be)
> >>  {
> >>      int ret;
> >> @@ -1360,11 +1423,40 @@ static int mxf_read_generic_descriptor(void
> *arg, AVIOContext *pb, int tag, int
> >>                  descriptor->coll->MaxFALL = avio_rb16(pb);
> >>              }
> >>          }
> >> +
> >> +        if (IS_KLV_KEY(uid, mxf_sub_descriptor))
> >> +            return mxf_read_strong_ref_array(pb,
> &descriptor->sub_descriptors_refs, &descriptor->sub_descriptors_count);
> >> +
> >>          break;
> >>      }
> >>      return 0;
> >>  }
> >>
> >> +static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int
> tag, int size, UID uid, int64_t klv_offset)
> >> +{
> >> +    MXFMCASubDescriptor *mca_sub_descriptor = arg;
> >> +
> >> +    if (IS_KLV_KEY(uid, mxf_mca_label_dictionary_id))
> >> +        avio_read(pb, mca_sub_descriptor->mca_label_dictionary_id, 16);
> >> +
> >> +    if (IS_KLV_KEY(uid, mxf_mca_link_id))
> >> +        avio_read(pb, mca_sub_descriptor->mca_link_id, 16);
> >> +
> >> +    if (IS_KLV_KEY(uid, mxf_soundfield_group_link_id))
> >> +        avio_read(pb, mca_sub_descriptor->soundfield_group_link_id,
> 16);
> >> +
> >> +    if (IS_KLV_KEY(uid, mxf_group_of_soundfield_groups_link_id))
> >> +        return mxf_read_strong_ref_array(pb,
> &mca_sub_descriptor->group_of_soundfield_groups_link_id_refs,
> &mca_sub_descriptor->group_of_soundfield_groups_link_id_count);
> >> +
> >> +    if (IS_KLV_KEY(uid, mxf_mca_channel_id))
> >> +        mca_sub_descriptor->mca_channel_id = avio_rb32(pb);
> >> +
> >> +    if (IS_KLV_KEY(uid, mxf_mca_rfc5646_spoken_language))
> >> +        return mxf_read_us_ascii_string(pb, size,
> &mca_sub_descriptor->language);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >>  static int mxf_read_indirect_value(void *arg, AVIOContext *pb, int
> size)
> >>  {
> >>      MXFTaggedValue *tagged_value = arg;
> >> @@ -1494,6 +1586,50 @@ static const MXFCodecUL
> mxf_data_essence_container_uls[] = {
> >>      { {
> 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
> },  0, AV_CODEC_ID_NONE },
> >>  };
> >>
> >> +typedef struct MXFChannelOrderingUL {
> >> +    UID uid;
> >> +    uint64_t layout_mask;
> >> +    enum AVAudioServiceType service_type;
> >> +} MXFChannelOrderingUL;
> >> +
> >> +static const MXFChannelOrderingUL mxf_channel_ordering[] = {
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_LEFT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Left
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_RIGHT,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00
> }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Low
> Frequency Effects
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00
> }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00
> }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00
> }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00
> }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00
> }, AV_CH_BACK_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00
> }, AV_CH_BACK_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_LEFT_OF_CENTER,  AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> Center
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00
> }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED },
> // Hearing impaired audio channel
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00
> }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED },
> // Visually impaired narrative audio channel
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00
> }, AV_CH_TOP_FRONT_LEFT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00
> }, AV_CH_TOP_FRONT_RIGHT,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00
> }, AV_CH_TOP_FRONT_CENTER,      AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
> Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00
> }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left
> Surround Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00
> }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> Surround Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00
> }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side
> Surround Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00
> }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side
> Surround Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00
> }, AV_CH_TOP_BACK_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear
> Surround Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00
> }, AV_CH_TOP_BACK_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear
> Surround Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00
> }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00
> }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00
> }, AV_CH_TOP_CENTER,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Top
> Surround
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00
> }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front
> Subwoofer
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00
> }, AV_CH_LOW_FREQUENCY_2,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
> Front Subwoofer
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00
> }, AV_CH_TOP_BACK_CENTER,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
> Rear Height
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00
> }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00
> }, AV_CH_BOTTOM_FRONT_LEFT,     AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00
> }, AV_CH_BOTTOM_FRONT_RIGHT,    AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00
> }, AV_CH_BOTTOM_FRONT_CENTER,   AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
> Below
> >> +    { {
> 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
> }, 0,                           AV_AUDIO_SERVICE_TYPE_NB },
> >> +};
> >> +
> >>  static MXFWrappingScheme mxf_get_wrapping_kind(UID
> *essence_container_ul)
> >>  {
> >>      int val;
> >> @@ -2292,6 +2428,156 @@ static enum AVColorRange
> mxf_get_color_range(MXFContext *mxf, MXFDescriptor *des
> >>      return AVCOL_RANGE_UNSPECIFIED;
> >>  }
> >>
> >> +static int is_pcm(enum AVCodecID codec_id)
> >> +{
> >> +    /* we only care about "normal" PCM codecs until we get samples */
> >> +    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id <
> AV_CODEC_ID_PCM_S24DAUD;
> >> +}
> >> +
> >> +static int set_language(AVFormatContext *s, const char *rfc5646,
> AVDictionary **met)
> >> +{
> >> +    // language abbr should contain at least 2 chars
> >> +    if (rfc5646 && strlen(rfc5646) > 1) {
> >> +        char primary_tag[4] =
> >> +            {rfc5646[0], rfc5646[1], rfc5646[2] != '-' ? rfc5646[2] :
> '\0', '\0'};
> >> +
> >> +        const char *iso6392       = ff_convert_lang_to(primary_tag,
> >> +
>  AV_LANG_ISO639_2_BIBL);
> >> +        if (iso6392)
> >> +            return(av_dict_set(met, "language", iso6392, 0));
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +static MXFMCASubDescriptor *find_mca_link_id(MXFContext *mxf, enum
> MXFMetadataSetType type, UID *mca_link_id)
> >> +{
> >> +    for (int k = 0; k < mxf->metadata_sets_count; k++) {
> >> +        MXFMCASubDescriptor *group =
> (MXFMCASubDescriptor*)mxf->metadata_sets[k];
> >> +        if (group->meta.type == type && !memcmp(&group->mca_link_id,
> mca_link_id, 16))
> >> +            return group;
> >> +    }
> >> +    return NULL;
> >> +}
> >> +
> >> +static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track,
> MXFDescriptor *descriptor, AVStream *st)
> >> +{
> >> +    uint64_t routing[FF_SANE_NB_CHANNELS] = {0};
> >> +    char *language = NULL;
> >> +    int ambigous_language = 0;
> >> +    enum AVAudioServiceType service_type = AV_AUDIO_SERVICE_TYPE_NB;
> >> +    int ambigous_service_type = 0;
> >> +    int has_channel_label = 0;
> >> +
> >> +    for (int i = 0; i < descriptor->sub_descriptors_count; i++) {
> >> +        char *channel_language;
> >> +
> >> +        MXFMCASubDescriptor *label = mxf_resolve_strong_ref(mxf,
> &descriptor->sub_descriptors_refs[i], AudioChannelLabelSubDescriptor);
> >> +        if (label == NULL)
> >> +            continue;
> >> +
> >> +        has_channel_label = 1;
> >> +        for (const MXFChannelOrderingUL* channel_ordering =
> mxf_channel_ordering; channel_ordering->uid[0]; channel_ordering++) {
> >> +            if (IS_KLV_KEY(channel_ordering->uid,
> label->mca_label_dictionary_id)) {
> >> +                int target_channel = label->mca_channel_id;
> >> +                if (target_channel == 0 && descriptor->channels == 1)
> >> +                    target_channel = 1;
> >> +                if (target_channel <= 0 || target_channel >
> descriptor->channels) {
> >> +                    av_log(mxf->fc, AV_LOG_ERROR,
> "AudioChannelLabelSubDescriptor has invalid MCA channel ID %d\n",
> target_channel);
> >> +                    return AVERROR_INVALIDDATA;
> >> +                }
> >> +                routing[target_channel - 1] =
> channel_ordering->layout_mask;
> >> +                if (service_type == AV_AUDIO_SERVICE_TYPE_NB)
> >> +                    service_type = channel_ordering->service_type;
> >> +                else if (service_type !=
> channel_ordering->service_type)
> >> +                    ambigous_service_type = 1;
> >> +                break;
> >> +            }
> >> +        }
> >> +
> >> +        channel_language = label->language;
> >> +        if (!channel_language) {
> >> +            MXFMCASubDescriptor *group = find_mca_link_id(mxf,
> SoundfieldGroupLabelSubDescriptor, &label->soundfield_group_link_id);
> >> +            if (group) {
> >> +                channel_language = group->language;
> >> +                if (!channel_language &&
> group->group_of_soundfield_groups_link_id_count) {
> >> +                    MXFMCASubDescriptor *supergroup =
> find_mca_link_id(mxf, GroupOfSoundfieldGroupsLabelSubDescriptor,
> >> +
>  group->group_of_soundfield_groups_link_id_refs);
> >> +                    if (supergroup)
> >> +                        channel_language = supergroup->language;
> >> +                }
> >> +            }
> >> +        }
> >> +        if (channel_language) {
> >> +            if (language && strcmp(language, channel_language))
> >> +                ambigous_language = 1;
> >> +            else
> >> +                language = channel_language;
> >> +        }
> >> +    }
> >> +
> >> +    if (language && !ambigous_language) {
> >> +       int ret = set_language(mxf->fc, language, &st->metadata);
> >> +       if (ret < 0)
> >> +           return ret;
> >> +    }
> >> +
> >> +    if (service_type != AV_AUDIO_SERVICE_TYPE_NB && service_type !=
> AV_AUDIO_SERVICE_TYPE_MAIN && !ambigous_service_type) {
> >> +        enum AVAudioServiceType *ast;
> >> +        uint8_t* side_data = av_stream_new_side_data(st,
> AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast));
> >> +        if (!side_data)
> >> +            return AVERROR(ENOMEM);
> >> +        ast = (enum AVAudioServiceType*)side_data;
> >> +        *ast = service_type;
> >> +    }
> >> +
> >> +    if (has_channel_label) {
> >> +        uint64_t channel_layout = 0;
> >> +        int require_reorder = 0;
> >> +
> >> +        for (int i = 0; i < descriptor->channels; i++) {
> >> +            if (!routing[i]) {
> >> +                av_log(mxf->fc, AV_LOG_WARNING, "Designation of audio
> channel %d in stream #%d is unknown or unsupported, "
> >> +                                                "falling back to
> unknown channel layout\n", st->index, i);
> >> +                return 0;
> >> +            }
> >> +            if (channel_layout & routing[i]) {
> >> +                av_log(mxf->fc, AV_LOG_WARNING, "%s audio channel is
> used multiple times in stream #%d, "
> >> +                                                "falling back to
> unknown channel layout\n",
> >> +
> av_get_channel_name(routing[i]), st->index);
> >> +                return 0;
> >> +            }
> >> +            if (routing[i] < channel_layout)
> >> +                require_reorder = 1;
> >> +            channel_layout |= routing[i];
> >> +        }
> >> +
> >> +        av_assert0(descriptor->channels ==
> av_get_channel_layout_nb_channels(channel_layout));
> >> +
> >> +        if (require_reorder) {
> >> +            if (mxf->skip_audio_reordering)
> >> +                return 0;
> >> +            if (!is_pcm(st->codecpar->codec_id)) {
> >> +                av_log(mxf->fc, AV_LOG_WARNING, "Audio channel
> reordering for stream #%d is not supported because it is using a non-PCM
> codec, "
> >> +                                                "falling back to
> unknown channel layout\n", st->index);
> >> +                return 0;
> >> +            }
> >> +            av_log(mxf->fc, AV_LOG_VERBOSE, "MCA mapping for stream
> #%d: ", st->index);
> >> +            for (int j = 0; j < descriptor->channels; j++) {
> >> +                int reordered_channel =
> av_get_channel_layout_channel_index(channel_layout, routing[j]);
> >> +                av_assert0(reordered_channel >= 0 && reordered_channel
> < descriptor->channels);
> >> +                source_track->channel_ordering[j] = reordered_channel;
> >> +                av_log(mxf->fc, AV_LOG_VERBOSE, "%s%s: %d->%d", j ? ",
> " : "", av_get_channel_name(routing[j]), j, reordered_channel);
> >> +            }
> >> +            av_log(mxf->fc, AV_LOG_VERBOSE, "\n");
> >> +            source_track->require_reordering = 1;
> >> +        }
> >> +
> >> +        st->codecpar->channel_layout = channel_layout;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >>  static int mxf_parse_structural_metadata(MXFContext *mxf)
> >>  {
> >>      MXFPackage *material_package = NULL;
> >> @@ -2688,6 +2974,15 @@ static int
> mxf_parse_structural_metadata(MXFContext *mxf)
> >>                  sti->need_parsing = AVSTREAM_PARSE_FULL;
> >>              }
> >>              st->codecpar->bits_per_coded_sample =
> av_get_bits_per_sample(st->codecpar->codec_id);
> >> +
> >> +            if (descriptor->channels <= 0 || descriptor->channels >=
> FF_SANE_NB_CHANNELS) {
> >> +                av_log(mxf->fc, AV_LOG_ERROR, "Invalid number of
> channels %d, must be less than %d\n", descriptor->channels,
> FF_SANE_NB_CHANNELS);
> >> +                return AVERROR_INVALIDDATA;
> >> +            }
> >> +
> >> +            ret = parse_mca_labels(mxf, source_track, descriptor, st);
> >> +            if (ret < 0)
> >> +                return ret;
> >>          } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
> >>              enum AVMediaType type;
> >>              container_ul =
> mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
> >> @@ -2888,6 +3183,9 @@ static const MXFMetadataReadTableEntry
> mxf_metadata_read_table[] = {
> >>      { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5c,0x00
> }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /*
> VANC/VBI - SMPTE 436M */
> >>      { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00
> }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /*
> MPEG2AudioDescriptor */
> >>      { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x64,0x00
> }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* DC
> Timed Text Descriptor */
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00
> }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor),
> AudioChannelLabelSubDescriptor },
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6c,0x00
> }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor),
> SoundfieldGroupLabelSubDescriptor },
> >> +    { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6d,0x00
> }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor),
> GroupOfSoundfieldGroupsLabelSubDescriptor },
> >>      { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00
> }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */
> >>      { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00
> }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */
> >>      { {
> 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00
> }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent),
> TimecodeComponent },
> >> @@ -3187,12 +3485,6 @@ static void
> mxf_compute_essence_containers(AVFormatContext *s)
> >>      }
> >>  }
> >>
> >> -static int is_pcm(enum AVCodecID codec_id)
> >> -{
> >> -    /* we only care about "normal" PCM codecs until we get samples */
> >> -    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id <
> AV_CODEC_ID_PCM_S24DAUD;
> >> -}
> >> -
> >>  static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int
> index_sid)
> >>  {
> >>      int i;
> >> @@ -3619,6 +3911,25 @@ static int mxf_set_pts(MXFContext *mxf, AVStream
> *st, AVPacket *pkt)
> >>      return 0;
> >>  }
> >>
> >> +static int mxf_audio_remapping(int* channel_ordering, uint8_t* data,
> int size, int sample_size, int channels)
> >> +{
> >> +    int sample_offset = channels * sample_size;
> >> +    int number_of_samples = size / sample_offset;
> >> +    uint8_t tmp[FF_SANE_NB_CHANNELS * 4];
> >> +    uint8_t* data_ptr = data;
> >> +
> >> +    for (int sample = 0; sample < number_of_samples; ++sample) {
> >> +        memcpy(tmp, data_ptr, sample_offset);
> >> +
> >> +        for (int channel = 0; channel < channels; ++channel) {
> >> +            memcpy(&data_ptr[sample_size * channel_ordering[channel]],
> &tmp[sample_size * channel], sample_size);
> >> +        }
> >> +
> >> +        data_ptr += sample_offset;
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >>  static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
> >>  {
> >>      KLVPacket klv;
> >> @@ -3733,6 +4044,15 @@ static int mxf_read_packet(AVFormatContext *s,
> AVPacket *pkt)
> >>                  return ret;
> >>              }
> >>
> >> +            // for audio, process audio remapping if MCA label
> requires it
> >> +            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
> track->require_reordering) {
> >> +                int byte_per_sample =
> st->codecpar->bits_per_coded_sample / 8;
> >> +                ret = mxf_audio_remapping(track->channel_ordering,
> pkt->data, pkt->size, byte_per_sample, st->codecpar->channels);
> >> +                if (ret < 0) {
> >> +                    return ret;
> >> +                }
> >> +            }
> >> +
> >>              /* seek for truncated packets */
> >>              avio_seek(s->pb, klv.next_klv, SEEK_SET);
> >>
> >> @@ -3920,6 +4240,9 @@ static const AVOption options[] = {
> >>      { "eia608_extract", "extract eia 608 captions from s436m track",
> >>        offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 =
> 0}, 0, 1,
> >>        AV_OPT_FLAG_DECODING_PARAM },
> >> +    { "skip_audio_reordering", "skip audio reordering based on
> Multi-Channel Audio labelling",
> >> +      offsetof(MXFContext, skip_audio_reordering), AV_OPT_TYPE_BOOL,
> {.i64 = 0}, 0, 1,
> >> +      AV_OPT_FLAG_DECODING_PARAM },
> >>      { NULL },
> >>  };
> >>
> >> diff --git a/libavformat/version.h b/libavformat/version.h
> >> index 0705ee4112..21ca6ed096 100644
> >> --- a/libavformat/version.h
> >> +++ b/libavformat/version.h
> >> @@ -33,7 +33,7 @@
> >>  // Also please add any ticket numbers that you believe might be
> affected here
> >>  #define LIBAVFORMAT_VERSION_MAJOR  59
> >>  #define LIBAVFORMAT_VERSION_MINOR   9
> >> -#define LIBAVFORMAT_VERSION_MICRO 102
> >> +#define LIBAVFORMAT_VERSION_MICRO 103
> >>
> >>  #define LIBAVFORMAT_VERSION_INT
> AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
> >>
>  LIBAVFORMAT_VERSION_MINOR, \
> >> --
> >> 2.33.1
> >>
> >
> >
> > --
> > Marc-Antoine ARNAUD
> > CEO & Founder
> >
> > mobile:  +33 6 84 71 84 45
> > email:     arnaud.marc-antoine@luminvent.com
> > website:  luminvent.com
> > _______________________________________________
> > 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".
>
Tomas Härdin Dec. 21, 2021, 1:53 p.m. UTC | #4
tis 2021-12-21 klockan 11:44 +0100 skrev Marc-Antoine ARNAUD:
> Le ven. 17 déc. 2021 à 19:12, Marton Balint <cus@passwd.hu> a écrit :
> 
> > 
> > 
> > On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:
> > 
> > > Hi all,
> > > 
> > > Can I have an update on this patch submission ?
> > > Is something required to be done before it can be merged ?
> > 
> > New channel layout API is on its way, which makes in-demuxer
> > channel
> > reordering uneeded. Therefore the reordering option should not be
> > added
> > as it is in this patch. I can rework the patch after the channel
> > layout
> > API is in. (should happen in a couple of weeks at most).
> > 
> > Regards,
> > Marton
> > 
> 
> So it will happen only after the release 5 of FFMpeg right ?
> 
> Is it possible to merge it, and we can rework it after the new API is
> released ?
> Patches are related to IMF (new format) patches, and if FFmpeg can
> accept
> IMF without MCA support it will generate a lot of errors in audio
> mapping.
> So even if it's not performant for now, is it possible to imagine to
> merge
> patches and rework after ?

I suspect this might create problems for the people writing the
reordering API

/Tomas
Marton Balint Dec. 21, 2021, 8:24 p.m. UTC | #5
On Tue, 21 Dec 2021, Tomas Härdin wrote:

> tis 2021-12-21 klockan 11:44 +0100 skrev Marc-Antoine ARNAUD:
>> Le ven. 17 déc. 2021 à 19:12, Marton Balint <cus@passwd.hu> a écrit :
>> 
>> > 
>> > 
>> > On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:
>> > 
>> > > Hi all,
>> > > 
>> > > Can I have an update on this patch submission ?
>> > > Is something required to be done before it can be merged ?
>> > 
>> > New channel layout API is on its way, which makes in-demuxer
>> > channel
>> > reordering uneeded. Therefore the reordering option should not be
>> > added
>> > as it is in this patch. I can rework the patch after the channel
>> > layout
>> > API is in. (should happen in a couple of weeks at most).
>> > 
>> > Regards,
>> > Marton
>> > 
>> 
>> So it will happen only after the release 5 of FFMpeg right ?

Not sure. There were people who wanted the merge the channel layout api 
before the release.

>> 
>> Is it possible to merge it, and we can rework it after the new API is
>> released ?
>> Patches are related to IMF (new format) patches, and if FFmpeg can
>> accept
>> IMF without MCA support it will generate a lot of errors in audio
>> mapping.
>> So even if it's not performant for now, is it possible to imagine to
>> merge
>> patches and rework after ?
>
> I suspect this might create problems for the people writing the
> reordering API

It is not matter of performance, we should not introduce a hack such as 
reordering PCM channels in a demuxer if there is a better solution on the 
horizon.

If this is urgent, we could merge it without reordering support.

Regards,
Marton
Tomas Härdin Dec. 22, 2021, 12:37 p.m. UTC | #6
tis 2021-12-21 klockan 21:24 +0100 skrev Marton Balint:
> 
> 
> On Tue, 21 Dec 2021, Tomas Härdin wrote:
> 
> > tis 2021-12-21 klockan 11:44 +0100 skrev Marc-Antoine ARNAUD:
> > > Le ven. 17 déc. 2021 à 19:12, Marton Balint <cus@passwd.hu> a
> > > écrit :
> > > 
> > > > 
> > > > 
> > > > On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:
> > > > 
> > > > > Hi all,
> > > > > 
> > > > > Can I have an update on this patch submission ?
> > > > > Is something required to be done before it can be merged ?
> > > > 
> > > > New channel layout API is on its way, which makes in-demuxer
> > > > channel
> > > > reordering uneeded. Therefore the reordering option should not
> > > > be
> > > > added
> > > > as it is in this patch. I can rework the patch after the
> > > > channel
> > > > layout
> > > > API is in. (should happen in a couple of weeks at most).
> > > > 
> > > > Regards,
> > > > Marton
> > > > 
> > > 
> > > So it will happen only after the release 5 of FFMpeg right ?
> 
> Not sure. There were people who wanted the merge the channel layout
> api 
> before the release.
> 
> > > 
> > > Is it possible to merge it, and we can rework it after the new
> > > API is
> > > released ?
> > > Patches are related to IMF (new format) patches, and if FFmpeg
> > > can
> > > accept
> > > IMF without MCA support it will generate a lot of errors in audio
> > > mapping.
> > > So even if it's not performant for now, is it possible to imagine
> > > to
> > > merge
> > > patches and rework after ?
> > 
> > I suspect this might create problems for the people writing the
> > reordering API
> 
> It is not matter of performance, we should not introduce a hack such
> as 
> reordering PCM channels in a demuxer if there is a better solution on
> the 
> horizon.
> 
> If this is urgent, we could merge it without reordering support.

I think we can trust our users to maintain their own forks if they need
special hacks

/Tomas
Pierre-Anthony Lemieux Dec. 22, 2021, 3:53 p.m. UTC | #7
On Wed, Dec 22, 2021 at 4:38 AM Tomas Härdin <tjoppen@acc.umu.se> wrote:
>
> tis 2021-12-21 klockan 21:24 +0100 skrev Marton Balint:
> >
> >
> > On Tue, 21 Dec 2021, Tomas Härdin wrote:
> >
> > > tis 2021-12-21 klockan 11:44 +0100 skrev Marc-Antoine ARNAUD:
> > > > Le ven. 17 déc. 2021 à 19:12, Marton Balint <cus@passwd.hu> a
> > > > écrit :
> > > >
> > > > >
> > > > >
> > > > > On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:
> > > > >
> > > > > > Hi all,
> > > > > >
> > > > > > Can I have an update on this patch submission ?
> > > > > > Is something required to be done before it can be merged ?
> > > > >
> > > > > New channel layout API is on its way, which makes in-demuxer
> > > > > channel
> > > > > reordering uneeded. Therefore the reordering option should not
> > > > > be
> > > > > added
> > > > > as it is in this patch. I can rework the patch after the
> > > > > channel
> > > > > layout
> > > > > API is in. (should happen in a couple of weeks at most).
> > > > >
> > > > > Regards,
> > > > > Marton
> > > > >
> > > >
> > > > So it will happen only after the release 5 of FFMpeg right ?
> >
> > Not sure. There were people who wanted the merge the channel layout
> > api
> > before the release.
> >
> > > >
> > > > Is it possible to merge it, and we can rework it after the new
> > > > API is
> > > > released ?
> > > > Patches are related to IMF (new format) patches, and if FFmpeg
> > > > can
> > > > accept
> > > > IMF without MCA support it will generate a lot of errors in audio
> > > > mapping.
> > > > So even if it's not performant for now, is it possible to imagine
> > > > to
> > > > merge
> > > > patches and rework after ?
> > >
> > > I suspect this might create problems for the people writing the
> > > reordering API
> >
> > It is not matter of performance, we should not introduce a hack such
> > as
> > reordering PCM channels in a demuxer if there is a better solution on
> > the
> > horizon.

Yes, it feels like channel reordering should be a framework-level
feature and not a per-demuxer feature.

What about a roadmap where the channel-reordering-in-MXF patch is
scheduled to be merged after some reasonable time (1 month?) if the
channel API has not landed by then? This would allow contributors and
users to plan ahead.

> >
> > If this is urgent, we could merge it without reordering support.
>
> I think we can trust our users to maintain their own forks if they need
> special hacks
>
> /Tomas
>
> _______________________________________________
> 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".
Marton Balint Jan. 1, 2022, 7:32 p.m. UTC | #8
On Wed, 22 Dec 2021, Pierre-Anthony Lemieux wrote:

> On Wed, Dec 22, 2021 at 4:38 AM Tomas Härdin <tjoppen@acc.umu.se> wrote:
>>
>> tis 2021-12-21 klockan 21:24 +0100 skrev Marton Balint:
>> >
>> >
>> > It is not matter of performance, we should not introduce a hack such
>> > as
>> > reordering PCM channels in a demuxer if there is a better solution on
>> > the
>> > horizon.
>
> Yes, it feels like channel reordering should be a framework-level
> feature and not a per-demuxer feature.
>
> What about a roadmap where the channel-reordering-in-MXF patch is
> scheduled to be merged after some reasonable time (1 month?) if the
> channel API has not landed by then? This would allow contributors and
> users to plan ahead.

I am not sure, if 1 month is enough for things to settle.

Anyway, I have sent a patch which has MCA info but without reordering. We 
could merge that now, and later, when the API is in place, add support for 
non-native channel ordering.

Regards,
Marton
Pierre-Anthony Lemieux Jan. 1, 2022, 11:15 p.m. UTC | #9
On Sat, Jan 1, 2022 at 11:32 AM Marton Balint <cus@passwd.hu> wrote:
>
>
>
> On Wed, 22 Dec 2021, Pierre-Anthony Lemieux wrote:
>
> > On Wed, Dec 22, 2021 at 4:38 AM Tomas Härdin <tjoppen@acc.umu.se> wrote:
> >>
> >> tis 2021-12-21 klockan 21:24 +0100 skrev Marton Balint:
> >> >
> >> >
> >> > It is not matter of performance, we should not introduce a hack such
> >> > as
> >> > reordering PCM channels in a demuxer if there is a better solution on
> >> > the
> >> > horizon.
> >
> > Yes, it feels like channel reordering should be a framework-level
> > feature and not a per-demuxer feature.
> >
> > What about a roadmap where the channel-reordering-in-MXF patch is
> > scheduled to be merged after some reasonable time (1 month?) if the
> > channel API has not landed by then? This would allow contributors and
> > users to plan ahead.
>
> I am not sure, if 1 month is enough for things to settle.
>
> Anyway, I have sent a patch which has MCA info but without reordering. We
> could merge that now, and later, when the API is in place, add support for
> non-native channel ordering.

Sounds good to me. I have made two minor suggestions on the proposed patch.

>
> Regards,
> Marton
> _______________________________________________
> 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".
diff mbox series

Patch

diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index cab8a7072c..23b6753602 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -770,6 +770,16 @@  MJPEG stream. Turning this option on by setting it to 1 will result in a stricte
 of the boundary value.
 @end table
 
+@section mxf
+
+MXF demuxer.
+
+@table @option
+
+@item -skip_audio_reordering @var{bool}
+This option will disable the audio reordering based on Multi-Channel Audio (MCA) labelling (SMPTE ST-377-4).
+@end table
+
 @section rawvideo
 
 Raw video demuxer.
diff --git a/libavformat/mxf.h b/libavformat/mxf.h
index fe9c52732c..d53a16df51 100644
--- a/libavformat/mxf.h
+++ b/libavformat/mxf.h
@@ -50,6 +50,9 @@  enum MXFMetadataSetType {
     TaggedValue,
     TapeDescriptor,
     AVCSubDescriptor,
+    AudioChannelLabelSubDescriptor,
+    SoundfieldGroupLabelSubDescriptor,
+    GroupOfSoundfieldGroupsLabelSubDescriptor,
 };
 
 enum MXFFrameLayout {
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index af9d33f796..6e1da75542 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -51,11 +51,14 @@ 
 #include "libavutil/mastering_display_metadata.h"
 #include "libavutil/mathematics.h"
 #include "libavcodec/bytestream.h"
+#include "libavcodec/internal.h"
+#include "libavutil/channel_layout.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/parseutils.h"
 #include "libavutil/timecode.h"
 #include "libavutil/opt.h"
 #include "avformat.h"
+#include "avlanguage.h"
 #include "internal.h"
 #include "mxf.h"
 
@@ -177,6 +180,8 @@  typedef struct {
     int body_sid;
     MXFWrappingScheme wrapping;
     int edit_units_per_packet; /* how many edit units to read at a time (PCM, ClipWrapped) */
+    int require_reordering;
+    int channel_ordering[FF_SANE_NB_CHANNELS];
 } MXFTrack;
 
 typedef struct MXFDescriptor {
@@ -205,6 +210,8 @@  typedef struct MXFDescriptor {
     unsigned int vert_subsampling;
     UID *file_descriptors_refs;
     int file_descriptors_count;
+    UID *sub_descriptors_refs;
+    int sub_descriptors_count;
     int linked_track_id;
     uint8_t *extradata;
     int extradata_size;
@@ -217,6 +224,18 @@  typedef struct MXFDescriptor {
     size_t coll_size;
 } MXFDescriptor;
 
+typedef struct MXFMCASubDescriptor {
+    MXFMetadataSet meta;
+    UID uid;
+    UID mca_link_id;
+    UID soundfield_group_link_id;
+    UID *group_of_soundfield_groups_link_id_refs;
+    int group_of_soundfield_groups_link_id_count;
+    UID mca_label_dictionary_id;
+    int mca_channel_id;
+    char *language;
+} MXFMCASubDescriptor;
+
 typedef struct MXFIndexTableSegment {
     MXFMetadataSet meta;
     int edit_unit_byte_count;
@@ -290,6 +309,7 @@  typedef struct MXFContext {
     int nb_index_tables;
     MXFIndexTable *index_tables;
     int eia608_extract;
+    int skip_audio_reordering;
 } MXFContext;
 
 /* NOTE: klv_offset is not set (-1) for local keys */
@@ -311,6 +331,7 @@  static const uint8_t mxf_system_item_key_cp[]              = { 0x06,0x0e,0x2b,0x
 static const uint8_t mxf_system_item_key_gc[]              = { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 };
 static const uint8_t mxf_klv_key[]                         = { 0x06,0x0e,0x2b,0x34 };
 static const uint8_t mxf_apple_coll_prefix[]               = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01 };
+
 /* complete keys to match */
 static const uint8_t mxf_crypto_source_container_ul[]      = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 };
 static const uint8_t mxf_encrypted_triplet_key[]           = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
@@ -323,6 +344,17 @@  static const uint8_t mxf_indirect_value_utf16be[]          = { 0x42,0x01,0x10,0x
 static const uint8_t mxf_apple_coll_max_cll[]              = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x01 };
 static const uint8_t mxf_apple_coll_max_fall[]             = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x02 };
 
+static const uint8_t mxf_mca_label_dictionary_id[]         = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x01,0x00,0x00,0x00 };
+static const uint8_t mxf_mca_tag_symbol[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x02,0x00,0x00,0x00 };
+static const uint8_t mxf_mca_tag_name[]                    = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x03,0x00,0x00,0x00 };
+static const uint8_t mxf_group_of_soundfield_groups_link_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x04,0x00,0x00,0x00 };
+static const uint8_t mxf_mca_link_id[]                     = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x05,0x00,0x00,0x00 };
+static const uint8_t mxf_mca_channel_id[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x04,0x0a,0x00,0x00,0x00,0x00 };
+static const uint8_t mxf_soundfield_group_link_id[]        = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x06,0x00,0x00,0x00 };
+static const uint8_t mxf_mca_rfc5646_spoken_language[]     = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0d,0x03,0x01,0x01,0x02,0x03,0x15,0x00,0x00 };
+
+static const uint8_t mxf_sub_descriptor[]                  = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00 };
+
 static const uint8_t mxf_mastering_display_prefix[13]      = { FF_MXF_MasteringDisplay_PREFIX };
 static const uint8_t mxf_mastering_display_uls[4][16] = {
     FF_MXF_MasteringDisplayPrimaries,
@@ -343,6 +375,13 @@  static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx)
         av_freep(&((MXFDescriptor *)*ctx)->mastering);
         av_freep(&((MXFDescriptor *)*ctx)->coll);
         av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs);
+        av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
+        break;
+    case AudioChannelLabelSubDescriptor:
+    case SoundfieldGroupLabelSubDescriptor:
+    case GroupOfSoundfieldGroupsLabelSubDescriptor:
+        av_freep(&((MXFMCASubDescriptor *)*ctx)->language);
+        av_freep(&((MXFMCASubDescriptor *)*ctx)->group_of_soundfield_groups_link_id_refs);
         break;
     case Sequence:
         av_freep(&((MXFSequence *)*ctx)->structural_components_refs);
@@ -906,6 +945,30 @@  static int mxf_read_strong_ref_array(AVIOContext *pb, UID **refs, int *count)
     return 0;
 }
 
+static inline int mxf_read_us_ascii_string(AVIOContext *pb, int size, char** str)
+{
+    int ret;
+    size_t buf_size;
+
+    if (size < 0 || size > INT_MAX - 1)
+        return AVERROR(EINVAL);
+
+    buf_size = size + 1;
+    av_free(*str);
+    *str = av_malloc(buf_size);
+    if (!*str)
+        return AVERROR(ENOMEM);
+
+    ret = avio_get_str(pb, size, *str, buf_size);
+
+    if (ret < 0) {
+        av_freep(str);
+        return ret;
+    }
+
+    return ret;
+}
+
 static inline int mxf_read_utf16_string(AVIOContext *pb, int size, char** str, int be)
 {
     int ret;
@@ -1360,11 +1423,40 @@  static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
                 descriptor->coll->MaxFALL = avio_rb16(pb);
             }
         }
+
+        if (IS_KLV_KEY(uid, mxf_sub_descriptor))
+            return mxf_read_strong_ref_array(pb, &descriptor->sub_descriptors_refs, &descriptor->sub_descriptors_count);
+
         break;
     }
     return 0;
 }
 
+static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
+{
+    MXFMCASubDescriptor *mca_sub_descriptor = arg;
+
+    if (IS_KLV_KEY(uid, mxf_mca_label_dictionary_id))
+        avio_read(pb, mca_sub_descriptor->mca_label_dictionary_id, 16);
+
+    if (IS_KLV_KEY(uid, mxf_mca_link_id))
+        avio_read(pb, mca_sub_descriptor->mca_link_id, 16);
+
+    if (IS_KLV_KEY(uid, mxf_soundfield_group_link_id))
+        avio_read(pb, mca_sub_descriptor->soundfield_group_link_id, 16);
+
+    if (IS_KLV_KEY(uid, mxf_group_of_soundfield_groups_link_id))
+        return mxf_read_strong_ref_array(pb, &mca_sub_descriptor->group_of_soundfield_groups_link_id_refs, &mca_sub_descriptor->group_of_soundfield_groups_link_id_count);
+
+    if (IS_KLV_KEY(uid, mxf_mca_channel_id))
+        mca_sub_descriptor->mca_channel_id = avio_rb32(pb);
+
+    if (IS_KLV_KEY(uid, mxf_mca_rfc5646_spoken_language))
+        return mxf_read_us_ascii_string(pb, size, &mca_sub_descriptor->language);
+
+    return 0;
+}
+
 static int mxf_read_indirect_value(void *arg, AVIOContext *pb, int size)
 {
     MXFTaggedValue *tagged_value = arg;
@@ -1494,6 +1586,50 @@  static const MXFCodecUL mxf_data_essence_container_uls[] = {
     { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  0, AV_CODEC_ID_NONE },
 };
 
+typedef struct MXFChannelOrderingUL {
+    UID uid;
+    uint64_t layout_mask;
+    enum AVAudioServiceType service_type;
+} MXFChannelOrderingUL;
+
+static const MXFChannelOrderingUL mxf_channel_ordering[] = {
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Left
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Right
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_MAIN }, // Center
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Low Frequency Effects
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, AV_CH_BACK_LEFT,             AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, AV_CH_BACK_RIGHT,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT_OF_CENTER,  AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Center
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, // Hearing impaired audio channel
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER,          AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00 }, AV_CH_TOP_FRONT_LEFT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00 }, AV_CH_TOP_FRONT_RIGHT,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00 }, AV_CH_TOP_FRONT_CENTER,      AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00 }, AV_CH_TOP_BACK_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00 }, AV_CH_TOP_BACK_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT,        AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00 }, AV_CH_TOP_CENTER,            AV_AUDIO_SERVICE_TYPE_MAIN }, // Top Surround
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00 }, AV_CH_LOW_FREQUENCY,         AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front Subwoofer
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00 }, AV_CH_LOW_FREQUENCY_2,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Front Subwoofer
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00 }, AV_CH_TOP_BACK_CENTER,       AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear Height
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00 }, AV_CH_BACK_CENTER,           AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00 }, AV_CH_BOTTOM_FRONT_LEFT,     AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00 }, AV_CH_BOTTOM_FRONT_RIGHT,    AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below
+    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00 }, AV_CH_BOTTOM_FRONT_CENTER,   AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Below
+    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0,                           AV_AUDIO_SERVICE_TYPE_NB },
+};
+
 static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul)
 {
     int val;
@@ -2292,6 +2428,156 @@  static enum AVColorRange mxf_get_color_range(MXFContext *mxf, MXFDescriptor *des
     return AVCOL_RANGE_UNSPECIFIED;
 }
 
+static int is_pcm(enum AVCodecID codec_id)
+{
+    /* we only care about "normal" PCM codecs until we get samples */
+    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
+}
+
+static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary **met)
+{
+    // language abbr should contain at least 2 chars
+    if (rfc5646 && strlen(rfc5646) > 1) {
+        char primary_tag[4] =
+            {rfc5646[0], rfc5646[1], rfc5646[2] != '-' ? rfc5646[2] : '\0', '\0'};
+
+        const char *iso6392       = ff_convert_lang_to(primary_tag,
+                                                       AV_LANG_ISO639_2_BIBL);
+        if (iso6392)
+            return(av_dict_set(met, "language", iso6392, 0));
+    }
+    return 0;
+}
+
+static MXFMCASubDescriptor *find_mca_link_id(MXFContext *mxf, enum MXFMetadataSetType type, UID *mca_link_id)
+{
+    for (int k = 0; k < mxf->metadata_sets_count; k++) {
+        MXFMCASubDescriptor *group = (MXFMCASubDescriptor*)mxf->metadata_sets[k];
+        if (group->meta.type == type && !memcmp(&group->mca_link_id, mca_link_id, 16))
+            return group;
+    }
+    return NULL;
+}
+
+static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescriptor *descriptor, AVStream *st)
+{
+    uint64_t routing[FF_SANE_NB_CHANNELS] = {0};
+    char *language = NULL;
+    int ambigous_language = 0;
+    enum AVAudioServiceType service_type = AV_AUDIO_SERVICE_TYPE_NB;
+    int ambigous_service_type = 0;
+    int has_channel_label = 0;
+
+    for (int i = 0; i < descriptor->sub_descriptors_count; i++) {
+        char *channel_language;
+
+        MXFMCASubDescriptor *label = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[i], AudioChannelLabelSubDescriptor);
+        if (label == NULL)
+            continue;
+
+        has_channel_label = 1;
+        for (const MXFChannelOrderingUL* channel_ordering = mxf_channel_ordering; channel_ordering->uid[0]; channel_ordering++) {
+            if (IS_KLV_KEY(channel_ordering->uid, label->mca_label_dictionary_id)) {
+                int target_channel = label->mca_channel_id;
+                if (target_channel == 0 && descriptor->channels == 1)
+                    target_channel = 1;
+                if (target_channel <= 0 || target_channel > descriptor->channels) {
+                    av_log(mxf->fc, AV_LOG_ERROR, "AudioChannelLabelSubDescriptor has invalid MCA channel ID %d\n", target_channel);
+                    return AVERROR_INVALIDDATA;
+                }
+                routing[target_channel - 1] = channel_ordering->layout_mask;
+                if (service_type == AV_AUDIO_SERVICE_TYPE_NB)
+                    service_type = channel_ordering->service_type;
+                else if (service_type != channel_ordering->service_type)
+                    ambigous_service_type = 1;
+                break;
+            }
+        }
+
+        channel_language = label->language;
+        if (!channel_language) {
+            MXFMCASubDescriptor *group = find_mca_link_id(mxf, SoundfieldGroupLabelSubDescriptor, &label->soundfield_group_link_id);
+            if (group) {
+                channel_language = group->language;
+                if (!channel_language && group->group_of_soundfield_groups_link_id_count) {
+                    MXFMCASubDescriptor *supergroup = find_mca_link_id(mxf, GroupOfSoundfieldGroupsLabelSubDescriptor,
+                                                                       group->group_of_soundfield_groups_link_id_refs);
+                    if (supergroup)
+                        channel_language = supergroup->language;
+                }
+            }
+        }
+        if (channel_language) {
+            if (language && strcmp(language, channel_language))
+                ambigous_language = 1;
+            else
+                language = channel_language;
+        }
+    }
+
+    if (language && !ambigous_language) {
+       int ret = set_language(mxf->fc, language, &st->metadata);
+       if (ret < 0)
+           return ret;
+    }
+
+    if (service_type != AV_AUDIO_SERVICE_TYPE_NB && service_type != AV_AUDIO_SERVICE_TYPE_MAIN && !ambigous_service_type) {
+        enum AVAudioServiceType *ast;
+        uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast));
+        if (!side_data)
+            return AVERROR(ENOMEM);
+        ast = (enum AVAudioServiceType*)side_data;
+        *ast = service_type;
+    }
+
+    if (has_channel_label) {
+        uint64_t channel_layout = 0;
+        int require_reorder = 0;
+
+        for (int i = 0; i < descriptor->channels; i++) {
+            if (!routing[i]) {
+                av_log(mxf->fc, AV_LOG_WARNING, "Designation of audio channel %d in stream #%d is unknown or unsupported, "
+                                                "falling back to unknown channel layout\n", st->index, i);
+                return 0;
+            }
+            if (channel_layout & routing[i]) {
+                av_log(mxf->fc, AV_LOG_WARNING, "%s audio channel is used multiple times in stream #%d, "
+                                                "falling back to unknown channel layout\n",
+                                                av_get_channel_name(routing[i]), st->index);
+                return 0;
+            }
+            if (routing[i] < channel_layout)
+                require_reorder = 1;
+            channel_layout |= routing[i];
+        }
+
+        av_assert0(descriptor->channels == av_get_channel_layout_nb_channels(channel_layout));
+
+        if (require_reorder) {
+            if (mxf->skip_audio_reordering)
+                return 0;
+            if (!is_pcm(st->codecpar->codec_id)) {
+                av_log(mxf->fc, AV_LOG_WARNING, "Audio channel reordering for stream #%d is not supported because it is using a non-PCM codec, "
+                                                "falling back to unknown channel layout\n", st->index);
+                return 0;
+            }
+            av_log(mxf->fc, AV_LOG_VERBOSE, "MCA mapping for stream #%d: ", st->index);
+            for (int j = 0; j < descriptor->channels; j++) {
+                int reordered_channel = av_get_channel_layout_channel_index(channel_layout, routing[j]);
+                av_assert0(reordered_channel >= 0 && reordered_channel < descriptor->channels);
+                source_track->channel_ordering[j] = reordered_channel;
+                av_log(mxf->fc, AV_LOG_VERBOSE, "%s%s: %d->%d", j ? ", " : "", av_get_channel_name(routing[j]), j, reordered_channel);
+            }
+            av_log(mxf->fc, AV_LOG_VERBOSE, "\n");
+            source_track->require_reordering = 1;
+        }
+
+        st->codecpar->channel_layout = channel_layout;
+    }
+
+    return 0;
+}
+
 static int mxf_parse_structural_metadata(MXFContext *mxf)
 {
     MXFPackage *material_package = NULL;
@@ -2688,6 +2974,15 @@  static int mxf_parse_structural_metadata(MXFContext *mxf)
                 sti->need_parsing = AVSTREAM_PARSE_FULL;
             }
             st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
+
+            if (descriptor->channels <= 0 || descriptor->channels >= FF_SANE_NB_CHANNELS) {
+                av_log(mxf->fc, AV_LOG_ERROR, "Invalid number of channels %d, must be less than %d\n", descriptor->channels, FF_SANE_NB_CHANNELS);
+                return AVERROR_INVALIDDATA;
+            }
+
+            ret = parse_mca_labels(mxf, source_track, descriptor, st);
+            if (ret < 0)
+                return ret;
         } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
             enum AVMediaType type;
             container_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul);
@@ -2888,6 +3183,9 @@  static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = {
     { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5c,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* VANC/VBI - SMPTE 436M */
     { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */
     { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x64,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* DC Timed Text Descriptor */
+    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), AudioChannelLabelSubDescriptor },
+    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6c,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), SoundfieldGroupLabelSubDescriptor },
+    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6d,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor },
     { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */
     { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */
     { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent },
@@ -3187,12 +3485,6 @@  static void mxf_compute_essence_containers(AVFormatContext *s)
     }
 }
 
-static int is_pcm(enum AVCodecID codec_id)
-{
-    /* we only care about "normal" PCM codecs until we get samples */
-    return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD;
-}
-
 static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid)
 {
     int i;
@@ -3619,6 +3911,25 @@  static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt)
     return 0;
 }
 
+static int mxf_audio_remapping(int* channel_ordering, uint8_t* data, int size, int sample_size, int channels)
+{
+    int sample_offset = channels * sample_size;
+    int number_of_samples = size / sample_offset;
+    uint8_t tmp[FF_SANE_NB_CHANNELS * 4];
+    uint8_t* data_ptr = data;
+
+    for (int sample = 0; sample < number_of_samples; ++sample) {
+        memcpy(tmp, data_ptr, sample_offset);
+
+        for (int channel = 0; channel < channels; ++channel) {
+            memcpy(&data_ptr[sample_size * channel_ordering[channel]], &tmp[sample_size * channel], sample_size);
+        }
+
+        data_ptr += sample_offset;
+    }
+    return 0;
+}
+
 static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     KLVPacket klv;
@@ -3733,6 +4044,15 @@  static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
                 return ret;
             }
 
+            // for audio, process audio remapping if MCA label requires it
+            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && track->require_reordering) {
+                int byte_per_sample = st->codecpar->bits_per_coded_sample / 8;
+                ret = mxf_audio_remapping(track->channel_ordering, pkt->data, pkt->size, byte_per_sample, st->codecpar->channels);
+                if (ret < 0) {
+                    return ret;
+                }
+            }
+
             /* seek for truncated packets */
             avio_seek(s->pb, klv.next_klv, SEEK_SET);
 
@@ -3920,6 +4240,9 @@  static const AVOption options[] = {
     { "eia608_extract", "extract eia 608 captions from s436m track",
       offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
       AV_OPT_FLAG_DECODING_PARAM },
+    { "skip_audio_reordering", "skip audio reordering based on Multi-Channel Audio labelling",
+      offsetof(MXFContext, skip_audio_reordering), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,
+      AV_OPT_FLAG_DECODING_PARAM },
     { NULL },
 };
 
diff --git a/libavformat/version.h b/libavformat/version.h
index 0705ee4112..21ca6ed096 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -33,7 +33,7 @@ 
 // Also please add any ticket numbers that you believe might be affected here
 #define LIBAVFORMAT_VERSION_MAJOR  59
 #define LIBAVFORMAT_VERSION_MINOR   9
-#define LIBAVFORMAT_VERSION_MICRO 102
+#define LIBAVFORMAT_VERSION_MICRO 103
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \