diff mbox series

[FFmpeg-devel] avformat/mxfdec: support MCA audio information

Message ID 20220101192645.19849-1-cus@passwd.hu
State New
Headers show
Series [FFmpeg-devel] avformat/mxfdec: 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 fail Make fate failed

Commit Message

Marton Balint Jan. 1, 2022, 7:26 p.m. UTC
From: Marc-Antoine Arnaud <marc-antoine.arnaud@luminvent.com>

Channel reordering is removed from this patch because the new channel layout
API will support it properly.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/mxf.h    |   3 +
 libavformat/mxfdec.c | 286 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 283 insertions(+), 6 deletions(-)

Comments

Pierre-Anthony Lemieux Jan. 1, 2022, 11:13 p.m. UTC | #1
Minor suggestion below.

In addition, will sample file(s) be added to FATE? Below are two examples:

http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf

On Sat, Jan 1, 2022 at 11:27 AM Marton Balint <cus@passwd.hu> wrote:
>
> From: Marc-Antoine Arnaud <marc-antoine.arnaud@luminvent.com>
>
> Channel reordering is removed from this patch because the new channel layout
> API will support it properly.
>
> Signed-off-by: Marton Balint <cus@passwd.hu>
> ---
>  libavformat/mxf.h    |   3 +
>  libavformat/mxfdec.c | 286 ++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 283 insertions(+), 6 deletions(-)
>
> 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 1d50198279..26b5531720 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;
> @@ -311,6 +330,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 +343,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 +374,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 +944,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;
> @@ -1363,11 +1425,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;
> @@ -1497,6 +1588,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 },
> +};

I suggest adding the following audio channels, which are specified in
SMPTE ST 2067-8 [1] and have a direct mapping to ffmpeg audio
channels:

urn:smpte:ul:060e2b34.0401010d.03020120.03000000 (Left Total) ->
AV_CH_STEREO_LEFT
urn:smpte:ul:060e2b34.0401010d.03020120.04000000 (RightTotal) ->
AV_CH_STEREO_RIGHT

[1] https://registry.smpte-ra.org/view/published/labels_view.html

> +
>  static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul)
>  {
>      int val;
> @@ -2295,6 +2430,139 @@ 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;
> +
> +        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) {
> +                av_log(mxf->fc, AV_LOG_WARNING, "stream #%d is not in in native channel order, "
> +                                                "falling back to unknown channel layout\n", st->index);
> +                return 0;
> +            }
> +            channel_layout |= routing[i];
> +        }
> +
> +        av_assert0(descriptor->channels == av_get_channel_layout_nb_channels(channel_layout));
> +
> +        st->codecpar->channel_layout = channel_layout;
> +    }
> +
> +    return 0;
> +}
> +
>  static int mxf_parse_structural_metadata(MXFContext *mxf)
>  {
>      MXFPackage *material_package = NULL;
> @@ -2691,6 +2959,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);
> @@ -2891,6 +3168,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 },
> @@ -3190,12 +3470,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;
> --
> 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 Jan. 2, 2022, 1:11 a.m. UTC | #2
On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:

> Minor suggestion below.
>
> In addition, will sample file(s) be added to FATE? Below are two examples:
>
> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf

Good idea, can someone with access please upload these to the fate mxf 
folder?

[...]

> I suggest adding the following audio channels, which are specified in
> SMPTE ST 2067-8 [1] and have a direct mapping to ffmpeg audio
> channels:
>
> urn:smpte:ul:060e2b34.0401010d.03020120.03000000 (Left Total) ->
> AV_CH_STEREO_LEFT
> urn:smpte:ul:060e2b34.0401010d.03020120.04000000 (RightTotal) ->
> AV_CH_STEREO_RIGHT
>
> [1] https://registry.smpte-ra.org/view/published/labels_view.html

Unfortunately this is a Dolby encoded stereo mix, not an ordinary LeftOnly 
/ RightOnly mix. So I am not how this should be signalled.

Regards,
Marton
Andreas Rheinhardt Jan. 2, 2022, 1:35 a.m. UTC | #3
Pierre-Anthony Lemieux:
> Minor suggestion below.
> 
> In addition, will sample file(s) be added to FATE? Below are two examples:
> 
> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
> 

These files are 1.62MiB each, having 49 packets of lossless audio. Would
it decrease coverage if this were reduced? After all, the amount of
audio should not matter for channel reorderings.

