• 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 #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