• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright (c) 2024, Alliance for Open Media. All rights reserved
4  *
5  * This source code is subject to the terms of the BSD 3-Clause Clear License
6  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
7  * License was not distributed with this source code in the LICENSE file, you
8  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
9  * Alliance for Open Media Patent License 1.0 was not distributed with this
10  * source code in the PATENTS file, you can obtain it at
11  * www.aomedia.org/license/patent.
12  */
13 
14 #include "iamf/cli/renderer/audio_element_renderer_passthrough.h"
15 
16 #include <cstddef>
17 #include <memory>
18 #include <vector>
19 
20 #include "absl/base/no_destructor.h"
21 #include "absl/container/flat_hash_map.h"
22 #include "absl/functional/any_invocable.h"
23 #include "absl/log/log.h"
24 #include "absl/memory/memory.h"
25 #include "absl/status/status.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/types/span.h"
29 #include "iamf/cli/channel_label.h"
30 #include "iamf/common/utils/macros.h"
31 #include "iamf/common/utils/map_utils.h"
32 #include "iamf/common/utils/sample_processing_utils.h"
33 #include "iamf/common/utils/validation_utils.h"
34 #include "iamf/obu/audio_element.h"
35 #include "iamf/obu/mix_presentation.h"
36 #include "iamf/obu/types.h"
37 
38 namespace iamf_tools {
39 namespace {
40 
41 using enum LoudspeakersSsConventionLayout::SoundSystem;
42 using enum ChannelAudioLayerConfig::LoudspeakerLayout;
43 using enum ChannelAudioLayerConfig::ExpandedLoudspeakerLayout;
44 
IsLoudspeakerLayoutEquivalentToSoundSystem(ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,LoudspeakersSsConventionLayout::SoundSystem layout)45 absl::StatusOr<bool> IsLoudspeakerLayoutEquivalentToSoundSystem(
46     ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,
47     LoudspeakersSsConventionLayout::SoundSystem layout) {
48   static const absl::NoDestructor<
49       absl::flat_hash_map<LoudspeakersSsConventionLayout::SoundSystem,
50                           ChannelAudioLayerConfig::LoudspeakerLayout>>
51       kSoundSystemToLoudspeakerLayout({
52           {kSoundSystem12_0_1_0, kLayoutMono},
53           {kSoundSystemA_0_2_0, kLayoutStereo},
54           {kSoundSystemB_0_5_0, kLayout5_1_ch},
55           {kSoundSystemC_2_5_0, kLayout5_1_2_ch},
56           {kSoundSystemD_4_5_0, kLayout5_1_4_ch},
57           {kSoundSystemI_0_7_0, kLayout7_1_ch},
58           {kSoundSystem10_2_7_0, kLayout7_1_2_ch},
59           {kSoundSystemJ_4_7_0, kLayout7_1_4_ch},
60           {kSoundSystem11_2_3_0, kLayout3_1_2_ch},
61       });
62 
63   ChannelAudioLayerConfig::LoudspeakerLayout equivalent_loudspeaker_layout;
64   RETURN_IF_NOT_OK(
65       CopyFromMap(*kSoundSystemToLoudspeakerLayout, layout,
66                   "`LoudspeakerLayout` equivalent to `SoundSystem`",
67                   equivalent_loudspeaker_layout));
68 
69   return equivalent_loudspeaker_layout == loudspeaker_layout;
70 }
71 
72 // Several expanded layouts are defined as being based on a particular sound
73 // system. The passthrough renderer can be used with the associated sound system
74 // if the expanded layout is a based on the sound system. Other channels can be
75 // omitted.
IsExpandedLoudspeakerLayoutBasedOnSoundSystem(ChannelAudioLayerConfig::ExpandedLoudspeakerLayout expanded_loudspeaker_layout,LoudspeakersSsConventionLayout::SoundSystem layout)76 absl::StatusOr<bool> IsExpandedLoudspeakerLayoutBasedOnSoundSystem(
77     ChannelAudioLayerConfig::ExpandedLoudspeakerLayout
78         expanded_loudspeaker_layout,
79     LoudspeakersSsConventionLayout::SoundSystem layout) {
80   switch (expanded_loudspeaker_layout) {
81     case kExpandedLayoutStereoS:
82       return layout == kSoundSystemD_4_5_0;
83     case kExpandedLayoutLFE:
84     case kExpandedLayoutStereoSS:
85     case kExpandedLayoutStereoRS:
86     case kExpandedLayoutStereoTF:
87     case kExpandedLayoutStereoTB:
88     case kExpandedLayoutTop4Ch:
89     case kExpandedLayout3_0_ch:
90       return layout == kSoundSystemJ_4_7_0;
91     case kExpandedLayout9_1_6_ch:
92     case kExpandedLayoutStereoF:
93     case kExpandedLayoutStereoSi:
94     case kExpandedLayoutStereoTpSi:
95     case kExpandedLayoutTop6Ch:
96       return layout == kSoundSystem13_6_9_0;
97     case kExpandedLayoutReserved13:
98     case kExpandedLayoutReserved255:
99     default:
100       return absl::InvalidArgumentError(absl::StrCat(
101           "Unknown expanded layout cannot be used for pass-through: ",
102           expanded_loudspeaker_layout));
103   }
104 }
105 
CanChannelAudioLayerConfigPassThroguhToLayout(const ChannelAudioLayerConfig & channel_config,const Layout & layout)106 absl::StatusOr<bool> CanChannelAudioLayerConfigPassThroguhToLayout(
107     const ChannelAudioLayerConfig& channel_config, const Layout& layout) {
108   switch (layout.layout_type) {
109     case Layout::kLayoutTypeLoudspeakersSsConvention:
110       // Pass-through the associated demixed layer.
111       if (channel_config.loudspeaker_layout == kLayoutExpanded) {
112         RETURN_IF_NOT_OK(
113             ValidateHasValue(channel_config.expanded_loudspeaker_layout,
114                              "expanded_loudspeaker_layout"));
115         return IsExpandedLoudspeakerLayoutBasedOnSoundSystem(
116             *channel_config.expanded_loudspeaker_layout,
117             std::get<LoudspeakersSsConventionLayout>(layout.specific_layout)
118                 .sound_system);
119       } else {
120         return IsLoudspeakerLayoutEquivalentToSoundSystem(
121             channel_config.loudspeaker_layout,
122             std::get<LoudspeakersSsConventionLayout>(layout.specific_layout)
123                 .sound_system);
124       }
125     case Layout::kLayoutTypeBinaural:
126       // Pass-through binaural.
127       return channel_config.loudspeaker_layout == kLayoutBinaural;
128     default:
129       return absl::InvalidArgumentError(
130           absl::StrCat("Unknown layout_type= ", layout.layout_type));
131   }
132 }
133 
134 // Finds the layer with the equivalent loudspeaker layout if present.
FindEquivalentLayer(const ScalableChannelLayoutConfig & scalable_channel_layout_config,const Layout & layout)135 absl::StatusOr<ChannelAudioLayerConfig> FindEquivalentLayer(
136     const ScalableChannelLayoutConfig& scalable_channel_layout_config,
137     const Layout& layout) {
138   for (const auto& channel_audio_layer_config :
139        scalable_channel_layout_config.channel_audio_layer_configs) {
140     const auto can_pass_through = CanChannelAudioLayerConfigPassThroguhToLayout(
141         channel_audio_layer_config, layout);
142     if (!can_pass_through.ok()) {
143       return can_pass_through.status();
144     } else if (*can_pass_through) {
145       return channel_audio_layer_config;
146     } else {
147       // Search in the remaining layers.
148       continue;
149     }
150   }
151 
152   return absl::InvalidArgumentError(
153       "No equivalent layers found for the requested layout. The passthrough "
154       "render is not suitable here. Down-mixing may be required.");
155 }
156 
157 }  // namespace
158 
159 std::unique_ptr<AudioElementRendererPassThrough>
CreateFromScalableChannelLayoutConfig(const ScalableChannelLayoutConfig & scalable_channel_layout_config,const Layout & playback_layout,size_t num_samples_per_frame)160 AudioElementRendererPassThrough::CreateFromScalableChannelLayoutConfig(
161     const ScalableChannelLayoutConfig& scalable_channel_layout_config,
162     const Layout& playback_layout, size_t num_samples_per_frame) {
163   const auto& equivalent_layer =
164       FindEquivalentLayer(scalable_channel_layout_config, playback_layout);
165   if (!equivalent_layer.ok()) {
166     return nullptr;
167   }
168   const auto& ordered_labels =
169       ChannelLabel::LookupEarChannelOrderFromScalableLoudspeakerLayout(
170           equivalent_layer->loudspeaker_layout,
171           equivalent_layer->expanded_loudspeaker_layout);
172   if (!ordered_labels.ok()) {
173     return nullptr;
174   }
175 
176   return absl::WrapUnique(new AudioElementRendererPassThrough(
177       *ordered_labels, num_samples_per_frame));
178 }
179 
RenderSamples(absl::Span<const std::vector<InternalSampleType>> samples_to_render,std::vector<InternalSampleType> & rendered_samples)180 absl::Status AudioElementRendererPassThrough::RenderSamples(
181     absl::Span<const std::vector<InternalSampleType>> samples_to_render,
182     std::vector<InternalSampleType>& rendered_samples) {
183   // Flatten the (time, channel) axes into interleaved samples.
184   const absl::AnyInvocable<absl::Status(InternalSampleType, InternalSampleType&)
185                                const>
186       kIdentityTransform =
187           [](InternalSampleType input, InternalSampleType& output) {
188             output = input;
189             return absl::OkStatus();
190           };
191 
192   return ConvertTimeChannelToInterleaved(samples_to_render, kIdentityTransform,
193                                          rendered_samples);
194 }
195 
196 }  // namespace iamf_tools
197