- Andreas
Pierre-Anthony Lemieux Jan. 2, 2022, 1:54 a.m. UTC | #4
On Sat, Jan 1, 2022 at 5:35 PM Andreas Rheinhardt
<andreas.rheinhardt@outlook.com> wrote:
>
> Pierre-Anthony Lemieux:
> > Minor suggestion below.
> >
> > In addition, will sample file(s) be added to FATE? Below are two examples:
> >
> > http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
> > http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
> >
>
> These files are 1.62MiB each, having 49 packets of lossless audio. Would
> it decrease coverage if this were reduced? After all, the amount of
> audio should not matter for channel reorderings.

Each channel contains a synthesized human voice that announces the
name of the channel, so that a reviewer can, by simple inspection,
confirm the expected channel order. This will be particularly useful
when automatic channel reordering is performed.

The contents of each channel could be changed to a single tone with
each channel assigned a different tone, e.g. 1 to 6 kHz. This might
make the files shorter.

>
> - Andreas
> _______________________________________________
> 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".
Pierre-Anthony Lemieux Jan. 2, 2022, 2:16 a.m. UTC | #5
On Sat, Jan 1, 2022 at 5:11 PM Marton Balint <cus@passwd.hu> wrote:
>
>
>
> On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:
>
> > Minor suggestion below.
> >
> > In addition, will sample file(s) be added to FATE? Below are two examples:
> >
> > http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
> > http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
>
> Good idea, can someone with access please upload these to the fate mxf
> folder?
>
> [...]
>
> > I suggest adding the following audio channels, which are specified in
> > SMPTE ST 2067-8 [1] and have a direct mapping to ffmpeg audio
> > channels:AV_CH_STEREO_LEFT
> >
> > urn:smpte:ul:060e2b34.0401010d.03020120.03000000 (Left Total) ->
> > AV_CH_STEREO_LEFT
> > urn:smpte:ul:060e2b34.0401010d.03020120.04000000 (RightTotal) ->
> > AV_CH_STEREO_RIGHT
> >
> > [1] https://registry.smpte-ra.org/view/published/labels_view.html
>
> Unfortunately this is a Dolby encoded stereo mix, not an ordinary LeftOnly
> / RightOnly mix. So I am not how this should be signalled.

My interpretation below.

In "mov_chan.c", AV_CH_LAYOUT_STEREO_DOWNMIX means
MOV_CH_LAYOUT_MATRIXSTEREO [1], which is defined in Core Audio [2].

LtRt, i.e. matrix-encoded audio, is not generally Dolby-specific [3].

[1] https://github.com/FFmpeg/FFmpeg/blob/d6b2357eddca392ee137cb2a92ff178a0a7d0cce/libavformat/mov_chan.c#L168
[2] https://developer.apple.com/documentation/coreaudiotypes/1572101-audio_channel_layout_tags/kaudiochannellayouttag_matrixstereo?changes=_8_7&language=objc
[3] https://en.wikipedia.org/wiki/Matrix_decoder

>
> 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 Jan. 2, 2022, 10:26 a.m. UTC | #6
On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:

> On Sat, Jan 1, 2022 at 5:11 PM Marton Balint <cus@passwd.hu> wrote:
>>
>>
>>
>> On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:
>>
>>> Minor suggestion below.
>>>
>>> In addition, will sample file(s) be added to FATE? Below are two examples:
>>>
>>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
>>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
>>
>> Good idea, can someone with access please upload these to the fate mxf
>> folder?
>>
>> [...]
>>
>>> I suggest adding the following audio channels, which are specified in
>>> SMPTE ST 2067-8 [1] and have a direct mapping to ffmpeg audio
>>> channels:AV_CH_STEREO_LEFT
>>>
>>> urn:smpte:ul:060e2b34.0401010d.03020120.03000000 (Left Total) ->
>>> AV_CH_STEREO_LEFT
>>> urn:smpte:ul:060e2b34.0401010d.03020120.04000000 (RightTotal) ->
>>> AV_CH_STEREO_RIGHT
>>>
>>> [1] https://registry.smpte-ra.org/view/published/labels_view.html
>>
>> Unfortunately this is a Dolby encoded stereo mix, not an ordinary LeftOnly
>> / RightOnly mix. So I am not how this should be signalled.
>
> My interpretation below.
>
> In "mov_chan.c", AV_CH_LAYOUT_STEREO_DOWNMIX means
> MOV_CH_LAYOUT_MATRIXSTEREO [1], which is defined in Core Audio [2].
>
> LtRt, i.e. matrix-encoded audio, is not generally Dolby-specific [3].
>
> [1] https://github.com/FFmpeg/FFmpeg/blob/d6b2357eddca392ee137cb2a92ff178a0a7d0cce/libavformat/mov_chan.c#L168
> [2] https://developer.apple.com/documentation/coreaudiotypes/1572101-audio_channel_layout_tags/kaudiochannellayouttag_matrixstereo?changes=_8_7&language=objc
> [3] https://en.wikipedia.org/wiki/Matrix_decoder

