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 #include "iamf/cli/channel_label.h"
13
14 #include <optional>
15 #include <string>
16 #include <vector>
17
18 #include "absl/base/no_destructor.h"
19 #include "absl/container/flat_hash_map.h"
20 #include "absl/container/flat_hash_set.h"
21 #include "absl/log/log.h"
22 #include "absl/status/status.h"
23 #include "absl/status/statusor.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/string_view.h"
26 #include "iamf/common/utils/macros.h"
27 #include "iamf/common/utils/map_utils.h"
28 #include "iamf/common/utils/validation_utils.h"
29 #include "iamf/obu/audio_element.h"
30 #include "iamf/obu/recon_gain_info_parameter_data.h"
31
32 namespace iamf_tools {
33
34 namespace {
35
36 absl::StatusOr<std::vector<ChannelLabel::Label>>
LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(const ChannelAudioLayerConfig::LoudspeakerLayout & loudspeaker_layout)37 LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
38 const ChannelAudioLayerConfig::LoudspeakerLayout& loudspeaker_layout) {
39 using enum ChannelAudioLayerConfig::LoudspeakerLayout;
40 using enum ChannelLabel::Label;
41 static const absl::NoDestructor<
42 absl::flat_hash_map<ChannelAudioLayerConfig::LoudspeakerLayout,
43 std::vector<ChannelLabel::Label>>>
44 kSoundSystemToLoudspeakerLayout({
45 {kLayoutMono, {kMono}},
46 {kLayoutStereo, {kL2, kR2}},
47 {kLayout5_1_ch, {kL5, kR5, kCentre, kLFE, kLs5, kRs5}},
48 {kLayout5_1_2_ch,
49 {kL5, kR5, kCentre, kLFE, kLs5, kRs5, kLtf2, kRtf2}},
50 {kLayout5_1_4_ch,
51 {kL5, kR5, kCentre, kLFE, kLs5, kRs5, kLtf4, kRtf4, kLtb4, kRtb4}},
52 {kLayout7_1_ch,
53 {kL7, kR7, kCentre, kLFE, kLss7, kRss7, kLrs7, kRrs7}},
54 {kLayout7_1_2_ch,
55 {kL7, kR7, kCentre, kLFE, kLss7, kRss7, kLrs7, kRrs7, kLtf2, kRtf2}},
56 {kLayout7_1_4_ch,
57 {kL7, kR7, kCentre, kLFE, kLss7, kRss7, kLrs7, kRrs7, kLtf4, kRtf4,
58 kLtb4, kRtb4}},
59 {kLayout3_1_2_ch, {kL3, kR3, kCentre, kLFE, kLtf3, kRtf3}},
60 {kLayoutBinaural, {kL2, kR2}},
61 });
62
63 return LookupInMap(*kSoundSystemToLoudspeakerLayout, loudspeaker_layout,
64 "`ChannelLabel::Label` for `LoudspeakerLayout`");
65 }
66
SetLabelsToOmittedExceptFor(const absl::flat_hash_set<ChannelLabel::Label> & labels_to_keep,std::vector<ChannelLabel::Label> & ordered_labels)67 void SetLabelsToOmittedExceptFor(
68 const absl::flat_hash_set<ChannelLabel::Label>& labels_to_keep,
69 std::vector<ChannelLabel::Label>& ordered_labels) {
70 for (auto& label : ordered_labels) {
71 if (!labels_to_keep.contains(label)) {
72 label = ChannelLabel::kOmitted;
73 }
74 }
75 }
76
77 absl::StatusOr<std::vector<ChannelLabel::Label>>
LookupEarChannelOrderFromExpandedLoudspeakerLayout(const ChannelAudioLayerConfig::ExpandedLoudspeakerLayout & expanded_loudspeaker_layout)78 LookupEarChannelOrderFromExpandedLoudspeakerLayout(
79 const ChannelAudioLayerConfig::ExpandedLoudspeakerLayout&
80 expanded_loudspeaker_layout) {
81 using enum ChannelLabel::Label;
82 static const absl::NoDestructor<std::vector<ChannelLabel::Label>>
83 k9_1_6ChannelOrder(std::vector<ChannelLabel::Label>(
84 {kFL, kFR, kFC, kLFE, kBL, kBR, kFLc, kFRc, kSiL, kSiR, kTpFL, kTpFR,
85 kTpBL, kTpBR, kTpSiL, kTpSiR}));
86 // Determine the related layout and then omit any irrelevant channels. This
87 // ensures the permitted channels are in the same slot and allows downstream
88 // processing to use the related layout's EAR matrix.
89 absl::StatusOr<std::vector<ChannelLabel::Label>> related_labels;
90 absl::flat_hash_set<ChannelLabel::Label> labels_to_keep;
91 switch (expanded_loudspeaker_layout) {
92 using enum ChannelAudioLayerConfig::ExpandedLoudspeakerLayout;
93 using enum ChannelAudioLayerConfig::LoudspeakerLayout;
94 case kExpandedLayoutLFE:
95 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
96 kLayout7_1_4_ch);
97 labels_to_keep = {kLFE};
98 break;
99 case kExpandedLayoutStereoS:
100 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
101 kLayout5_1_4_ch);
102 labels_to_keep = {kLs5, kRs5};
103 break;
104 case kExpandedLayoutStereoSS:
105 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
106 kLayout7_1_4_ch);
107 labels_to_keep = {kLss7, kRss7};
108 break;
109 case kExpandedLayoutStereoRS:
110 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
111 kLayout7_1_4_ch);
112 labels_to_keep = {kLrs7, kRrs7};
113 break;
114 case kExpandedLayoutStereoTF:
115 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
116 kLayout7_1_4_ch);
117 labels_to_keep = {kLtf4, kRtf4};
118 break;
119 case kExpandedLayoutStereoTB:
120 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
121 kLayout7_1_4_ch);
122 labels_to_keep = {kLtb4, kRtb4};
123 break;
124 case kExpandedLayoutTop4Ch:
125 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
126 kLayout7_1_4_ch);
127 labels_to_keep = {kLtf4, kRtf4, kLtb4, kRtb4};
128 break;
129 case kExpandedLayout3_0_ch:
130 related_labels = LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
131 kLayout7_1_4_ch);
132 labels_to_keep = {kL7, kR7, kCentre};
133 break;
134 case kExpandedLayout9_1_6_ch:
135 return *k9_1_6ChannelOrder;
136 case kExpandedLayoutStereoF:
137 related_labels = *k9_1_6ChannelOrder;
138 labels_to_keep = {kFL, kFR};
139 break;
140 case kExpandedLayoutStereoSi:
141 related_labels = *k9_1_6ChannelOrder;
142 labels_to_keep = {kSiL, kSiR};
143 break;
144 case kExpandedLayoutStereoTpSi:
145 related_labels = *k9_1_6ChannelOrder;
146 labels_to_keep = {kTpSiL, kTpSiR};
147 break;
148 case kExpandedLayoutTop6Ch:
149 related_labels = *k9_1_6ChannelOrder;
150 labels_to_keep = {kTpFL, kTpFR, kTpSiL, kTpSiR, kTpBL, kTpBR};
151 break;
152 default:
153 return absl::InvalidArgumentError(
154 absl::StrCat("Reserved or unknown expanded_loudspeaker_layout= ",
155 expanded_loudspeaker_layout));
156 }
157
158 // Leave the labels to keep in their original slot, but filter out all other
159 // labels.
160 if (!related_labels.ok()) {
161 return related_labels.status();
162 }
163 SetLabelsToOmittedExceptFor(labels_to_keep, *related_labels);
164 return related_labels;
165 }
166
167 } // namespace
168
169 absl::StatusOr<ChannelLabel::Label>
AmbisonicsChannelNumberToLabel(int ambisonics_channel_number)170 ChannelLabel::AmbisonicsChannelNumberToLabel(int ambisonics_channel_number) {
171 return ChannelLabel::DeprecatedStringBasedLabelToLabel(
172 absl::StrCat("A", ambisonics_channel_number));
173 }
174
175 absl::StatusOr<ChannelLabel::Label>
DeprecatedStringBasedLabelToLabel(absl::string_view label)176 ChannelLabel::DeprecatedStringBasedLabelToLabel(absl::string_view label) {
177 using enum ChannelLabel::Label;
178 static const absl::NoDestructor<
179 absl::flat_hash_map<absl::string_view, ChannelLabel::Label>>
180 kStringToChannelLabel({
181 {"Omitted", kOmitted},
182 {"M", kMono},
183 {"L2", kL2},
184 {"R2", kR2},
185 {"DemixedR2", kDemixedR2},
186 {"C", kCentre},
187 {"LFE", kLFE},
188 {"L3", kL3},
189 {"R3", kR3},
190 {"Rtf3", kRtf3},
191 {"Ltf3", kLtf3},
192 {"DemixedL3", kDemixedL3},
193 {"DemixedR3", kDemixedR3},
194 {"L5", kL5},
195 {"R5", kR5},
196 {"Ls5", kLs5},
197 {"Rs5", kRs5},
198 {"DemixedL5", kDemixedL5},
199 {"DemixedR5", kDemixedR5},
200 {"DemixedLs5", kDemixedLs5},
201 {"DemixedRs5", kDemixedRs5},
202 {"Ltf2", kLtf2},
203 {"Rtf2", kRtf2},
204 {"DemixedRtf2", kDemixedRtf2},
205 {"DemixedLtf2", kDemixedLtf2},
206 {"Ltf4", kLtf4},
207 {"Rtf4", kRtf4},
208 {"Ltb4", kLtb4},
209 {"Rtb4", kRtb4},
210 {"DemixedLtb4", kDemixedLtb4},
211 {"DemixedRtb4", kDemixedRtb4},
212 {"L7", kL7},
213 {"R7", kR7},
214 {"Lss7", kLss7},
215 {"Rss7", kRss7},
216 {"Lrs7", kLrs7},
217 {"Rrs7", kRrs7},
218 {"DemixedL7", kDemixedL7},
219 {"DemixedR7", kDemixedR7},
220 {"DemixedLrs7", kDemixedLrs7},
221 {"DemixedRrs7", kDemixedRrs7},
222 {"FLc", kFLc},
223 {"FC", kFC},
224 {"FRc", kFRc},
225 {"FL", kFL},
226 {"FR", kFR},
227 {"SiL", kSiL},
228 {"SiR", kSiR},
229 {"BL", kBL},
230 {"BR", kBR},
231 {"TpFL", kTpFL},
232 {"TpFR", kTpFR},
233 {"TpSiL", kTpSiL},
234 {"TpSiR", kTpSiR},
235 {"TpBL", kTpBL},
236 {"TpBR", kTpBR},
237 {"A0", kA0},
238 {"A1", kA1},
239 {"A2", kA2},
240 {"A3", kA3},
241 {"A4", kA4},
242 {"A5", kA5},
243 {"A6", kA6},
244 {"A7", kA7},
245 {"A8", kA8},
246 {"A9", kA9},
247 {"A10", kA10},
248 {"A11", kA11},
249 {"A12", kA12},
250 {"A13", kA13},
251 {"A14", kA14},
252 {"A15", kA15},
253 {"A16", kA16},
254 {"A17", kA17},
255 {"A18", kA18},
256 {"A19", kA19},
257 {"A20", kA20},
258 {"A21", kA21},
259 {"A22", kA22},
260 {"A23", kA23},
261 {"A24", kA24},
262 });
263
264 return LookupInMap(*kStringToChannelLabel, label,
265 "`Channel::Label` for string-based label");
266 }
267
LabelToStringForDebugging(Label label)268 std::string ChannelLabel::LabelToStringForDebugging(Label label) {
269 using enum ChannelLabel::Label;
270 switch (label) {
271 case kOmitted:
272 return "Omitted";
273 case kMono:
274 return "M";
275 case kL2:
276 return "L2";
277 case kR2:
278 return "R2";
279 case kDemixedR2:
280 return "DemixedR2";
281 case kCentre:
282 return "C";
283 case kLFE:
284 return "LFE";
285 case kL3:
286 return "L3";
287 case kR3:
288 return "R3";
289 case kRtf3:
290 return "Rtf3";
291 case kLtf3:
292 return "Ltf3";
293 case kDemixedL3:
294 return "DemixedL3";
295 case kDemixedR3:
296 return "DemixedR3";
297 case kL5:
298 return "L5";
299 case kR5:
300 return "R5";
301 case kLs5:
302 return "Ls5";
303 case kRs5:
304 return "Rs5";
305 case kDemixedL5:
306 return "DemixedL5";
307 case kDemixedR5:
308 return "DemixedR5";
309 case kDemixedLs5:
310 return "DemixedLs5";
311 case kDemixedRs5:
312 return "DemixedRs5";
313 case kLtf2:
314 return "Ltf2";
315 case kRtf2:
316 return "Rtf2";
317 case kDemixedRtf2:
318 return "DemixedRtf2";
319 case kDemixedLtf2:
320 return "DemixedLtf2";
321 case kLtf4:
322 return "Ltf4";
323 case kRtf4:
324 return "Rtf4";
325 case kLtb4:
326 return "Ltb4";
327 case kRtb4:
328 return "Rtb4";
329 case kDemixedLtb4:
330 return "DemixedLtb4";
331 case kDemixedRtb4:
332 return "DemixedRtb4";
333 case kL7:
334 return "L7";
335 case kR7:
336 return "R7";
337 case kLss7:
338 return "Lss7";
339 case kRss7:
340 return "Rss7";
341 case kLrs7:
342 return "Lrs7";
343 case kRrs7:
344 return "Rrs7";
345 case kDemixedL7:
346 return "DemixedL7";
347 case kDemixedR7:
348 return "DemixedR7";
349 case kDemixedLrs7:
350 return "DemixedLrs7";
351 case kDemixedRrs7:
352 return "DemixedRrs7";
353 case kFLc:
354 return "FLc";
355 case kFC:
356 return "FC";
357 case kFRc:
358 return "FRc";
359 case kFL:
360 return "FL";
361 case kFR:
362 return "FR";
363 case kSiL:
364 return "SiL";
365 case kSiR:
366 return "SiR";
367 case kBL:
368 return "BL";
369 case kBR:
370 return "BR";
371 case kTpFL:
372 return "TpFL";
373 case kTpFR:
374 return "TpFR";
375 case kTpSiL:
376 return "TpSiL";
377 case kTpSiR:
378 return "TpSiR";
379 case kTpBL:
380 return "TpBL";
381 case kTpBR:
382 return "TpBR";
383 case kA0:
384 return "A0";
385 case kA1:
386 return "A1";
387 case kA2:
388 return "A2";
389 case kA3:
390 return "A3";
391 case kA4:
392 return "A4";
393 case kA5:
394 return "A5";
395 case kA6:
396 return "A6";
397 case kA7:
398 return "A7";
399 case kA8:
400 return "A8";
401 case kA9:
402 return "A9";
403 case kA10:
404 return "A10";
405 case kA11:
406 return "A11";
407 case kA12:
408 return "A12";
409 case kA13:
410 return "A13";
411 case kA14:
412 return "A14";
413 case kA15:
414 return "A15";
415 case kA16:
416 return "A16";
417 case kA17:
418 return "A17";
419 case kA18:
420 return "A18";
421 case kA19:
422 return "A19";
423 case kA20:
424 return "A20";
425 case kA21:
426 return "A21";
427 case kA22:
428 return "A22";
429 case kA23:
430 return "A23";
431 case kA24:
432 return "A24";
433 }
434
435 // The above switch statement is exhaustive.
436 LOG(FATAL) << "Enum out of range.";
437 }
438
GetDemixedLabel(ChannelLabel::Label label)439 absl::StatusOr<ChannelLabel::Label> ChannelLabel::GetDemixedLabel(
440 ChannelLabel::Label label) {
441 using enum ChannelLabel::Label;
442 static const absl::NoDestructor<
443 absl::flat_hash_map<ChannelLabel::Label, ChannelLabel::Label>>
444 kChannelLabelToDemixedLabel({{kR2, kDemixedR2},
445 {kL3, kDemixedL3},
446 {kR3, kDemixedR3},
447 {kL5, kDemixedL5},
448 {kR5, kDemixedR5},
449 {kLs5, kDemixedLs5},
450 {kRs5, kDemixedRs5},
451 {kLtf2, kDemixedLtf2},
452 {kRtf2, kDemixedRtf2},
453 {kLtb4, kDemixedLtb4},
454 {kRtb4, kDemixedRtb4},
455 {kL7, kDemixedL7},
456 {kR7, kDemixedR7},
457 {kLrs7, kDemixedLrs7},
458 {kRrs7, kDemixedRrs7}});
459 return LookupInMap(*kChannelLabelToDemixedLabel, label,
460 "Demixed label for `ChannelLabel::Label`");
461 }
462
463 absl::StatusOr<std::vector<ChannelLabel::Label>>
LookupEarChannelOrderFromScalableLoudspeakerLayout(ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,const std::optional<ChannelAudioLayerConfig::ExpandedLoudspeakerLayout> & expanded_loudspeaker_layout)464 ChannelLabel::LookupEarChannelOrderFromScalableLoudspeakerLayout(
465 ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,
466 const std::optional<ChannelAudioLayerConfig::ExpandedLoudspeakerLayout>&
467 expanded_loudspeaker_layout) {
468 if (loudspeaker_layout == ChannelAudioLayerConfig::kLayoutExpanded) {
469 RETURN_IF_NOT_OK(ValidateHasValue(expanded_loudspeaker_layout,
470 "expanded_loudspeaker_layout"));
471 return LookupEarChannelOrderFromExpandedLoudspeakerLayout(
472 *expanded_loudspeaker_layout);
473 } else {
474 return LookupEarChannelOrderFromNonExpandedLoudspeakerLayout(
475 loudspeaker_layout);
476 }
477 }
478
479 absl::StatusOr<absl::flat_hash_set<ChannelLabel::Label>>
LookupLabelsToReconstructFromScalableLoudspeakerLayout(ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,const std::optional<ChannelAudioLayerConfig::ExpandedLoudspeakerLayout> & expanded_loudspeaker_layout)480 ChannelLabel::LookupLabelsToReconstructFromScalableLoudspeakerLayout(
481 ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,
482 const std::optional<ChannelAudioLayerConfig::ExpandedLoudspeakerLayout>&
483 expanded_loudspeaker_layout) {
484 if (loudspeaker_layout == ChannelAudioLayerConfig::kLayoutExpanded) {
485 RETURN_IF_NOT_OK(ValidateHasValue(expanded_loudspeaker_layout,
486 "expanded_loudspeaker_layout"));
487 // OK. Expanded layouts may only exist in a single-layer and thus never need
488 // to be reconstructed as of IAMF v1.1.0.
489 return absl::flat_hash_set<ChannelLabel::Label>{};
490 }
491 // Reconstruct the highest layer.
492 const auto ordered_labels =
493 ChannelLabel::LookupEarChannelOrderFromScalableLoudspeakerLayout(
494 loudspeaker_layout, expanded_loudspeaker_layout);
495 if (!ordered_labels.ok()) {
496 return ordered_labels.status();
497 } else {
498 return absl::flat_hash_set<ChannelLabel::Label>(ordered_labels->begin(),
499 ordered_labels->end());
500 }
501 }
502
503 absl::StatusOr<ChannelLabel::Label>
GetDemixedChannelLabelForReconGain(const ChannelAudioLayerConfig::LoudspeakerLayout & layout,const ReconGainElement::ReconGainFlagBitmask & flag)504 ChannelLabel::GetDemixedChannelLabelForReconGain(
505 const ChannelAudioLayerConfig::LoudspeakerLayout& layout,
506 const ReconGainElement::ReconGainFlagBitmask& flag) {
507 switch (flag) {
508 using enum ReconGainElement::ReconGainFlagBitmask;
509 using enum ChannelLabel::Label;
510 using enum ChannelAudioLayerConfig::LoudspeakerLayout;
511 case kReconGainFlagL:
512 if (layout == kLayout5_1_ch || layout == kLayout5_1_2_ch ||
513 layout == kLayout5_1_4_ch) {
514 return kDemixedL5;
515 } else if (layout == kLayout7_1_ch || layout == kLayout7_1_2_ch ||
516 layout == kLayout7_1_4_ch) {
517 return kDemixedL7;
518 } else if (layout == kLayout3_1_2_ch) {
519 return kDemixedL3;
520 } else {
521 LOG(WARNING)
522 << "Unexpected recon gain flag. No corresponding channel label.";
523 return absl::InvalidArgumentError("Unexpected recon gain flag.");
524 }
525 case kReconGainFlagC:
526 LOG(WARNING)
527 << "Unexpected recon gain flag. No corresponding channel label.";
528 return absl::InvalidArgumentError("Unexpected recon gain flag.");
529 case kReconGainFlagR:
530 if (layout == kLayoutStereo) {
531 return kDemixedR2;
532 } else if (layout == kLayout5_1_ch || layout == kLayout5_1_2_ch ||
533 layout == kLayout5_1_4_ch) {
534 return kDemixedR5;
535 } else if (layout == kLayout7_1_ch || layout == kLayout7_1_2_ch ||
536 layout == kLayout7_1_4_ch) {
537 return kDemixedR7;
538 } else if (layout == kLayout3_1_2_ch) {
539 return kDemixedR3;
540 } else {
541 LOG(WARNING)
542 << "Unexpected recon gain flag. No corresponding channel label.";
543 return absl::InvalidArgumentError("Unexpected recon gain flag.");
544 }
545 case kReconGainFlagLss:
546 return kDemixedLs5;
547 case kReconGainFlagRss:
548 return kDemixedRs5;
549 case kReconGainFlagLtf:
550 return kDemixedLtf2;
551 case kReconGainFlagRtf:
552 return kDemixedRtf2;
553 case kReconGainFlagLrs:
554 return kDemixedLrs7;
555 case kReconGainFlagRrs:
556 return kDemixedRrs7;
557 case kReconGainFlagLtb:
558 return kDemixedLtb4;
559 case kReconGainFlagRtb:
560 return kDemixedRtb4;
561 case kReconGainFlagLfe:
562 default:
563 LOG(WARNING)
564 << "Unexpected recon gain flag. No corresponding channel label.";
565 return absl::InvalidArgumentError("Unexpected recon gain flag.");
566 }
567 }
568
569 } // namespace iamf_tools
570