• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "iamf/cli/user_metadata_builder/audio_element_metadata_builder.h"
14 
15 #include <cstdint>
16 
17 #include "absl/base/no_destructor.h"
18 #include "absl/container/flat_hash_map.h"
19 #include "absl/status/status.h"
20 #include "absl/status/statusor.h"
21 #include "absl/strings/str_cat.h"
22 #include "iamf/cli/proto/audio_element.pb.h"
23 #include "iamf/cli/proto/user_metadata.pb.h"
24 #include "iamf/cli/user_metadata_builder/iamf_input_layout.h"
25 #include "iamf/common/utils/map_utils.h"
26 
27 namespace iamf_tools {
28 
29 namespace {
30 
LookupNumSubstreamsFromInputLayout(IamfInputLayout input_layout)31 absl::StatusOr<int32_t> LookupNumSubstreamsFromInputLayout(
32     IamfInputLayout input_layout) {
33   // Map which holds the loudspeaker layout and the count of substream(s)
34   // corresponding to it.
35   using enum IamfInputLayout;
36   static const absl::NoDestructor<absl::flat_hash_map<IamfInputLayout, int32_t>>
37       kInputLayoutToNumSubstreams({
38           {kMono, 1},
39           {kStereo, 1},
40           {k5_1, 4},
41           {k5_1_2, 5},
42           {k5_1_4, 6},
43           {k5_1_4, 6},
44           {k7_1, 5},
45           {k7_1_4, 7},
46           {kBinaural, 1},
47           {kLFE, 1},
48           {kAmbisonicsOrder1, 4},
49           {kAmbisonicsOrder2, 9},
50           {kAmbisonicsOrder3, 16},
51       });
52 
53   return LookupInMap(*kInputLayoutToNumSubstreams, input_layout,
54                      "Number of substreams for `IamfInputLayout`");
55 }
56 
LookupCoupledSubstreamCountFromInputLayout(IamfInputLayout input_layout)57 absl::StatusOr<int32_t> LookupCoupledSubstreamCountFromInputLayout(
58     IamfInputLayout input_layout) {
59   // Map which holds the loudspeaker layout and the count of coupled
60   // substream(s) corresponding to it.
61   using enum IamfInputLayout;
62   static const absl::NoDestructor<absl::flat_hash_map<IamfInputLayout, int32_t>>
63       kInputLayoutToCoupledSubstreamCount({
64           {kMono, 0},
65           {kStereo, 1},
66           {k5_1, 2},
67           {k5_1_2, 3},
68           {k5_1_4, 4},
69           {k7_1, 3},
70           {k7_1_4, 5},
71           {kBinaural, 1},
72           {kLFE, 0},
73       });
74 
75   return LookupInMap(*kInputLayoutToCoupledSubstreamCount, input_layout,
76                      "Coupled substream count for `IamfInputLayout`");
77 }
78 
79 absl::StatusOr<iamf_tools_cli_proto::LoudspeakerLayout>
LookupLoudspeakerLayoutFromInputLayout(IamfInputLayout input_layout)80 LookupLoudspeakerLayoutFromInputLayout(IamfInputLayout input_layout) {
81   using enum IamfInputLayout;
82   using enum iamf_tools_cli_proto::LoudspeakerLayout;
83 
84   // Map which holds the channel layout and the corresponding loudspeaker layout
85   // in IAMF.
86   static const absl::NoDestructor<absl::flat_hash_map<
87       IamfInputLayout, iamf_tools_cli_proto::LoudspeakerLayout>>
88       KInputLayoutToLoudspeakerLayout({
89           {kMono, LOUDSPEAKER_LAYOUT_MONO},
90           {kStereo, LOUDSPEAKER_LAYOUT_STEREO},
91           {k5_1, LOUDSPEAKER_LAYOUT_5_1_CH},
92           {k5_1_2, LOUDSPEAKER_LAYOUT_5_1_2_CH},
93           {k5_1_4, LOUDSPEAKER_LAYOUT_5_1_4_CH},
94           {k7_1, LOUDSPEAKER_LAYOUT_7_1_CH},
95           {k7_1_4, LOUDSPEAKER_LAYOUT_7_1_4_CH},
96           {kBinaural, LOUDSPEAKER_LAYOUT_BINAURAL},
97           {kLFE, LOUDSPEAKER_LAYOUT_EXPANDED},
98       });
99 
100   return LookupInMap(*KInputLayoutToLoudspeakerLayout, input_layout,
101                      "Proto `LoudspeakerLayout` for `IamfInputLayout`");
102 }
103 
104 absl::StatusOr<iamf_tools_cli_proto::ExpandedLoudspeakerLayout>
LookupExpandedLoudspeakerLayoutFromInputLayout(IamfInputLayout input_layout)105 LookupExpandedLoudspeakerLayoutFromInputLayout(IamfInputLayout input_layout) {
106   using enum IamfInputLayout;
107   using enum iamf_tools_cli_proto::ExpandedLoudspeakerLayout;
108 
109   // Map which holds the channel layout and the corresponding expanded
110   // loudspeaker layout in IAMF.
111   static const absl::NoDestructor<absl::flat_hash_map<
112       IamfInputLayout, iamf_tools_cli_proto::ExpandedLoudspeakerLayout>>
113       KInputLayoutToExpandedLoudspeakerLayout({
114           {kLFE, EXPANDED_LOUDSPEAKER_LAYOUT_LFE},
115       });
116 
117   return LookupInMap(*KInputLayoutToExpandedLoudspeakerLayout, input_layout,
118                      "Proto `ExpandedLoudspeakerLayout` for `IamfInputLayout`");
119 }
120 
121 absl::StatusOr<iamf_tools_cli_proto::AudioElementType>
LookupAudioElementTypeFromInputLayout(IamfInputLayout input_layout)122 LookupAudioElementTypeFromInputLayout(IamfInputLayout input_layout) {
123   using enum IamfInputLayout;
124   using enum iamf_tools_cli_proto::AudioElementType;
125 
126   // Map which holds the channel layout and the corresponding audio element
127   // type.
128   static const absl::NoDestructor<absl::flat_hash_map<
129       IamfInputLayout, iamf_tools_cli_proto::AudioElementType>>
130       KInputLayoutToAudioElementType({
131           {kMono, AUDIO_ELEMENT_CHANNEL_BASED},
132           {kStereo, AUDIO_ELEMENT_CHANNEL_BASED},
133           {k5_1, AUDIO_ELEMENT_CHANNEL_BASED},
134           {k5_1_2, AUDIO_ELEMENT_CHANNEL_BASED},
135           {k5_1_4, AUDIO_ELEMENT_CHANNEL_BASED},
136           {k7_1, AUDIO_ELEMENT_CHANNEL_BASED},
137           {k7_1_4, AUDIO_ELEMENT_CHANNEL_BASED},
138           {kBinaural, AUDIO_ELEMENT_CHANNEL_BASED},
139           {kLFE, AUDIO_ELEMENT_CHANNEL_BASED},
140           {kAmbisonicsOrder1, AUDIO_ELEMENT_SCENE_BASED},
141           {kAmbisonicsOrder2, AUDIO_ELEMENT_SCENE_BASED},
142           {kAmbisonicsOrder3, AUDIO_ELEMENT_SCENE_BASED},
143       });
144 
145   return LookupInMap(*KInputLayoutToAudioElementType, input_layout,
146                      "Proto `AudioElementType` for `IamfInputLayout`");
147 }
148 
PopulateChannelBasedAudioElementMetadata(IamfInputLayout input_layout,int32_t num_substreams,iamf_tools_cli_proto::ScalableChannelLayoutConfig & scalable_channel_layout_config)149 absl::Status PopulateChannelBasedAudioElementMetadata(
150     IamfInputLayout input_layout, int32_t num_substreams,
151     iamf_tools_cli_proto::ScalableChannelLayoutConfig&
152         scalable_channel_layout_config) {
153   // Simplistically choose one layer. This most closely matches other popular
154   // formats (e.g. ADM).
155   scalable_channel_layout_config.set_num_layers(1);
156 
157   auto* channel_audio_layer_config =
158       scalable_channel_layout_config.add_channel_audio_layer_configs();
159 
160   const auto loudspeaker_layout =
161       LookupLoudspeakerLayoutFromInputLayout(input_layout);
162   if (!loudspeaker_layout.ok()) {
163     return loudspeaker_layout.status();
164   }
165   channel_audio_layer_config->set_loudspeaker_layout(*loudspeaker_layout);
166 
167   // Set 'output_gain_is_present_flag' and 'recon_gain_is_present_flag' to agree
168   // with the single-layer assumption.
169   channel_audio_layer_config->set_output_gain_is_present_flag(0);
170   channel_audio_layer_config->set_recon_gain_is_present_flag(0);
171 
172   // As 'num_layers' is set to 1, 'substream_count' is equal to
173   // 'num_substreams'.
174   channel_audio_layer_config->set_substream_count(num_substreams);
175   const auto coupled_substream_count =
176       LookupCoupledSubstreamCountFromInputLayout(input_layout);
177   if (!coupled_substream_count.ok()) {
178     return coupled_substream_count.status();
179   }
180   channel_audio_layer_config->set_coupled_substream_count(
181       *coupled_substream_count);
182 
183   // Set the specific 'expanded_loudspeaker_layout' field when it is relevant
184   // (e.g. LFE).
185   if (channel_audio_layer_config->loudspeaker_layout() ==
186       iamf_tools_cli_proto::LOUDSPEAKER_LAYOUT_EXPANDED) {
187     const auto expanded_loudspeaker_layout =
188         LookupExpandedLoudspeakerLayoutFromInputLayout(input_layout);
189     if (!expanded_loudspeaker_layout.ok()) {
190       return expanded_loudspeaker_layout.status();
191     }
192     channel_audio_layer_config->set_expanded_loudspeaker_layout(
193         *expanded_loudspeaker_layout);
194   }
195 
196   return absl::OkStatus();
197 }
198 
PopulateSceneBasedAudioElementMetadata(int32_t num_substreams,iamf_tools_cli_proto::AudioElementObuMetadata & audio_element_obu_metadata)199 void PopulateSceneBasedAudioElementMetadata(
200     int32_t num_substreams,
201     iamf_tools_cli_proto::AudioElementObuMetadata& audio_element_obu_metadata) {
202   audio_element_obu_metadata.set_audio_element_type(
203       iamf_tools_cli_proto::AUDIO_ELEMENT_SCENE_BASED);
204 
205   auto* ambisonics_config =
206       audio_element_obu_metadata.mutable_ambisonics_config();
207   // For typeDefinition = HOA and since input contains LPCM audio samples, set
208   // ambisonics_mode to AMBISONICS_MODE_MONO.
209   ambisonics_config->set_ambisonics_mode(
210       iamf_tools_cli_proto::AMBISONICS_MODE_MONO);
211 
212   auto* ambisonics_mono_config =
213       ambisonics_config->mutable_ambisonics_mono_config();
214 
215   ambisonics_mono_config->set_output_channel_count(num_substreams);
216   ambisonics_mono_config->set_substream_count(num_substreams);
217 
218   for (int32_t substream_id = 0; substream_id < num_substreams;
219        ++substream_id) {
220     ambisonics_mono_config->mutable_channel_mapping()->Add(substream_id);
221   }
222 }
223 
224 }  // namespace
225 
226 // Sets the required textproto fields for audio_element_metadata.
PopulateAudioElementMetadata(uint32_t audio_element_id,uint32_t codec_config_id,const IamfInputLayout input_layout,iamf_tools_cli_proto::AudioElementObuMetadata & audio_element_obu_metadata)227 absl::Status AudioElementMetadataBuilder::PopulateAudioElementMetadata(
228     uint32_t audio_element_id, uint32_t codec_config_id,
229     const IamfInputLayout input_layout,
230     iamf_tools_cli_proto::AudioElementObuMetadata& audio_element_obu_metadata) {
231   audio_element_obu_metadata.set_audio_element_id(audio_element_id);
232   audio_element_obu_metadata.set_codec_config_id(codec_config_id);
233 
234   const auto num_substreams = LookupNumSubstreamsFromInputLayout(input_layout);
235   if (!num_substreams.ok()) {
236     return num_substreams.status();
237   }
238   audio_element_obu_metadata.set_num_substreams(*num_substreams);
239 
240   // Generate sequential substream IDs. Although not REQUIRED by IAMF this helps
241   // ensure that the substream IDs are unique between subsequent calls to this
242   // function.
243   for (int i = 0; i < *num_substreams; ++i) {
244     audio_element_obu_metadata.mutable_audio_substream_ids()->Add(
245         audio_stream_id_counter_);
246     ++audio_stream_id_counter_;
247   }
248 
249   // Simplistically set 'num_parameters' to zero.
250   audio_element_obu_metadata.set_num_parameters(0);
251 
252   const auto audio_element_type =
253       LookupAudioElementTypeFromInputLayout(input_layout);
254   if (!audio_element_type.ok()) {
255     return audio_element_type.status();
256   }
257   audio_element_obu_metadata.set_audio_element_type(*audio_element_type);
258 
259   switch (*audio_element_type) {
260     using enum iamf_tools_cli_proto::AudioElementType;
261     case AUDIO_ELEMENT_CHANNEL_BASED:
262       return PopulateChannelBasedAudioElementMetadata(
263           input_layout, *num_substreams,
264           *audio_element_obu_metadata.mutable_scalable_channel_layout_config());
265     case AUDIO_ELEMENT_SCENE_BASED:
266       PopulateSceneBasedAudioElementMetadata(*num_substreams,
267                                              audio_element_obu_metadata);
268       return absl::OkStatus();
269     default:
270       return absl::InvalidArgumentError(absl::StrCat(
271           "Unsupported audio_element_type= ", *audio_element_type));
272   }
273 }
274 
275 }  // namespace iamf_tools
276