Well, ok, I will add these two labels then before pushing.

Thanks,
Marton
Marton Balint Jan. 2, 2022, 10:33 a.m. UTC | #7
On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:

> On Sat, Jan 1, 2022 at 5:35 PM Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com> wrote:
>>
>> Pierre-Anthony Lemieux:
>>> Minor suggestion below.
>>>
>>> In addition, will sample file(s) be added to FATE? Below are two examples:
>>>
>>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
>>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
>>>
>>
>> These files are 1.62MiB each, having 49 packets of lossless audio. Would
>> it decrease coverage if this were reduced? After all, the amount of
>> audio should not matter for channel reorderings.
>
> Each channel contains a synthesized human voice that announces the
> name of the channel, so that a reviewer can, by simple inspection,
> confirm the expected channel order. This will be particularly useful
> when automatic channel reordering is performed.
>
> The contents of each channel could be changed to a single tone with
> each channel assigned a different tone, e.g. 1 to 6 kHz. This might
> make the files shorter.

I'd rather stick to the human voice.

Regards,
Marton
Michael Niedermayer Jan. 2, 2022, 5:35 p.m. UTC | #8
On Sun, Jan 02, 2022 at 02:11:01AM +0100, Marton Balint wrote:
> 
> 
> On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:
> 
> > Minor suggestion below.
> > 
> > In addition, will sample file(s) be added to FATE? Below are two examples:
> > 
> > http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
> > http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
> 
> Good idea, can someone with access please upload these to the fate mxf
> folder?

you should CC samples-request once theres agreement what to upload

thx

[...]
Andreas Rheinhardt Jan. 3, 2022, 7:12 a.m. UTC | #9
Pierre-Anthony Lemieux:
> On Sat, Jan 1, 2022 at 5:35 PM Andreas Rheinhardt
> <andreas.rheinhardt@outlook.com> wrote:
>>
>> Pierre-Anthony Lemieux:
>>> Minor suggestion below.
>>>
>>> In addition, will sample file(s) be added to FATE? Below are two examples:
>>>
>>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
>>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
>>>
>>
>> These files are 1.62MiB each, having 49 packets of lossless audio. Would
>> it decrease coverage if this were reduced? After all, the amount of
>> audio should not matter for channel reorderings.
> 
> Each channel contains a synthesized human voice that announces the
> name of the channel, so that a reviewer can, by simple inspection,
> confirm the expected channel order. This will be particularly useful
> when automatic channel reordering is performed.
> 
> The contents of each channel could be changed to a single tone with
> each channel assigned a different tone, e.g. 1 to 6 kHz. This might
> make the files shorter.
> 

A human voice sounds like something that is nice to have; but does it
have to be 24bit? As I see it, mxf allows 16bit sound everywhere where
it allows 24bit. Furthermore, the channels will still be recognizably
different if the audio is cut off at 1s (this will cut off the "effect"
from "low frequency effect" and will also cut off beeps for the others).

- Andreas
Pierre-Anthony Lemieux Jan. 4, 2022, 4:38 a.m. UTC | #10
On Sun, Jan 2, 2022 at 11:13 PM Andreas Rheinhardt
<andreas.rheinhardt@outlook.com> wrote:
>
> Pierre-Anthony Lemieux:
> > On Sat, Jan 1, 2022 at 5:35 PM Andreas Rheinhardt
> > <andreas.rheinhardt@outlook.com> wrote:
> >>
> >> Pierre-Anthony Lemieux:
> >>> Minor suggestion below.
> >>>
> >>> In addition, will sample file(s) be added to FATE? Below are two examples:
> >>>
> >>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
> >>> http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
> >>>
> >>
> >> These files are 1.62MiB each, having 49 packets of lossless audio. Would
> >> it decrease coverage if this were reduced? After all, the amount of
> >> audio should not matter for channel reorderings.
> >
> > Each channel contains a synthesized human voice that announces the
> > name of the channel, so that a reviewer can, by simple inspection,
> > confirm the expected channel order. This will be particularly useful
> > when automatic channel reordering is performed.
> >
> > The contents of each channel could be changed to a single tone with
> > each channel assigned a different tone, e.g. 1 to 6 kHz. This might
> > make the files shorter.
> >
>
> A human voice sounds like something that is nice to have; but does it
> have to be 24bit? As I see it, mxf allows 16bit sound everywhere where
> it allows 24bit.

