From patchwork Fri Dec 3 09:57:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marc-Antoine ARNAUD X-Patchwork-Id: 31910 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp450050iog; Fri, 3 Dec 2021 01:57:57 -0800 (PST) X-Google-Smtp-Source: ABdhPJy6XO8oVNngM9C1jwhDiLixOEYK2Gzf999tPq3DOSTgJhcXn8EFo1V+AF33QtMR6oyljAGM X-Received: by 2002:a17:906:d54c:: with SMTP id cr12mr22063758ejc.56.1638525477072; Fri, 03 Dec 2021 01:57:57 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1638525477; cv=none; d=google.com; s=arc-20160816; b=AEX8nnB/kh//DJiuI3MnPfD4nhvh1n2SykOmO50H7Dn3nQOVTMZ5ktjmYhfSV9I88/ UWnxRBwZiNaDOf4Niw0QHzKz9sr3d+iYFv9noNu0CAjAlj4FEcWuXFl/88Otz/nIfppf LSteH1oWD14YJykx/Kh4Cbd+hf91MlUP7cH3OmsftxZzvthun12EkoXHdP1Wfw7anVg1 NmefGzpVMe9RfP7sdBSW94kzOzRL1vVXqj5g53cEf6j9ssp+wKUt/YU4F2eQ9PZOGVyC hD+dKj8T5QgNBC3siOFNGzc4YTwwBpuaCLJcTRMCNxhWUKWuivbPod1DkYHi14TRL0ly GVLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=cJWUuj8w0cJAuXB0zkKdUxYzhts4QFrHEsxn9taQUd0=; b=VqWvWYGDHvZ/hQ7LsFNdiWYo6kKwTZgX/iKFrQUWFpikAKCxAVw5RIgIj5xgc1azav f3puMJcsPaZ7fKv9qCUtcSqfIGZ1yTrJb0V4WXGU0EWBIQMfPqks0+cgfw1kRhgHLn/L I9eLvN/dYfKj1C4cZLUA4OBnvSP4AIof6k4xBLUDq4rbLoreHxmXmzOsngPh8Bhyi95a uaUrH+majZLfHtEcBNY7GfLLelzvDE0bs4DlI/lpAX8K4x7bD4ARR/husgNaZriEy5ci HDExFUzjJ2aaoKEK4MieLGWy0nh5FyXMi0oxPq3owU9QuRiTzJXeWpdrU2rW2m7cztAY pQqg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@luminvent-com.20210112.gappssmtp.com header.s=20210112 header.b=uc7JQTnp; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id a13si3757575edf.181.2021.12.03.01.57.56; Fri, 03 Dec 2021 01:57:57 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@luminvent-com.20210112.gappssmtp.com header.s=20210112 header.b=uc7JQTnp; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id E914A68AC65; Fri, 3 Dec 2021 11:57:52 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 18C9A68A583 for ; Fri, 3 Dec 2021 11:57:46 +0200 (EET) Received: by mail-wr1-f49.google.com with SMTP id o13so4472845wrs.12 for ; Fri, 03 Dec 2021 01:57:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=luminvent-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=KrGpxiRu4c0ibq9P8wKYADPf6j/vm8hiPzR+6AlEqsk=; b=uc7JQTnpppOtGIMXqJOU4+p7Ge0b1BMDTu5Cvb1+xBMWvyxj9mxdk4futPTPfqNKnt 5mLfMtJ1zO+qArzWv1UjwzQ3vShi0zJX+v+PAtGdEGAWnUg5k1IvfI11aP8Hn47ELTQU UebWkXwM3RXd+kWrkzS6oUzLDH9AJUVBZ6JjkqwxGQ/CQn5nFu2BtTZJBK65+mO+eEJm 0rLzBalX802GscxpZy3OeZkiHwashMQhfuQ/xwfSLgzeXIY5W05YyvNEK4GLLURhGQIh agUxLTMvxnK3MEgZ9/IYYFkbcEnUVea0AAnWuFdDTbQqg++nN9Zzyl/J+v0vDG3+5twM Nj7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=KrGpxiRu4c0ibq9P8wKYADPf6j/vm8hiPzR+6AlEqsk=; b=IyeSfar2854dM60hvl0yb3ydv1pnM5YrPx0LlZ7Ki2upRyEvSLomjnjOlitOlb6v4W hf8ize3F38JPSuEa+dOz0ULCeG32Wklc8cPZddd4er6NJitjD3MzH2Y5P1tFLPUR0r95 IxRTcBCjUyNOGgBMFBBf1CvDYs6XXPWUQ6xCz7lYjJcOdyXavMuRwV3ha9xbRZJPpNo0 agp5iphzcc9TUYy2p6gY0aeOJu7oh4AyorA0y2Morj4fwRYlEMKweZvmJP4cVqeGXKfZ uz8Hams6B3G3IxHEyBooqQylw/NVtVvlnZ9/RPrftNJmDlI/dUGC++y3F4QunGGN5wK/ ZUbg== X-Gm-Message-State: AOAM533vG+8XcI4LTG3ICacooLAZFSJZuUv3/LREZHIydVLLrpwimB5u uAKRPiUNCnrUQGpI2zzdNmi1WFqxHtXTEYGJ X-Received: by 2002:a5d:66c5:: with SMTP id k5mr20645089wrw.621.1638525465612; Fri, 03 Dec 2021 01:57:45 -0800 (PST) Received: from localhost.localdomain ([188.65.168.123]) by smtp.gmail.com with ESMTPSA id v15sm2302061wro.35.2021.12.03.01.57.44 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 03 Dec 2021 01:57:45 -0800 (PST) From: Marc-Antoine Arnaud To: ffmpeg-devel@ffmpeg.org Date: Fri, 3 Dec 2021 10:57:41 +0100 Message-Id: <20211203095742.16108-1-marc-antoine.arnaud@luminvent.com> X-Mailer: git-send-email 2.33.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/2] avformat/mxf: support MCA audio information X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Marc-Antoine Arnaud Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: x/NXJ9YjeaTR --- doc/demuxers.texi | 10 ++ libavformat/mxf.h | 3 + libavformat/mxfdec.c | 335 +++++++++++++++++++++++++++++++++++++++++- libavformat/version.h | 2 +- 4 files changed, 343 insertions(+), 7 deletions(-) diff --git a/doc/demuxers.texi b/doc/demuxers.texi index cab8a7072c..23b6753602 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -770,6 +770,16 @@ MJPEG stream. Turning this option on by setting it to 1 will result in a stricte of the boundary value. @end table +@section mxf + +MXF demuxer. + +@table @option + +@item -skip_audio_reordering @var{bool} +This option will disable the audio reordering based on Multi-Channel Audio (MCA) labelling (SMPTE ST-377-4). +@end table + @section rawvideo Raw video demuxer. diff --git a/libavformat/mxf.h b/libavformat/mxf.h index fe9c52732c..d53a16df51 100644 --- a/libavformat/mxf.h +++ b/libavformat/mxf.h @@ -50,6 +50,9 @@ enum MXFMetadataSetType { TaggedValue, TapeDescriptor, AVCSubDescriptor, + AudioChannelLabelSubDescriptor, + SoundfieldGroupLabelSubDescriptor, + GroupOfSoundfieldGroupsLabelSubDescriptor, }; enum MXFFrameLayout { diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index af9d33f796..6e1da75542 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -51,11 +51,14 @@ #include "libavutil/mastering_display_metadata.h" #include "libavutil/mathematics.h" #include "libavcodec/bytestream.h" +#include "libavcodec/internal.h" +#include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "libavutil/parseutils.h" #include "libavutil/timecode.h" #include "libavutil/opt.h" #include "avformat.h" +#include "avlanguage.h" #include "internal.h" #include "mxf.h" @@ -177,6 +180,8 @@ typedef struct { int body_sid; MXFWrappingScheme wrapping; int edit_units_per_packet; /* how many edit units to read at a time (PCM, ClipWrapped) */ + int require_reordering; + int channel_ordering[FF_SANE_NB_CHANNELS]; } MXFTrack; typedef struct MXFDescriptor { @@ -205,6 +210,8 @@ typedef struct MXFDescriptor { unsigned int vert_subsampling; UID *file_descriptors_refs; int file_descriptors_count; + UID *sub_descriptors_refs; + int sub_descriptors_count; int linked_track_id; uint8_t *extradata; int extradata_size; @@ -217,6 +224,18 @@ typedef struct MXFDescriptor { size_t coll_size; } MXFDescriptor; +typedef struct MXFMCASubDescriptor { + MXFMetadataSet meta; + UID uid; + UID mca_link_id; + UID soundfield_group_link_id; + UID *group_of_soundfield_groups_link_id_refs; + int group_of_soundfield_groups_link_id_count; + UID mca_label_dictionary_id; + int mca_channel_id; + char *language; +} MXFMCASubDescriptor; + typedef struct MXFIndexTableSegment { MXFMetadataSet meta; int edit_unit_byte_count; @@ -290,6 +309,7 @@ typedef struct MXFContext { int nb_index_tables; MXFIndexTable *index_tables; int eia608_extract; + int skip_audio_reordering; } MXFContext; /* NOTE: klv_offset is not set (-1) for local keys */ @@ -311,6 +331,7 @@ static const uint8_t mxf_system_item_key_cp[] = { 0x06,0x0e,0x2b,0x static const uint8_t mxf_system_item_key_gc[] = { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x03,0x01,0x14 }; static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 }; static const uint8_t mxf_apple_coll_prefix[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01 }; + /* complete keys to match */ static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 }; static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 }; @@ -323,6 +344,17 @@ static const uint8_t mxf_indirect_value_utf16be[] = { 0x42,0x01,0x10,0x static const uint8_t mxf_apple_coll_max_cll[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x01 }; static const uint8_t mxf_apple_coll_max_fall[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x0e,0x20,0x04,0x01,0x05,0x03,0x01,0x02 }; +static const uint8_t mxf_mca_label_dictionary_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x01,0x00,0x00,0x00 }; +static const uint8_t mxf_mca_tag_symbol[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x02,0x00,0x00,0x00 }; +static const uint8_t mxf_mca_tag_name[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x03,0x00,0x00,0x00 }; +static const uint8_t mxf_group_of_soundfield_groups_link_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x04,0x00,0x00,0x00 }; +static const uint8_t mxf_mca_link_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x05,0x00,0x00,0x00 }; +static const uint8_t mxf_mca_channel_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x04,0x0a,0x00,0x00,0x00,0x00 }; +static const uint8_t mxf_soundfield_group_link_id[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0e,0x01,0x03,0x07,0x01,0x06,0x00,0x00,0x00 }; +static const uint8_t mxf_mca_rfc5646_spoken_language[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0d,0x03,0x01,0x01,0x02,0x03,0x15,0x00,0x00 }; + +static const uint8_t mxf_sub_descriptor[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00 }; + static const uint8_t mxf_mastering_display_prefix[13] = { FF_MXF_MasteringDisplay_PREFIX }; static const uint8_t mxf_mastering_display_uls[4][16] = { FF_MXF_MasteringDisplayPrimaries, @@ -343,6 +375,13 @@ static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx) av_freep(&((MXFDescriptor *)*ctx)->mastering); av_freep(&((MXFDescriptor *)*ctx)->coll); av_freep(&((MXFDescriptor *)*ctx)->file_descriptors_refs); + av_freep(&((MXFDescriptor *)*ctx)->sub_descriptors_refs); + break; + case AudioChannelLabelSubDescriptor: + case SoundfieldGroupLabelSubDescriptor: + case GroupOfSoundfieldGroupsLabelSubDescriptor: + av_freep(&((MXFMCASubDescriptor *)*ctx)->language); + av_freep(&((MXFMCASubDescriptor *)*ctx)->group_of_soundfield_groups_link_id_refs); break; case Sequence: av_freep(&((MXFSequence *)*ctx)->structural_components_refs); @@ -906,6 +945,30 @@ static int mxf_read_strong_ref_array(AVIOContext *pb, UID **refs, int *count) return 0; } +static inline int mxf_read_us_ascii_string(AVIOContext *pb, int size, char** str) +{ + int ret; + size_t buf_size; + + if (size < 0 || size > INT_MAX - 1) + return AVERROR(EINVAL); + + buf_size = size + 1; + av_free(*str); + *str = av_malloc(buf_size); + if (!*str) + return AVERROR(ENOMEM); + + ret = avio_get_str(pb, size, *str, buf_size); + + if (ret < 0) { + av_freep(str); + return ret; + } + + return ret; +} + static inline int mxf_read_utf16_string(AVIOContext *pb, int size, char** str, int be) { int ret; @@ -1360,11 +1423,40 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int descriptor->coll->MaxFALL = avio_rb16(pb); } } + + if (IS_KLV_KEY(uid, mxf_sub_descriptor)) + return mxf_read_strong_ref_array(pb, &descriptor->sub_descriptors_refs, &descriptor->sub_descriptors_count); + break; } return 0; } +static int mxf_read_mca_sub_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset) +{ + MXFMCASubDescriptor *mca_sub_descriptor = arg; + + if (IS_KLV_KEY(uid, mxf_mca_label_dictionary_id)) + avio_read(pb, mca_sub_descriptor->mca_label_dictionary_id, 16); + + if (IS_KLV_KEY(uid, mxf_mca_link_id)) + avio_read(pb, mca_sub_descriptor->mca_link_id, 16); + + if (IS_KLV_KEY(uid, mxf_soundfield_group_link_id)) + avio_read(pb, mca_sub_descriptor->soundfield_group_link_id, 16); + + if (IS_KLV_KEY(uid, mxf_group_of_soundfield_groups_link_id)) + return mxf_read_strong_ref_array(pb, &mca_sub_descriptor->group_of_soundfield_groups_link_id_refs, &mca_sub_descriptor->group_of_soundfield_groups_link_id_count); + + if (IS_KLV_KEY(uid, mxf_mca_channel_id)) + mca_sub_descriptor->mca_channel_id = avio_rb32(pb); + + if (IS_KLV_KEY(uid, mxf_mca_rfc5646_spoken_language)) + return mxf_read_us_ascii_string(pb, size, &mca_sub_descriptor->language); + + return 0; +} + static int mxf_read_indirect_value(void *arg, AVIOContext *pb, int size) { MXFTaggedValue *tagged_value = arg; @@ -1494,6 +1586,50 @@ static const MXFCodecUL mxf_data_essence_container_uls[] = { { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; +typedef struct MXFChannelOrderingUL { + UID uid; + uint64_t layout_mask; + enum AVAudioServiceType service_type; +} MXFChannelOrderingUL; + +static const MXFChannelOrderingUL mxf_channel_ordering[] = { + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LOW_FREQUENCY, AV_AUDIO_SERVICE_TYPE_MAIN }, // Low Frequency Effects + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, AV_CH_BACK_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, AV_CH_BACK_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Center + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, AV_CH_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, // Hearing impaired audio channel + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00 }, AV_CH_TOP_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00 }, AV_CH_TOP_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00 }, AV_CH_TOP_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00 }, AV_CH_TOP_BACK_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00 }, AV_CH_TOP_BACK_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00 }, AV_CH_TOP_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Top Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00 }, AV_CH_LOW_FREQUENCY, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front Subwoofer + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00 }, AV_CH_LOW_FREQUENCY_2, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Front Subwoofer + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00 }, AV_CH_TOP_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00 }, AV_CH_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00 }, AV_CH_BOTTOM_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00 }, AV_CH_BOTTOM_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00 }, AV_CH_BOTTOM_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Below + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_AUDIO_SERVICE_TYPE_NB }, +}; + static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul) { int val; @@ -2292,6 +2428,156 @@ static enum AVColorRange mxf_get_color_range(MXFContext *mxf, MXFDescriptor *des return AVCOL_RANGE_UNSPECIFIED; } +static int is_pcm(enum AVCodecID codec_id) +{ + /* we only care about "normal" PCM codecs until we get samples */ + return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD; +} + +static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary **met) +{ + // language abbr should contain at least 2 chars + if (rfc5646 && strlen(rfc5646) > 1) { + char primary_tag[4] = + {rfc5646[0], rfc5646[1], rfc5646[2] != '-' ? rfc5646[2] : '\0', '\0'}; + + const char *iso6392 = ff_convert_lang_to(primary_tag, + AV_LANG_ISO639_2_BIBL); + if (iso6392) + return(av_dict_set(met, "language", iso6392, 0)); + } + return 0; +} + +static MXFMCASubDescriptor *find_mca_link_id(MXFContext *mxf, enum MXFMetadataSetType type, UID *mca_link_id) +{ + for (int k = 0; k < mxf->metadata_sets_count; k++) { + MXFMCASubDescriptor *group = (MXFMCASubDescriptor*)mxf->metadata_sets[k]; + if (group->meta.type == type && !memcmp(&group->mca_link_id, mca_link_id, 16)) + return group; + } + return NULL; +} + +static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescriptor *descriptor, AVStream *st) +{ + uint64_t routing[FF_SANE_NB_CHANNELS] = {0}; + char *language = NULL; + int ambigous_language = 0; + enum AVAudioServiceType service_type = AV_AUDIO_SERVICE_TYPE_NB; + int ambigous_service_type = 0; + int has_channel_label = 0; + + for (int i = 0; i < descriptor->sub_descriptors_count; i++) { + char *channel_language; + + MXFMCASubDescriptor *label = mxf_resolve_strong_ref(mxf, &descriptor->sub_descriptors_refs[i], AudioChannelLabelSubDescriptor); + if (label == NULL) + continue; + + has_channel_label = 1; + for (const MXFChannelOrderingUL* channel_ordering = mxf_channel_ordering; channel_ordering->uid[0]; channel_ordering++) { + if (IS_KLV_KEY(channel_ordering->uid, label->mca_label_dictionary_id)) { + int target_channel = label->mca_channel_id; + if (target_channel == 0 && descriptor->channels == 1) + target_channel = 1; + if (target_channel <= 0 || target_channel > descriptor->channels) { + av_log(mxf->fc, AV_LOG_ERROR, "AudioChannelLabelSubDescriptor has invalid MCA channel ID %d\n", target_channel); + return AVERROR_INVALIDDATA; + } + routing[target_channel - 1] = channel_ordering->layout_mask; + if (service_type == AV_AUDIO_SERVICE_TYPE_NB) + service_type = channel_ordering->service_type; + else if (service_type != channel_ordering->service_type) + ambigous_service_type = 1; + break; + } + } + + channel_language = label->language; + if (!channel_language) { + MXFMCASubDescriptor *group = find_mca_link_id(mxf, SoundfieldGroupLabelSubDescriptor, &label->soundfield_group_link_id); + if (group) { + channel_language = group->language; + if (!channel_language && group->group_of_soundfield_groups_link_id_count) { + MXFMCASubDescriptor *supergroup = find_mca_link_id(mxf, GroupOfSoundfieldGroupsLabelSubDescriptor, + group->group_of_soundfield_groups_link_id_refs); + if (supergroup) + channel_language = supergroup->language; + } + } + } + if (channel_language) { + if (language && strcmp(language, channel_language)) + ambigous_language = 1; + else + language = channel_language; + } + } + + if (language && !ambigous_language) { + int ret = set_language(mxf->fc, language, &st->metadata); + if (ret < 0) + return ret; + } + + if (service_type != AV_AUDIO_SERVICE_TYPE_NB && service_type != AV_AUDIO_SERVICE_TYPE_MAIN && !ambigous_service_type) { + enum AVAudioServiceType *ast; + uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast)); + if (!side_data) + return AVERROR(ENOMEM); + ast = (enum AVAudioServiceType*)side_data; + *ast = service_type; + } + + if (has_channel_label) { + uint64_t channel_layout = 0; + int require_reorder = 0; + + for (int i = 0; i < descriptor->channels; i++) { + if (!routing[i]) { + av_log(mxf->fc, AV_LOG_WARNING, "Designation of audio channel %d in stream #%d is unknown or unsupported, " + "falling back to unknown channel layout\n", st->index, i); + return 0; + } + if (channel_layout & routing[i]) { + av_log(mxf->fc, AV_LOG_WARNING, "%s audio channel is used multiple times in stream #%d, " + "falling back to unknown channel layout\n", + av_get_channel_name(routing[i]), st->index); + return 0; + } + if (routing[i] < channel_layout) + require_reorder = 1; + channel_layout |= routing[i]; + } + + av_assert0(descriptor->channels == av_get_channel_layout_nb_channels(channel_layout)); + + if (require_reorder) { + if (mxf->skip_audio_reordering) + return 0; + if (!is_pcm(st->codecpar->codec_id)) { + av_log(mxf->fc, AV_LOG_WARNING, "Audio channel reordering for stream #%d is not supported because it is using a non-PCM codec, " + "falling back to unknown channel layout\n", st->index); + return 0; + } + av_log(mxf->fc, AV_LOG_VERBOSE, "MCA mapping for stream #%d: ", st->index); + for (int j = 0; j < descriptor->channels; j++) { + int reordered_channel = av_get_channel_layout_channel_index(channel_layout, routing[j]); + av_assert0(reordered_channel >= 0 && reordered_channel < descriptor->channels); + source_track->channel_ordering[j] = reordered_channel; + av_log(mxf->fc, AV_LOG_VERBOSE, "%s%s: %d->%d", j ? ", " : "", av_get_channel_name(routing[j]), j, reordered_channel); + } + av_log(mxf->fc, AV_LOG_VERBOSE, "\n"); + source_track->require_reordering = 1; + } + + st->codecpar->channel_layout = channel_layout; + } + + return 0; +} + static int mxf_parse_structural_metadata(MXFContext *mxf) { MXFPackage *material_package = NULL; @@ -2688,6 +2974,15 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) sti->need_parsing = AVSTREAM_PARSE_FULL; } st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id); + + if (descriptor->channels <= 0 || descriptor->channels >= FF_SANE_NB_CHANNELS) { + av_log(mxf->fc, AV_LOG_ERROR, "Invalid number of channels %d, must be less than %d\n", descriptor->channels, FF_SANE_NB_CHANNELS); + return AVERROR_INVALIDDATA; + } + + ret = parse_mca_labels(mxf, source_track, descriptor, st); + if (ret < 0) + return ret; } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { enum AVMediaType type; container_ul = mxf_get_codec_ul(mxf_data_essence_container_uls, essence_container_ul); @@ -2888,6 +3183,9 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5c,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* VANC/VBI - SMPTE 436M */ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x5e,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* MPEG2AudioDescriptor */ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x64,0x00 }, mxf_read_generic_descriptor, sizeof(MXFDescriptor), Descriptor }, /* DC Timed Text Descriptor */ + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6b,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), AudioChannelLabelSubDescriptor }, + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6c,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), SoundfieldGroupLabelSubDescriptor }, + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6d,0x00 }, mxf_read_mca_sub_descriptor, sizeof(MXFMCASubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Static Track */ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_track, sizeof(MXFTrack), Track }, /* Generic Track */ { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x14,0x00 }, mxf_read_timecode_component, sizeof(MXFTimecodeComponent), TimecodeComponent }, @@ -3187,12 +3485,6 @@ static void mxf_compute_essence_containers(AVFormatContext *s) } } -static int is_pcm(enum AVCodecID codec_id) -{ - /* we only care about "normal" PCM codecs until we get samples */ - return codec_id >= AV_CODEC_ID_PCM_S16LE && codec_id < AV_CODEC_ID_PCM_S24DAUD; -} - static MXFIndexTable *mxf_find_index_table(MXFContext *mxf, int index_sid) { int i; @@ -3619,6 +3911,25 @@ static int mxf_set_pts(MXFContext *mxf, AVStream *st, AVPacket *pkt) return 0; } +static int mxf_audio_remapping(int* channel_ordering, uint8_t* data, int size, int sample_size, int channels) +{ + int sample_offset = channels * sample_size; + int number_of_samples = size / sample_offset; + uint8_t tmp[FF_SANE_NB_CHANNELS * 4]; + uint8_t* data_ptr = data; + + for (int sample = 0; sample < number_of_samples; ++sample) { + memcpy(tmp, data_ptr, sample_offset); + + for (int channel = 0; channel < channels; ++channel) { + memcpy(&data_ptr[sample_size * channel_ordering[channel]], &tmp[sample_size * channel], sample_size); + } + + data_ptr += sample_offset; + } + return 0; +} + static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) { KLVPacket klv; @@ -3733,6 +4044,15 @@ static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } + // for audio, process audio remapping if MCA label requires it + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && track->require_reordering) { + int byte_per_sample = st->codecpar->bits_per_coded_sample / 8; + ret = mxf_audio_remapping(track->channel_ordering, pkt->data, pkt->size, byte_per_sample, st->codecpar->channels); + if (ret < 0) { + return ret; + } + } + /* seek for truncated packets */ avio_seek(s->pb, klv.next_klv, SEEK_SET); @@ -3920,6 +4240,9 @@ static const AVOption options[] = { { "eia608_extract", "extract eia 608 captions from s436m track", offsetof(MXFContext, eia608_extract), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { "skip_audio_reordering", "skip audio reordering based on Multi-Channel Audio labelling", + offsetof(MXFContext, skip_audio_reordering), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, + AV_OPT_FLAG_DECODING_PARAM }, { NULL }, }; diff --git a/libavformat/version.h b/libavformat/version.h index 0705ee4112..21ca6ed096 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -33,7 +33,7 @@ // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 59 #define LIBAVFORMAT_VERSION_MINOR 9 -#define LIBAVFORMAT_VERSION_MICRO 102 +#define LIBAVFORMAT_VERSION_MICRO 103 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \