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