I have never seen or created an MXF file that uses MCA and that has
16-bit PCM. I do not recommend treading new ground.

> Furthermore, the channels will still be recognizably
> different if the audio is cut off at 1s (this will cut off the "effect"
> from "low frequency effect" and will also cut off beeps for the others).

Ok. See:

http://ffmpeg-imf-samples-public.s3-website-us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.v2.mxf
http://ffmpeg-imf-samples-public.s3-website-us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.v2.mxf

>
> - Andreas
> _______________________________________________
> 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. 8, 2022, 6:48 p.m. UTC | #11
On Sun, 2 Jan 2022, Marton Balint wrote:

>
>
> On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:
>
>>  On Sat, Jan 1, 2022 at 5:11 PM Marton Balint <cus@passwd.hu> wrote:
>>> 
>>> 
>>>
>>>  On Sat, 1 Jan 2022, Pierre-Anthony Lemieux wrote:
>>>
>>>>  Minor suggestion below.
>>>>
>>>>  In addition, will sample file(s) be added to FATE? Below are two
>>>>  examples:
>>>>
>>>>  http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_r_c_lfe_ls_rs.mxf
>>>>  http://ffmpeg-imf-samples-public.s3.us-west-1.amazonaws.com/callout_51_l_c_r_ls_rs_lfe.mxf
>>>
>>>  Good idea, can someone with access please upload these to the fate mxf
>>>  folder?
>>>
>>>  [...]
>>>
>>>>  I suggest adding the following audio channels, which are specified in
>>>>  SMPTE ST 2067-8 [1] and have a direct mapping to ffmpeg audio
>>>>  channels:AV_CH_STEREO_LEFT
>>>>
>>>>  urn:smpte:ul:060e2b34.0401010d.03020120.03000000 (Left Total) ->
>>>>  AV_CH_STEREO_LEFT
>>>>  urn:smpte:ul:060e2b34.0401010d.03020120.04000000 (RightTotal) ->
>>>>  AV_CH_STEREO_RIGHT
>>>>
>>>>  [1] https://registry.smpte-ra.org/view/published/labels_view.html
>>>
>>>  Unfortunately this is a Dolby encoded stereo mix, not an ordinary
>>>  LeftOnly
>>>  / RightOnly mix. So I am not how this should be signalled.
>>
>>  My interpretation below.
>>
>>  In "mov_chan.c", AV_CH_LAYOUT_STEREO_DOWNMIX means
>>  MOV_CH_LAYOUT_MATRIXSTEREO [1], which is defined in Core Audio [2].
>>
>>  LtRt, i.e. matrix-encoded audio, is not generally Dolby-specific [3].
>>
>>  [1]
>>  https://github.com/FFmpeg/FFmpeg/blob/d6b2357eddca392ee137cb2a92ff178a0a7d0cce/libavformat/mov_chan.c#L168
>>  [2]
>>  https://developer.apple.com/documentation/coreaudiotypes/1572101-audio_channel_layout_tags/kaudiochannellayouttag_matrixstereo?changes=_8_7&language=objc
>>  [3] https://en.wikipedia.org/wiki/Matrix_decoder
>
> Well, ok, I will add these two labels then before pushing.

Will push and backport this tomorrow.

Regards,
Marton
diff mbox series

Patch

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 1d50198279..26b5531720 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;
@@ -311,6 +330,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 +343,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 +374,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 +944,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;
@@ -1363,11 +1425,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;
@@ -1497,6 +1588,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;
@@ -2295,6 +2430,139 @@  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;
+
+        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) {
+                av_log(mxf->fc, AV_LOG_WARNING, "stream #%d is not in in native channel order, "
+                                                "falling back to unknown channel layout\n", st->index);
+                return 0;
+            }
+            channel_layout |= routing[i];
+        }
+
+        av_assert0(descriptor->channels == av_get_channel_layout_nb_channels(channel_layout));
+
+        st->codecpar->channel_layout = channel_layout;
+    }
+
+    return 0;
+}
+
 static int mxf_parse_structural_metadata(MXFContext *mxf)
 {
     MXFPackage *material_package = NULL;
@@ -2691,6 +2959,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);
@@ -2891,6 +3168,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 },
@@ -3190,12 +3470,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;