diff mbox series

[FFmpeg-devel,5/5] avformat/mxfdec: rework MCA channel layout parsing

Message ID 20211129005930.7188-5-cus@passwd.hu
State New
Headers show
Series [FFmpeg-devel,1/5] avformat/mxf: support MCA audio information | expand

Checks

Context Check Description
andriy/commit_msg_x86 warning Please wrap lines in the body of the commit message between 60 and 72 characters.
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/commit_msg_ppc warning Please wrap lines in the body of the commit message between 60 and 72 characters.
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Marton Balint Nov. 29, 2021, 12:59 a.m. UTC
Setting the channel layout was based on SoundfieldGroupLabelSubDescriptor, but
this was wrong, because soundfield groups are not necessarily used and a file
can also contain e.g. a single channel of a multichannel audio.

The new approach is to parse every AudioChannelLabelSubDescriptor and create a
channel layout based on those. We should also make sure that we only provide a
channel layout if every channel designation is recognized and is supported. If
a designation is not supported, or if a channel designation is used more than
once (e.g. multiple languages) we fall back to the unknown channel layout.

MCA channel ID was also ignored previously, that should have been used to
determine which AudioChannelLabelSubDescriptor references which audio channel.
This is also fixed now.

The new parsing code also detects ambigous language or service types, and only
set these metadata if all channels have the same value.

Soundfield groups are now only used for determinig the language of the tracks,
if it is not provided in the AudioChannelLabelSubDescriptor. Track sub
descriptors not necessarily contain the references to their soundfield groups,
so pairing of the soundfield group with a channel is now based on MCA group
link ID. The language of the first GroupOfSoundfieldGroups which a
SoundfieldGroup is part of is also considered if the SoundfieldGroup has no
language metadata.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/mxf.h    |   4 +-
 libavformat/mxfdec.c | 333 ++++++++++++++++++++++++-------------------
 2 files changed, 191 insertions(+), 146 deletions(-)

Comments

Pierre-Anthony Lemieux Nov. 29, 2021, 3:58 a.m. UTC | #1
On Sun, Nov 28, 2021 at 5:00 PM Marton Balint <cus@passwd.hu> wrote:
>
> Setting the channel layout was based on SoundfieldGroupLabelSubDescriptor, but
> this was wrong, because soundfield groups are not necessarily used and a file
> can also contain e.g. a single channel of a multichannel audio.

For reference, below are MCA scenarios I am aware of:

- single MXF file containing a single soundfield and all its
constituent channels (specified in the Interoperable Master Format -
IMF)
- single MXF file containing a single soundfield and all its
constituent channels, plus additional auxiliary channels, e.g.
visually-impaired narration (specified in Digital cinema)
- N MXF files, each containing one channel of a single soundfield
(specified in IMF, but I have personally never seen it in practice)

>
> The new approach is to parse every AudioChannelLabelSubDescriptor and create a
> channel layout based on those. We should also make sure that we only provide a
> channel layout if every channel designation is recognized and is supported. If
> a designation is not supported, or if a channel designation is used more than
> once (e.g. multiple languages) we fall back to the unknown channel layout.
>
> MCA channel ID was also ignored previously, that should have been used to
> determine which AudioChannelLabelSubDescriptor references which audio channel.
> This is also fixed now.
>
> The new parsing code also detects ambigous language or service types, and only
> set these metadata if all channels have the same value.
>
>
> Soundfield groups are now only used for determinig the language of the tracks,
> if it is not provided in the AudioChannelLabelSubDescriptor. Track sub
> descriptors not necessarily contain the references to their soundfield groups,

ST 377-4 states that "If the Audio Channel is part of a Soundfield
Group then the SoundfieldGroupLinkID shall be present in the
AudioChannelLabelSubDescriptor Set. The SoundfieldGroupLinkID shall be
the MCA Link ID of the SoundfieldGroupLabelSubDescriptor to which this
Audio Channel belongs."

What do you mean by "Track sub descriptors not necessarily contain the
references to their soundfield groups,"?

