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

Marton Balint cus at passwd.hu
Fri Dec 17 20:12:17 EET 2021



On Fri, 17 Dec 2021, Marc-Antoine ARNAUD wrote:

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

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

Regards,
Marton

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


More information about the ffmpeg-devel mailing list