1 /*
2 * Copyright (c) 2024, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 3-Clause Clear License
5 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6 * License was not distributed with this source code in the LICENSE file, you
7 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8 * Alliance for Open Media Patent License 1.0 was not distributed with this
9 * source code in the PATENTS file, you can obtain it at
10 * www.aomedia.org/license/patent.
11 */
12
13 #include <algorithm>
14 #include <cstdint>
15 #include <filesystem>
16 #include <istream>
17
18 #include "absl/log/log.h"
19 #include "absl/status/status.h"
20 #include "absl/status/statusor.h"
21 #include "absl/strings/string_view.h"
22 #include "iamf/cli/adm_to_user_metadata/adm/adm_elements.h"
23 #include "iamf/cli/adm_to_user_metadata/adm/bw64_reader.h"
24 #include "iamf/cli/adm_to_user_metadata/adm/wav_file_splicer.h"
25 #include "iamf/cli/adm_to_user_metadata/iamf/user_metadata_generator.h"
26 #include "iamf/common/utils/macros.h"
27 #include "iamf/obu/ia_sequence_header.h"
28
29 namespace iamf_tools {
30 namespace adm_to_user_metadata {
31
32 namespace {
33
34 // ADM audioPackFormatID corresponding to 3rd order ambisonics, in which "0004"
35 // denotes type definition Ambisonics and "0003" denotes order 3.
36 constexpr char kAudioPackFormatIdFor3OA[] = "AP_00040003";
37 // A dummy audioPackFormatID created to represent typeDefinition as
38 // DirectSpeakers (0001) and layout as LFE (1FFF).
39 constexpr char kAudioPackFormatIdForLfe[] = "AP_00011FFF";
40
ModifyAdmToPanObjectsTo3OAAndSeparateLfe(const ProfileVersion profile_version,const int lfe_count,ADM & adm_metadata)41 void ModifyAdmToPanObjectsTo3OAAndSeparateLfe(
42 const ProfileVersion profile_version, const int lfe_count,
43 ADM& adm_metadata) {
44 using enum ProfileVersion;
45 if (profile_version == kIamfBaseProfile) {
46 // For IA Base Profile, max channels allowed per mix is 18, hence pan all
47 // audio objects(both channel beds and objects) to 3OA (16 channels).
48 adm_metadata.audio_objects.erase(adm_metadata.audio_objects.begin() + 1,
49 adm_metadata.audio_objects.end());
50 adm_metadata.audio_objects[0].audio_pack_format_id_refs[0] =
51 kAudioPackFormatIdFor3OA;
52 } else if (profile_version == kIamfBaseEnhancedProfile) {
53 // For IA Base Enhanced Profile, max channels allowed per mix is 28, hence
54 // pan all non-LFE channels(both channel beds and objects) in the to 3OA (16
55 // channels) and keep the LFE(s) as separate audio element(s).
56 adm_metadata.audio_objects.erase(
57 adm_metadata.audio_objects.begin() + 1 + lfe_count,
58 adm_metadata.audio_objects.end());
59 adm_metadata.audio_objects[0].audio_pack_format_id_refs[0] =
60 kAudioPackFormatIdFor3OA;
61 for (int lfe_index = 1; lfe_index <= lfe_count; ++lfe_index) {
62 adm_metadata.audio_objects[lfe_index].audio_pack_format_id_refs[0] =
63 kAudioPackFormatIdForLfe;
64 }
65 }
66 }
67
68 } // namespace
69
70 absl::StatusOr<iamf_tools_cli_proto::UserMetadata>
GenerateUserMetadataAndSpliceWavFiles(absl::string_view file_prefix,int32_t frame_duration_ms,int32_t input_importance_threshold,const std::filesystem::path & output_path,std::istream & adm_file,const iamf_tools::ProfileVersion profile_version)71 GenerateUserMetadataAndSpliceWavFiles(
72 absl::string_view file_prefix, int32_t frame_duration_ms,
73 int32_t input_importance_threshold,
74 const std::filesystem::path& output_path, std::istream& adm_file,
75 const iamf_tools::ProfileVersion profile_version) {
76 // Parse the input ADM BWF file.
77 const auto& reader =
78 iamf_tools::adm_to_user_metadata::Bw64Reader::BuildFromStream(
79 std::clamp(input_importance_threshold, 0, 10), adm_file);
80 if (!reader.ok()) {
81 return reader.status();
82 }
83
84 int lfe_count = 0;
85 // Write output ".wav" file(s).
86 RETURN_IF_NOT_OK(iamf_tools::adm_to_user_metadata::SpliceWavFilesFromAdm(
87 output_path, file_prefix, profile_version, *reader, adm_file, lfe_count));
88
89 ADM adm_metadata = reader->adm_;
90
91 if (reader->adm_.file_type == kAdmFileTypeDolby) {
92 ModifyAdmToPanObjectsTo3OAAndSeparateLfe(profile_version, lfe_count,
93 adm_metadata);
94 }
95
96 // Generate the user metadata.
97 const auto& user_metadata_generator =
98 iamf_tools::adm_to_user_metadata::UserMetadataGenerator(
99 adm_metadata, reader->format_info_, frame_duration_ms);
100
101 using enum iamf_tools_cli_proto::ProfileVersion;
102 using enum iamf_tools::ProfileVersion;
103 iamf_tools_cli_proto::ProfileVersion version_for_proto;
104 if (profile_version == kIamfBaseProfile) {
105 version_for_proto = PROFILE_VERSION_BASE;
106 } else if (profile_version == kIamfBaseEnhancedProfile) {
107 version_for_proto = PROFILE_VERSION_BASE_ENHANCED;
108 } else {
109 return absl::InvalidArgumentError("Invalid profile version for proto.");
110 }
111
112 return user_metadata_generator.GenerateUserMetadata(version_for_proto,
113 file_prefix);
114 }
115
116 } // namespace adm_to_user_metadata
117 } // namespace iamf_tools
118