> so pairing of the soundfield group with a channel is now based on MCA group
> link ID. The language of the first GroupOfSoundfieldGroups which a
> SoundfieldGroup is part of is also considered if the SoundfieldGroup has no
> language metadata.
>
> Signed-off-by: Marton Balint <cus@passwd.hu>
> ---
>  libavformat/mxf.h    |   4 +-
>  libavformat/mxfdec.c | 333 ++++++++++++++++++++++++-------------------
>  2 files changed, 191 insertions(+), 146 deletions(-)
>
> diff --git a/libavformat/mxf.h b/libavformat/mxf.h
> index cddbcb13c9..d53a16df51 100644
> --- a/libavformat/mxf.h
> +++ b/libavformat/mxf.h
> @@ -50,7 +50,9 @@ enum MXFMetadataSetType {
>      TaggedValue,
>      TapeDescriptor,
>      AVCSubDescriptor,
> -    MCASubDescriptor,
> +    AudioChannelLabelSubDescriptor,
> +    SoundfieldGroupLabelSubDescriptor,
> +    GroupOfSoundfieldGroupsLabelSubDescriptor,
>  };
>
>  enum MXFFrameLayout {
> diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
> index 229c02a85e..6e1da75542 100644
> --- a/libavformat/mxfdec.c
> +++ b/libavformat/mxfdec.c
> @@ -228,8 +228,11 @@ typedef struct MXFMCASubDescriptor {
>      MXFMetadataSet meta;
>      UID uid;
>      UID mca_link_id;
> -    UID mca_group_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;
>
> @@ -328,8 +331,6 @@ 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 };
> -static const uint8_t mxf_audio_channel[]                   = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01 };
> -static const uint8_t mxf_soundfield_group[]                = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02 };
>
>  /* 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 };
> @@ -346,7 +347,9 @@ static const uint8_t mxf_apple_coll_max_fall[]             = { 0x06,0x0e,0x2b,0x
>  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 };
>
> @@ -374,8 +377,11 @@ static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx)
>          av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs);
>          av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
>          break;
> -    case MCASubDescriptor:
> +    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);
> @@ -1437,7 +1443,13 @@ static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int tag, int
>          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->mca_group_link_id, 16);
> +        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);
> @@ -1574,54 +1586,48 @@ 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 MXFSoundfieldGroupUL {
> -    UID uid;
> -    int64_t id;
> -} MXFSoundfieldGroupUL;
> -
> -static const MXFSoundfieldGroupUL mxf_soundfield_groups[] = {
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_5POINT1 }, // 5.1
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_7POINT1 }, // 7.1 DS
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x03,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_7POINT1_WIDE }, // 7.1 SDS
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_6POINT1 }, // 6.1
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x05,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_MONO }, // 1.0 Monoral
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x01,0x00,0x00,0x00 }, AV_CH_LAYOUT_STEREO }, // Standard stereo
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x02,0x00,0x00,0x00 }, AV_CH_LAYOUT_STEREO }, // Dual mono
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x04,0x00,0x00,0x00 }, AV_CH_LAYOUT_SURROUND }, // 3.0
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x05,0x00,0x00,0x00 }, AV_CH_LAYOUT_4POINT0 }, // 4.0
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x06,0x00,0x00,0x00 }, AV_CH_LAYOUT_5POINT0 }, // 5.0
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x07,0x00,0x00,0x00 }, AV_CH_LAYOUT_6POINT0 }, // 6.0
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x08,0x00,0x00,0x00 }, AV_CH_LAYOUT_7POINT0 }, // 7.0
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x09,0x00,0x00,0x00 }, AV_CH_LAYOUT_STEREO }, // LrRt
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x0a,0x00,0x00,0x00 }, AV_CH_LAYOUT_5POINT1 }, // 5.1 EX
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x0b,0x00,0x00,0x00 }, AV_CH_LAYOUT_MONO }, // Hearing accessibility
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x0c,0x00,0x00,0x00 }, AV_CH_LAYOUT_MONO }, // Visual accessibility
> -    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  0 },
> -};
> -
>  typedef struct MXFChannelOrderingUL {
>      UID uid;
> -    int index;
> +    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 }, 0, AV_AUDIO_SERVICE_TYPE_NB }, // Left audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, 1, AV_AUDIO_SERVICE_TYPE_NB }, // Right audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, 2, AV_AUDIO_SERVICE_TYPE_NB }, // Center audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, 5, AV_AUDIO_SERVICE_TYPE_NB }, // LFE audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, 3, AV_AUDIO_SERVICE_TYPE_NB }, // Left surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, 4, AV_AUDIO_SERVICE_TYPE_NB }, // Right surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, 3, AV_AUDIO_SERVICE_TYPE_NB }, // Left side surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, 4, AV_AUDIO_SERVICE_TYPE_NB }, // Right side surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, 6, AV_AUDIO_SERVICE_TYPE_NB }, // Left rear surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, 7, AV_AUDIO_SERVICE_TYPE_NB }, // Right rear surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, 6, AV_AUDIO_SERVICE_TYPE_NB }, // Left center audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, 7, AV_AUDIO_SERVICE_TYPE_NB }, // Right center audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, 6, AV_AUDIO_SERVICE_TYPE_NB }, // Center surround audio channel
> -    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, 0, 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 }, 0, AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel
> -    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_AUDIO_SERVICE_TYPE_NB },
> +    { { 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)
> @@ -2443,6 +2449,135 @@ static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary **
>      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;
> @@ -2480,7 +2615,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
>          FFStream *sti;
>          AVTimecode tc;
>          int flags;
> -        int current_channel;
>
>          if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) {
>              av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track strong ref\n");
> @@ -2841,106 +2975,14 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
>              }
>              st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
>
> -            current_channel = 0;
> -
>              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;
>              }
>
> -            for (j = 0; j < descriptor->channels; ++j) {
> -                source_track->channel_ordering[j] = j;
> -            }
> -
> -            for (j = 0; j < descriptor->sub_descriptors_count; j++) {
> -                MXFMCASubDescriptor *mca_sub_descriptor = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[j], MCASubDescriptor);
> -                if (mca_sub_descriptor == NULL) {
> -                    continue;
> -                }
> -
> -                // Soundfield group
> -                if (IS_KLV_KEY(mca_sub_descriptor->mca_label_dictionary_id, mxf_soundfield_group)) {
> -                    const MXFSoundfieldGroupUL* group_ptr = mxf_soundfield_groups;
> -
> -                    while (group_ptr->uid[0]) {
> -                        if (IS_KLV_KEY(group_ptr->uid, mca_sub_descriptor->mca_label_dictionary_id)) {
> -                            st->codecpar->channel_layout = group_ptr->id;
> -                            break;
> -                        }
> -                        group_ptr++;
> -                    }
> -                }
> -
> -                // Audio channel
> -                if (IS_KLV_KEY(mca_sub_descriptor->mca_label_dictionary_id, mxf_audio_channel)) {
> -                    const MXFChannelOrderingUL* channel_ordering_ptr = mxf_channel_ordering;
> -
> -                    while (channel_ordering_ptr->uid[0]) {
> -                        if (IS_KLV_KEY(channel_ordering_ptr->uid, mca_sub_descriptor->mca_label_dictionary_id)) {
> -                            if (current_channel >= FF_SANE_NB_CHANNELS) {
> -                                av_log(mxf->fc, AV_LOG_ERROR, "max number of channels %d reached\n", FF_SANE_NB_CHANNELS);
> -                                return AVERROR_INVALIDDATA;
> -                            }
> -                            if (channel_ordering_ptr->index >= FF_SANE_NB_CHANNELS) {
> -                                av_log(mxf->fc, AV_LOG_ERROR, "mapping to channel index %d out of range, maximum is %d\n", channel_ordering_ptr->index, FF_SANE_NB_CHANNELS);
> -                                return AVERROR_INVALIDDATA;
> -                            }
> -                            source_track->channel_ordering[current_channel] = channel_ordering_ptr->index;
> -
> -                            if(channel_ordering_ptr->service_type != AV_AUDIO_SERVICE_TYPE_NB) {
> -                                enum AVAudioServiceType *ast;
> -                                uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast));
> -                                if (!side_data)
> -                                    goto fail_and_free;
> -                                ast = (enum AVAudioServiceType*)side_data;
> -                                *ast = channel_ordering_ptr->service_type;
> -                            }
> -
> -                            current_channel += 1;
> -                            break;
> -                        }
> -                        channel_ordering_ptr++;
> -                    }
> -                }
> -
> -                // set language from MCA spoken language information
> -                if (mca_sub_descriptor->language) {
> -                    ret = set_language(mxf->fc, mca_sub_descriptor->language, &st->metadata);
> -                    if (ret < 0) {
> -                        return ret;
> -                    }
> -                }
> -            }
> -
> -            // check if the mapping is not required
> -            source_track->require_reordering = 0;
> -            for (j = 0; j < descriptor->channels; ++j) {
> -                if (source_track->channel_ordering[j] != j) {
> -                    if (!is_pcm(st->codecpar->codec_id) || mxf->skip_audio_reordering) {
> -                        av_log(mxf->fc, AV_LOG_WARNING, "Skipping channel reordering!\n");
> -                        st->codecpar->channel_layout = 0;
> -                    } else {
> -                        source_track->require_reordering = 1;
> -                    }
> -                    break;
> -                }
> -            }
> -
> -            if (source_track->require_reordering) {
> -                current_channel = 0;
> -                av_log(mxf->fc, AV_LOG_DEBUG, "MCA Audio mapping (");
> -                for(j = 0; j < descriptor->channels; ++j) {
> -                    for(int k = 0; k < descriptor->channels; ++k) {
> -                        if(source_track->channel_ordering[k] == current_channel) {
> -                            av_log(mxf->fc, AV_LOG_DEBUG, "%d -> %d", source_track->channel_ordering[k], k);
> -                            if (current_channel != descriptor->channels - 1)
> -                                av_log(mxf->fc, AV_LOG_DEBUG, ", ");
> -                            current_channel += 1;
> -                        }
> -                    }
> -                }
> -                av_log(mxf->fc, AV_LOG_DEBUG, ")\n");
> -            }
> +            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);
> @@ -3141,8 +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,0x6c,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), MCASubDescriptor }, /* Soundfield Group Label Subdescriptor */
> -    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), MCASubDescriptor }, /* Audio Channel Label Subdescriptor */
> +    { { 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 },
> --
> 2.31.1
>
> _______________________________________________
> 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 Nov. 29, 2021, 7:19 p.m. UTC | #2
On Sun, 28 Nov 2021, Pierre-Anthony Lemieux wrote:

> On Sun, Nov 28, 2021 at 5:00 PM Marton Balint <cus@passwd.hu> wrote:
>>
>> Setting the channel layout was based on SoundfieldGroupLabelSubDescriptor, but
>> this was wrong, because soundfield groups are not necessarily used and a file
>> can also contain e.g. a single channel of a multichannel audio.
>
> For reference, below are MCA scenarios I am aware of:
>
> - single MXF file containing a single soundfield and all its
> constituent channels (specified in the Interoperable Master Format -
> IMF)
> - single MXF file containing a single soundfield and all its
> constituent channels, plus additional auxiliary channels, e.g.
> visually-impaired narration (specified in Digital cinema)
> - N MXF files, each containing one channel of a single soundfield
> (specified in IMF, but I have personally never seen it in practice)
>
>>
>> The new approach is to parse every AudioChannelLabelSubDescriptor and create a
>> channel layout based on those. We should also make sure that we only provide a
>> channel layout if every channel designation is recognized and is supported. If
>> a designation is not supported, or if a channel designation is used more than
>> once (e.g. multiple languages) we fall back to the unknown channel layout.
>>
>> MCA channel ID was also ignored previously, that should have been used to
>> determine which AudioChannelLabelSubDescriptor references which audio channel.
>> This is also fixed now.
>>
>> The new parsing code also detects ambigous language or service types, and only
>> set these metadata if all channels have the same value.
>>
>>
>> Soundfield groups are now only used for determinig the language of the tracks,
>> if it is not provided in the AudioChannelLabelSubDescriptor. Track sub
>> descriptors not necessarily contain the references to their soundfield groups,
>
> ST 377-4 states that "If the Audio Channel is part of a Soundfield
> Group then the SoundfieldGroupLinkID shall be present in the
> AudioChannelLabelSubDescriptor Set. The SoundfieldGroupLinkID shall be
> the MCA Link ID of the SoundfieldGroupLabelSubDescriptor to which this
> Audio Channel belongs."
>
> What do you mean by "Track sub descriptors not necessarily contain the
> references to their soundfield groups,"?

I mean that the SubDescriptors item of the WavePCMDescriptor not 
necessarily contains the references to the soundfield group. E.g. if a 
soundfield group consist of two channels, one channel is in audio track 1, 
another channel is in audio track 2. As far as I understand only one
WavePCMDescriptor can reference the SoundfieldGroup in its SubDescriptors, 
because it is a strong reference.

Obviosly the soundfield group can be found by iterating over all the items 
and searching for the link ID, but not by iterating over the UID 
references of the SubDescriptors of the WavePCMDescriptor.

Regards,
Marton
Pierre-Anthony Lemieux Nov. 30, 2021, 7:11 a.m. UTC | #3
On Mon, Nov 29, 2021 at 11:20 AM Marton Balint <cus@passwd.hu> wrote:
>
>
>
> On Sun, 28 Nov 2021, Pierre-Anthony Lemieux wrote:
>
> > On Sun, Nov 28, 2021 at 5:00 PM Marton Balint <cus@passwd.hu> wrote:
> >>
> >> Setting the channel layout was based on SoundfieldGroupLabelSubDescriptor, but
> >> this was wrong, because soundfield groups are not necessarily used and a file
> >> can also contain e.g. a single channel of a multichannel audio.
> >
> > For reference, below are MCA scenarios I am aware of:
> >
> > - single MXF file containing a single soundfield and all its
> > constituent channels (specified in the Interoperable Master Format -
> > IMF)
> > - single MXF file containing a single soundfield and all its
> > constituent channels, plus additional auxiliary channels, e.g.
> > visually-impaired narration (specified in Digital cinema)
> > - N MXF files, each containing one channel of a single soundfield
> > (specified in IMF, but I have personally never seen it in practice)
> >
> >>
> >> The new approach is to parse every AudioChannelLabelSubDescriptor and create a
> >> channel layout based on those. We should also make sure that we only provide a
> >> channel layout if every channel designation is recognized and is supported. If
> >> a designation is not supported, or if a channel designation is used more than
> >> once (e.g. multiple languages) we fall back to the unknown channel layout.
> >>
> >> MCA channel ID was also ignored previously, that should have been used to
> >> determine which AudioChannelLabelSubDescriptor references which audio channel.
> >> This is also fixed now.
> >>
> >> The new parsing code also detects ambigous language or service types, and only
> >> set these metadata if all channels have the same value.
> >>
> >>
> >> Soundfield groups are now only used for determinig the language of the tracks,
> >> if it is not provided in the AudioChannelLabelSubDescriptor. Track sub
> >> descriptors not necessarily contain the references to their soundfield groups,
> >
> > ST 377-4 states that "If the Audio Channel is part of a Soundfield
> > Group then the SoundfieldGroupLinkID shall be present in the
> > AudioChannelLabelSubDescriptor Set. The SoundfieldGroupLinkID shall be
> > the MCA Link ID of the SoundfieldGroupLabelSubDescriptor to which this
> > Audio Channel belongs."
> >
> > What do you mean by "Track sub descriptors not necessarily contain the
> > references to their soundfield groups,"?
>
> I mean that the SubDescriptors item of the WavePCMDescriptor not
> necessarily contains the references to the soundfield group. E.g. if a
> soundfield group consist of two channels, one channel is in audio track 1,
> another channel is in audio track 2. As far as I understand only one
> WavePCMDescriptor can reference the SoundfieldGroup in its SubDescriptors,
> because it is a strong reference.

As I recall, the intent is for all MCA subdescriptors relevant to a
track to be contained within the File Descriptor for that track, as
suggested by Figures 1 and A.2 of the MCA spec (see attached).

For example, if the L channel of a stereo soundfield is in track 1 and
the R channel of a stereo soundfield is in track 2, then the File
Descriptor for track 1 would contain one L MCA Audio Channel
SubDescriptor and one Stereo MCA Soundfield SubDescriptor, while the
the File Descriptor for track 2 would contain one R MCA Audio Channel
SubDescriptor and one Stereo MCA Soundfield SubDescriptor. The two MCA
Soundfield SubDescriptors would have identical MCA Link ID.

The MCA spec states "all MCALabelSubDescriptor instances associated
with the audio described by a File Package shall be contained within
the same File Package". This could be construed as allowing a MCA
Soundfield SubDescriptor in one child descriptor of a
MultipleDescriptor to be referenced by a MCA Audio Channel
SubDescriptor from another child descriptor of the same
MultipleDescriptor. Happy to check with other implementers and report.
In any case, this is somewhat academic at this point unless anyone is
aware of MCA-enabled MXF files that are not single track and/or that
have soundfields split across File Packages. [ed.: I am not aware of
such files.]

It might therefore make sense to focus, for now, on single track MXF
files that contain complete soundfields, which your patch addresses if
I am not mistaken.

Happy to generate sample files. Just let me know.

>
> Obviosly the soundfield group can be found by iterating over all the items
> and searching for the link ID, but not by iterating over the UID
> references of the SubDescriptors of the WavePCMDescriptor.
>
> 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".
Marton Balint Nov. 30, 2021, 6:36 p.m. UTC | #4
On Mon, 29 Nov 2021, Pierre-Anthony Lemieux wrote:

> On Mon, Nov 29, 2021 at 11:20 AM Marton Balint <cus@passwd.hu> wrote:
>>
>>
>>
>> On Sun, 28 Nov 2021, Pierre-Anthony Lemieux wrote:
>>
>>> On Sun, Nov 28, 2021 at 5:00 PM Marton Balint <cus@passwd.hu> wrote:
>>>>
>>>> Setting the channel layout was based on SoundfieldGroupLabelSubDescriptor, but
>>>> this was wrong, because soundfield groups are not necessarily used and a file
>>>> can also contain e.g. a single channel of a multichannel audio.
>>>
>>> For reference, below are MCA scenarios I am aware of:
>>>
>>> - single MXF file containing a single soundfield and all its
>>> constituent channels (specified in the Interoperable Master Format -
>>> IMF)
>>> - single MXF file containing a single soundfield and all its
>>> constituent channels, plus additional auxiliary channels, e.g.
>>> visually-impaired narration (specified in Digital cinema)
>>> - N MXF files, each containing one channel of a single soundfield
>>> (specified in IMF, but I have personally never seen it in practice)
>>>
>>>>
>>>> The new approach is to parse every AudioChannelLabelSubDescriptor and create a
>>>> channel layout based on those. We should also make sure that we only provide a
>>>> channel layout if every channel designation is recognized and is supported. If
>>>> a designation is not supported, or if a channel designation is used more than
>>>> once (e.g. multiple languages) we fall back to the unknown channel layout.
>>>>
>>>> MCA channel ID was also ignored previously, that should have been used to
>>>> determine which AudioChannelLabelSubDescriptor references which audio channel.
>>>> This is also fixed now.
>>>>
>>>> The new parsing code also detects ambigous language or service types, and only
>>>> set these metadata if all channels have the same value.
>>>>
>>>>
>>>> Soundfield groups are now only used for determinig the language of the tracks,
>>>> if it is not provided in the AudioChannelLabelSubDescriptor. Track sub
>>>> descriptors not necessarily contain the references to their soundfield groups,
>>>
>>> ST 377-4 states that "If the Audio Channel is part of a Soundfield
>>> Group then the SoundfieldGroupLinkID shall be present in the
>>> AudioChannelLabelSubDescriptor Set. The SoundfieldGroupLinkID shall be
>>> the MCA Link ID of the SoundfieldGroupLabelSubDescriptor to which this
>>> Audio Channel belongs."
>>>
>>> What do you mean by "Track sub descriptors not necessarily contain the
>>> references to their soundfield groups,"?
>>
>> I mean that the SubDescriptors item of the WavePCMDescriptor not
>> necessarily contains the references to the soundfield group. E.g. if a
>> soundfield group consist of two channels, one channel is in audio track 1,
>> another channel is in audio track 2. As far as I understand only one
>> WavePCMDescriptor can reference the SoundfieldGroup in its SubDescriptors,
>> because it is a strong reference.
>
> As I recall, the intent is for all MCA subdescriptors relevant to a
> track to be contained within the File Descriptor for that track, as
> suggested by Figures 1 and A.2 of the MCA spec (see attached).
>
> For example, if the L channel of a stereo soundfield is in track 1 and
> the R channel of a stereo soundfield is in track 2, then the File
> Descriptor for track 1 would contain one L MCA Audio Channel
> SubDescriptor and one Stereo MCA Soundfield SubDescriptor, while the
> the File Descriptor for track 2 would contain one R MCA Audio Channel
> SubDescriptor and one Stereo MCA Soundfield SubDescriptor. The two MCA
> Soundfield SubDescriptors would have identical MCA Link ID.

I guess duplicating the SoundfieldGroupLabelSubDescriptors as many 
times as it is referenced as a strong reference can work, but having more 
than one SoundfieldGroupLabelSubDescriptor with the same MCA Link ID in 
the same file seems a bit ugly...

>
> The MCA spec states "all MCALabelSubDescriptor instances associated
> with the audio described by a File Package shall be contained within
> the same File Package". This could be construed as allowing a MCA
> Soundfield SubDescriptor in one child descriptor of a
> MultipleDescriptor to be referenced by a MCA Audio Channel
> SubDescriptor from another child descriptor of the same
> MultipleDescriptor. Happy to check with other implementers and report.

The MCA tests of BMXlib applications (raw2bmx, bmxtranswrap) can generate 
such files. Actually raw2bmx can generate both types, either with repeated 
soundfield groups, or non-repeated. See the attached samples and the track 
maps which were provided to raw2bmx.

> In any case, this is somewhat academic at this point unless anyone is
> aware of MCA-enabled MXF files that are not single track and/or that
> have soundfields split across File Packages. [ed.: I am not aware of
> such files.]
>
> It might therefore make sense to focus, for now, on single track MXF
> files that contain complete soundfields, which your patch addresses if
> I am not mistaken.

Yeah, those should work no problem, as long as all the channels in a 
track have different designation, but this is an ffmpeg limitation, and 
not an issue with parsing.

Regards,
Marton
Pierre-Anthony Lemieux Dec. 1, 2021, 5:32 a.m. UTC | #5
On Tue, Nov 30, 2021 at 10:36 AM Marton Balint <cus@passwd.hu> wrote:
>
>
>
> On Mon, 29 Nov 2021, Pierre-Anthony Lemieux wrote:
>
> > On Mon, Nov 29, 2021 at 11:20 AM Marton Balint <cus@passwd.hu> wrote:
> >>
> >>
> >>
> >> On Sun, 28 Nov 2021, Pierre-Anthony Lemieux wrote:
> >>
> >>> On Sun, Nov 28, 2021 at 5:00 PM Marton Balint <cus@passwd.hu> wrote:
> >>>>
> >>>> Setting the channel layout was based on SoundfieldGroupLabelSubDescriptor, but
> >>>> this was wrong, because soundfield groups are not necessarily used and a file
> >>>> can also contain e.g. a single channel of a multichannel audio.
> >>>
> >>> For reference, below are MCA scenarios I am aware of:
> >>>
> >>> - single MXF file containing a single soundfield and all its
> >>> constituent channels (specified in the Interoperable Master Format -
> >>> IMF)
> >>> - single MXF file containing a single soundfield and all its
> >>> constituent channels, plus additional auxiliary channels, e.g.
> >>> visually-impaired narration (specified in Digital cinema)
> >>> - N MXF files, each containing one channel of a single soundfield
> >>> (specified in IMF, but I have personally never seen it in practice)
> >>>
> >>>>
> >>>> The new approach is to parse every AudioChannelLabelSubDescriptor and create a
> >>>> channel layout based on those. We should also make sure that we only provide a
> >>>> channel layout if every channel designation is recognized and is supported. If
> >>>> a designation is not supported, or if a channel designation is used more than
> >>>> once (e.g. multiple languages) we fall back to the unknown channel layout.
> >>>>
> >>>> MCA channel ID was also ignored previously, that should have been used to
> >>>> determine which AudioChannelLabelSubDescriptor references which audio channel.
> >>>> This is also fixed now.
> >>>>
> >>>> The new parsing code also detects ambigous language or service types, and only
> >>>> set these metadata if all channels have the same value.
> >>>>
> >>>>
> >>>> Soundfield groups are now only used for determinig the language of the tracks,
> >>>> if it is not provided in the AudioChannelLabelSubDescriptor. Track sub
> >>>> descriptors not necessarily contain the references to their soundfield groups,
> >>>
> >>> ST 377-4 states that "If the Audio Channel is part of a Soundfield
> >>> Group then the SoundfieldGroupLinkID shall be present in the
> >>> AudioChannelLabelSubDescriptor Set. The SoundfieldGroupLinkID shall be
> >>> the MCA Link ID of the SoundfieldGroupLabelSubDescriptor to which this
> >>> Audio Channel belongs."
> >>>
> >>> What do you mean by "Track sub descriptors not necessarily contain the
> >>> references to their soundfield groups,"?
> >>
> >> I mean that the SubDescriptors item of the WavePCMDescriptor not
> >> necessarily contains the references to the soundfield group. E.g. if a
> >> soundfield group consist of two channels, one channel is in audio track 1,
> >> another channel is in audio track 2. As far as I understand only one
> >> WavePCMDescriptor can reference the SoundfieldGroup in its SubDescriptors,
> >> because it is a strong reference.
> >
> > As I recall, the intent is for all MCA subdescriptors relevant to a
> > track to be contained within the File Descriptor for that track, as
> > suggested by Figures 1 and A.2 of the MCA spec (see attached).
> >
> > For example, if the L channel of a stereo soundfield is in track 1 and
> > the R channel of a stereo soundfield is in track 2, then the File
> > Descriptor for track 1 would contain one L MCA Audio Channel
> > SubDescriptor and one Stereo MCA Soundfield SubDescriptor, while the
> > the File Descriptor for track 2 would contain one R MCA Audio Channel
> > SubDescriptor and one Stereo MCA Soundfield SubDescriptor. The two MCA
> > Soundfield SubDescriptors would have identical MCA Link ID.
>
> I guess duplicating the SoundfieldGroupLabelSubDescriptors as many
> times as it is referenced as a strong reference can work, but having more
> than one SoundfieldGroupLabelSubDescriptor with the same MCA Link ID in
> the same file seems a bit ugly...
>
> >
> > The MCA spec states "all MCALabelSubDescriptor instances associated
> > with the audio described by a File Package shall be contained within
> > the same File Package". This could be construed as allowing a MCA
> > Soundfield SubDescriptor in one child descriptor of a
> > MultipleDescriptor to be referenced by a MCA Audio Channel
> > SubDescriptor from another child descriptor of the same
> > MultipleDescriptor. Happy to check with other implementers and report.
>
> The MCA tests of BMXlib applications (raw2bmx, bmxtranswrap) can generate
> such files. Actually raw2bmx can generate both types, either with repeated
> soundfield groups, or non-repeated. See the attached samples and the track
> maps which were provided to raw2bmx.
>
> > In any case, this is somewhat academic at this point unless anyone is
> > aware of MCA-enabled MXF files that are not single track and/or that
> > have soundfields split across File Packages. [ed.: I am not aware of
> > such files.]
> >
> > It might therefore make sense to focus, for now, on single track MXF
> > files that contain complete soundfields, which your patch addresses if
> > I am not mistaken.
>
> Yeah, those should work no problem, as long as all the channels in a
> track have different designation, but this is an ffmpeg limitation, and
> not an issue with parsing.

LGTM. The following outputs the channels in L,R,C,LFE,LS,RS order,
despite the channels being ordered as L,C,R,LS,RS,LFE in the MXF file:

ffmpeg -i http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
build/out.wav

The MXF file was created using ASDCPLib:

as-02-wrap wav/Left.wav wav/Center.wav wav/Right.wav wav/LS.wav
wav/RS.wav wav/LFE.wav  -g en -m "51(L,C,R,Ls,Rs,LFE)"
callout_51_l_c_r_ls_rs_lfe.mxf

>
> 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/libavformat/mxf.h b/libavformat/mxf.h
index cddbcb13c9..d53a16df51 100644
--- a/libavformat/mxf.h
+++ b/libavformat/mxf.h
@@ -50,7 +50,9 @@  enum MXFMetadataSetType {
     TaggedValue,
     TapeDescriptor,
     AVCSubDescriptor,
-    MCASubDescriptor,
+    AudioChannelLabelSubDescriptor,
+    SoundfieldGroupLabelSubDescriptor,
+    GroupOfSoundfieldGroupsLabelSubDescriptor,
 };
 
 enum MXFFrameLayout {
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 229c02a85e..6e1da75542 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -228,8 +228,11 @@  typedef struct MXFMCASubDescriptor {
     MXFMetadataSet meta;
     UID uid;
     UID mca_link_id;
-    UID mca_group_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;
 
@@ -328,8 +331,6 @@  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 };
-static const uint8_t mxf_audio_channel[]                   = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01 };
-static const uint8_t mxf_soundfield_group[]                = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02 };
 
 /* 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 };
@@ -346,7 +347,9 @@  static const uint8_t mxf_apple_coll_max_fall[]             = { 0x06,0x0e,0x2b,0x
 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 };
 
@@ -374,8 +377,11 @@  static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx)
         av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs);
         av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs);
         break;
-    case MCASubDescriptor:
+    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);
@@ -1437,7 +1443,13 @@  static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int tag, int
         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->mca_group_link_id, 16);
+        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);
@@ -1574,54 +1586,48 @@  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 MXFSoundfieldGroupUL {
-    UID uid;
-    int64_t id;
-} MXFSoundfieldGroupUL;
-
-static const MXFSoundfieldGroupUL mxf_soundfield_groups[] = {
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_5POINT1 }, // 5.1
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_7POINT1 }, // 7.1 DS
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x03,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_7POINT1_WIDE }, // 7.1 SDS
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_6POINT1 }, // 6.1
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x05,0x00,0x00,0x00,0x00 }, AV_CH_LAYOUT_MONO }, // 1.0 Monoral
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x01,0x00,0x00,0x00 }, AV_CH_LAYOUT_STEREO }, // Standard stereo
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x02,0x00,0x00,0x00 }, AV_CH_LAYOUT_STEREO }, // Dual mono
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x04,0x00,0x00,0x00 }, AV_CH_LAYOUT_SURROUND }, // 3.0
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x05,0x00,0x00,0x00 }, AV_CH_LAYOUT_4POINT0 }, // 4.0
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x06,0x00,0x00,0x00 }, AV_CH_LAYOUT_5POINT0 }, // 5.0
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x07,0x00,0x00,0x00 }, AV_CH_LAYOUT_6POINT0 }, // 6.0
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x08,0x00,0x00,0x00 }, AV_CH_LAYOUT_7POINT0 }, // 7.0
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x09,0x00,0x00,0x00 }, AV_CH_LAYOUT_STEREO }, // LrRt
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x0a,0x00,0x00,0x00 }, AV_CH_LAYOUT_5POINT1 }, // 5.1 EX
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x0b,0x00,0x00,0x00 }, AV_CH_LAYOUT_MONO }, // Hearing accessibility
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x02,0x20,0x0c,0x00,0x00,0x00 }, AV_CH_LAYOUT_MONO }, // Visual accessibility
-    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },  0 },
-};
-
 typedef struct MXFChannelOrderingUL {
     UID uid;
-    int index;
+    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 }, 0, AV_AUDIO_SERVICE_TYPE_NB }, // Left audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, 1, AV_AUDIO_SERVICE_TYPE_NB }, // Right audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, 2, AV_AUDIO_SERVICE_TYPE_NB }, // Center audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, 5, AV_AUDIO_SERVICE_TYPE_NB }, // LFE audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, 3, AV_AUDIO_SERVICE_TYPE_NB }, // Left surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, 4, AV_AUDIO_SERVICE_TYPE_NB }, // Right surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, 3, AV_AUDIO_SERVICE_TYPE_NB }, // Left side surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, 4, AV_AUDIO_SERVICE_TYPE_NB }, // Right side surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, 6, AV_AUDIO_SERVICE_TYPE_NB }, // Left rear surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, 7, AV_AUDIO_SERVICE_TYPE_NB }, // Right rear surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, 6, AV_AUDIO_SERVICE_TYPE_NB }, // Left center audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, 7, AV_AUDIO_SERVICE_TYPE_NB }, // Right center audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, 6, AV_AUDIO_SERVICE_TYPE_NB }, // Center surround audio channel
-    { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, 0, 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 }, 0, AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel
-    { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_AUDIO_SERVICE_TYPE_NB },
+    { { 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)
@@ -2443,6 +2449,135 @@  static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary **
     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;
@@ -2480,7 +2615,6 @@  static int mxf_parse_structural_metadata(MXFContext *mxf)
         FFStream *sti;
         AVTimecode tc;
         int flags;
-        int current_channel;
 
         if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) {
             av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track strong ref\n");
@@ -2841,106 +2975,14 @@  static int mxf_parse_structural_metadata(MXFContext *mxf)
             }
             st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id);
 
-            current_channel = 0;
-
             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;
             }
 
-            for (j = 0; j < descriptor->channels; ++j) {
-                source_track->channel_ordering[j] = j;
-            }
-
-            for (j = 0; j < descriptor->sub_descriptors_count; j++) {
-                MXFMCASubDescriptor *mca_sub_descriptor = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[j], MCASubDescriptor);
-                if (mca_sub_descriptor == NULL) {
-                    continue;
-                }
-
-                // Soundfield group
-                if (IS_KLV_KEY(mca_sub_descriptor->mca_label_dictionary_id, mxf_soundfield_group)) {
-                    const MXFSoundfieldGroupUL* group_ptr = mxf_soundfield_groups;
-
-                    while (group_ptr->uid[0]) {
-                        if (IS_KLV_KEY(group_ptr->uid, mca_sub_descriptor->mca_label_dictionary_id)) {
-                            st->codecpar->channel_layout = group_ptr->id;
-                            break;
-                        }
-                        group_ptr++;
-                    }
-                }
-
-                // Audio channel
-                if (IS_KLV_KEY(mca_sub_descriptor->mca_label_dictionary_id, mxf_audio_channel)) {
-                    const MXFChannelOrderingUL* channel_ordering_ptr = mxf_channel_ordering;
-
-                    while (channel_ordering_ptr->uid[0]) {
-                        if (IS_KLV_KEY(channel_ordering_ptr->uid, mca_sub_descriptor->mca_label_dictionary_id)) {
-                            if (current_channel >= FF_SANE_NB_CHANNELS) {
-                                av_log(mxf->fc, AV_LOG_ERROR, "max number of channels %d reached\n", FF_SANE_NB_CHANNELS);
-                                return AVERROR_INVALIDDATA;
-                            }
-                            if (channel_ordering_ptr->index >= FF_SANE_NB_CHANNELS) {
-                                av_log(mxf->fc, AV_LOG_ERROR, "mapping to channel index %d out of range, maximum is %d\n", channel_ordering_ptr->index, FF_SANE_NB_CHANNELS);
-                                return AVERROR_INVALIDDATA;
-                            }
-                            source_track->channel_ordering[current_channel] = channel_ordering_ptr->index;
-
-                            if(channel_ordering_ptr->service_type != AV_AUDIO_SERVICE_TYPE_NB) {
-                                enum AVAudioServiceType *ast;
-                                uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast));
-                                if (!side_data)
-                                    goto fail_and_free;
-                                ast = (enum AVAudioServiceType*)side_data;
-                                *ast = channel_ordering_ptr->service_type;
-                            }
-
-                            current_channel += 1;
-                            break;
-                        }
-                        channel_ordering_ptr++;
-                    }
-                }
-
-                // set language from MCA spoken language information
-                if (mca_sub_descriptor->language) {
-                    ret = set_language(mxf->fc, mca_sub_descriptor->language, &st->metadata);
-                    if (ret < 0) {
-                        return ret;
-                    }
-                }
-            }
-
-            // check if the mapping is not required
-            source_track->require_reordering = 0;
-            for (j = 0; j < descriptor->channels; ++j) {
-                if (source_track->channel_ordering[j] != j) {
-                    if (!is_pcm(st->codecpar->codec_id) || mxf->skip_audio_reordering) {
-                        av_log(mxf->fc, AV_LOG_WARNING, "Skipping channel reordering!\n");
-                        st->codecpar->channel_layout = 0;
-                    } else {
-                        source_track->require_reordering = 1;
-                    }
-                    break;
-                }
-            }
-
-            if (source_track->require_reordering) {
-                current_channel = 0;
-                av_log(mxf->fc, AV_LOG_DEBUG, "MCA Audio mapping (");
-                for(j = 0; j < descriptor->channels; ++j) {
-                    for(int k = 0; k < descriptor->channels; ++k) {
-                        if(source_track->channel_ordering[k] == current_channel) {
-                            av_log(mxf->fc, AV_LOG_DEBUG, "%d -> %d", source_track->channel_ordering[k], k);
-                            if (current_channel != descriptor->channels - 1)
-                                av_log(mxf->fc, AV_LOG_DEBUG, ", ");
-                            current_channel += 1;
-                        }
-                    }
-                }
-                av_log(mxf->fc, AV_LOG_DEBUG, ")\n");
-            }
+            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);
@@ -3141,8 +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,0x6c,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), MCASubDescriptor }, /* Soundfield Group Label Subdescriptor */
-    { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), MCASubDescriptor }, /* Audio Channel Label Subdescriptor */
+    { { 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 },