• 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 <cstddef>
13 #include <optional>
14 #include <string>
15 #include <vector>
16 
17 #include "absl/base/no_destructor.h"
18 #include "absl/container/flat_hash_map.h"
19 #include "absl/log/check.h"
20 #include "absl/status/status.h"
21 #include "absl/status/statusor.h"
22 #include "absl/strings/str_cat.h"
23 #include "iamf/cli/channel_label.h"
24 #include "iamf/cli/demixing_module.h"
25 #include "iamf/common/utils/macros.h"
26 #include "iamf/common/utils/map_utils.h"
27 #include "iamf/obu/mix_presentation.h"
28 #include "iamf/obu/types.h"
29 
30 namespace iamf_tools {
31 
32 namespace renderer_utils {
33 
34 namespace {
35 
36 // Returns the common number of time ticks to be rendered for the requested
37 // labels or associated demixed label in `labeled_frame`. This represents the
38 // number of time ticks in the rendered audio after trimming.
GetCommonNumTrimmedTimeTicks(const LabeledFrame & labeled_frame,const std::vector<ChannelLabel::Label> & ordered_labels)39 absl::StatusOr<size_t> GetCommonNumTrimmedTimeTicks(
40     const LabeledFrame& labeled_frame,
41     const std::vector<ChannelLabel::Label>& ordered_labels) {
42   std::optional<size_t> num_raw_time_ticks;
43   for (const auto& label : ordered_labels) {
44     if (label == ChannelLabel::kOmitted) {
45       continue;
46     }
47 
48     const std::vector<InternalSampleType>* samples_to_render = nullptr;
49     RETURN_IF_NOT_OK(DemixingModule::FindSamplesOrDemixedSamples(
50         label, labeled_frame.label_to_samples, &samples_to_render));
51 
52     if (samples_to_render == nullptr) {
53       return absl::InvalidArgumentError(
54           absl::StrCat("Label ", label, " or D_", label, " not found."));
55     } else if (!num_raw_time_ticks.has_value()) {
56       num_raw_time_ticks = samples_to_render->size();
57     } else if (*num_raw_time_ticks != samples_to_render->size()) {
58       return absl::InvalidArgumentError(absl::StrCat(
59           "All labels must have the same number of samples ", label, " (",
60           samples_to_render->size(), " vs. ", *num_raw_time_ticks, ")"));
61     }
62   }
63   if (*num_raw_time_ticks < (labeled_frame.samples_to_trim_at_start +
64                              labeled_frame.samples_to_trim_at_end)) {
65     return absl::InvalidArgumentError(absl::StrCat(
66         "Not enough samples to render samples",
67         ". #Raw samples: ", *num_raw_time_ticks,
68         ", samples to trim at start: ", labeled_frame.samples_to_trim_at_start,
69         ", samples to trim at end: ", labeled_frame.samples_to_trim_at_end));
70   }
71 
72   return *num_raw_time_ticks - labeled_frame.samples_to_trim_at_start -
73          labeled_frame.samples_to_trim_at_end;
74 }
75 
76 }  // namespace
77 
ArrangeSamplesToRender(const LabeledFrame & labeled_frame,const std::vector<ChannelLabel::Label> & ordered_labels,std::vector<std::vector<InternalSampleType>> & samples_to_render,size_t & num_valid_samples)78 absl::Status ArrangeSamplesToRender(
79     const LabeledFrame& labeled_frame,
80     const std::vector<ChannelLabel::Label>& ordered_labels,
81     std::vector<std::vector<InternalSampleType>>& samples_to_render,
82     size_t& num_valid_samples) {
83   if (ordered_labels.empty()) {
84     return absl::OkStatus();
85   }
86 
87   const auto common_num_trimmed_time_ticks =
88       GetCommonNumTrimmedTimeTicks(labeled_frame, ordered_labels);
89   if (!common_num_trimmed_time_ticks.ok()) {
90     return common_num_trimmed_time_ticks.status();
91   }
92   num_valid_samples = *common_num_trimmed_time_ticks;
93 
94   const auto num_channels = ordered_labels.size();
95   if (num_valid_samples > samples_to_render.size()) [[unlikely]] {
96     return absl::InvalidArgumentError(absl::StrCat(
97         "Number of time samples to render= ", num_valid_samples,
98         "does not fit into the output of size ", samples_to_render.size()));
99   }
100 
101   for (int channel = 0; channel < num_channels; ++channel) {
102     const auto& channel_label = ordered_labels[channel];
103     if (channel_label == ChannelLabel::kOmitted) {
104       // Missing channels for mixed-order ambisonics representation will not be
105       // updated and will thus have the initialized zeros.
106       continue;
107     }
108 
109     const std::vector<InternalSampleType>* channel_samples = nullptr;
110     RETURN_IF_NOT_OK(DemixingModule::FindSamplesOrDemixedSamples(
111         channel_label, labeled_frame.label_to_samples, &channel_samples));
112     // The lookup should not fail because its presence was already checked in
113     // `GetCommonNumTrimmedTimeTicks`.
114     CHECK_NE(channel_samples, nullptr);
115 
116     // Grab the entire time axes for this label, Skip over any samples that
117     // should be trimmed.
118     for (int time = 0; time < num_valid_samples; ++time) {
119       samples_to_render[time][channel] =
120           (*channel_samples)[time + labeled_frame.samples_to_trim_at_start];
121     }
122   }
123 
124   return absl::OkStatus();
125 }
126 
LookupOutputKeyFromPlaybackLayout(const Layout & output_layout)127 absl::StatusOr<std::string> LookupOutputKeyFromPlaybackLayout(
128     const Layout& output_layout) {
129   switch (output_layout.layout_type) {
130     using enum Layout::LayoutType;
131     case kLayoutTypeLoudspeakersSsConvention: {
132       auto sound_system = std::get<LoudspeakersSsConventionLayout>(
133                               output_layout.specific_layout)
134                               .sound_system;
135       using enum LoudspeakersSsConventionLayout::SoundSystem;
136       static const absl::NoDestructor<absl::flat_hash_map<
137           LoudspeakersSsConventionLayout::SoundSystem, std::string>>
138           kSoundSystemToOutputKey({
139               {kSoundSystemA_0_2_0, "0+2+0"},
140               {kSoundSystemB_0_5_0, "0+5+0"},
141               {kSoundSystemC_2_5_0, "2+5+0"},
142               {kSoundSystemD_4_5_0, "4+5+0"},
143               {kSoundSystemE_4_5_1, "4+5+1"},
144               {kSoundSystemF_3_7_0, "3+7+0"},
145               {kSoundSystemG_4_9_0, "4+9+0"},
146               {kSoundSystemH_9_10_3, "9+10+3"},
147               {kSoundSystemI_0_7_0, "0+7+0"},
148               {kSoundSystemJ_4_7_0, "4+7+0"},
149               {kSoundSystem10_2_7_0, "7.1.2"},
150               {kSoundSystem11_2_3_0, "3.1.2"},
151               {kSoundSystem12_0_1_0, "0+1+0"},
152               {kSoundSystem13_6_9_0, "9.1.6"},
153           });
154 
155       return LookupInMap(*kSoundSystemToOutputKey, sound_system,
156                          "Output key for `SoundSystem`");
157     }
158 
159     case kLayoutTypeBinaural:
160       return absl::UnimplementedError(
161           "Loudness layout key for BINAURAL not supported "
162           "yet.");
163     case kLayoutTypeReserved0:
164     case kLayoutTypeReserved1:
165     default:
166       return absl::UnimplementedError(
167           absl::StrCat("Unsupported layout_type= ", output_layout.layout_type));
168   }
169   return absl::OkStatus();
170 }
171 
172 }  // namespace renderer_utils
173 
174 }  // namespace iamf_tools
175