1 /*
2 * Copyright (c) 2023, 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/obu/parameter_block.h"
13
14 #include <cmath>
15 #include <cstddef>
16 #include <cstdint>
17 #include <memory>
18 #include <variant>
19 #include <vector>
20
21 #include "absl/container/flat_hash_map.h"
22 #include "absl/log/check.h"
23 #include "absl/log/log.h"
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/strings/str_cat.h"
27 #include "iamf/common/read_bit_buffer.h"
28 #include "iamf/common/utils/macros.h"
29 #include "iamf/common/utils/obu_util.h"
30 #include "iamf/common/write_bit_buffer.h"
31 #include "iamf/obu/mix_gain_parameter_data.h"
32 #include "iamf/obu/obu_base.h"
33 #include "iamf/obu/obu_header.h"
34 #include "iamf/obu/param_definition_variant.h"
35 #include "iamf/obu/param_definitions.h"
36 #include "iamf/obu/parameter_data.h"
37 #include "iamf/obu/types.h"
38
39 namespace iamf_tools {
40
ReadAndValidate(const ParamDefinition & param_definition,ReadBitBuffer & rb)41 absl::Status ParameterSubblock::ReadAndValidate(
42 const ParamDefinition& param_definition, ReadBitBuffer& rb) {
43 if (subblock_duration.has_value()) {
44 RETURN_IF_NOT_OK(rb.ReadULeb128(*subblock_duration));
45 }
46
47 param_data = param_definition.CreateParameterData();
48 RETURN_IF_NOT_OK(param_data->ReadAndValidate(rb));
49
50 return absl::OkStatus();
51 }
52
Write(WriteBitBuffer & wb) const53 absl::Status ParameterSubblock::Write(WriteBitBuffer& wb) const {
54 if (subblock_duration.has_value()) {
55 RETURN_IF_NOT_OK(wb.WriteUleb128(*subblock_duration));
56 }
57
58 // Write the specific parameter data depending on the specific type.
59 RETURN_IF_NOT_OK(param_data->Write(wb));
60
61 return absl::OkStatus();
62 }
63
Print() const64 void ParameterSubblock::Print() const {
65 if (subblock_duration.has_value()) {
66 LOG(INFO) << " subblock_duration= " << *subblock_duration;
67 }
68 param_data->Print();
69 }
70
71 absl::StatusOr<std::unique_ptr<ParameterBlockObu>>
CreateFromBuffer(const ObuHeader & header,int64_t payload_size,const absl::flat_hash_map<DecodedUleb128,ParamDefinitionVariant> & param_definition_variants,ReadBitBuffer & rb)72 ParameterBlockObu::CreateFromBuffer(
73 const ObuHeader& header, int64_t payload_size,
74 const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>&
75 param_definition_variants,
76 ReadBitBuffer& rb) {
77 DecodedUleb128 parameter_id;
78 int8_t encoded_uleb128_size = 0;
79 RETURN_IF_NOT_OK(rb.ReadULeb128(parameter_id, encoded_uleb128_size));
80
81 if (payload_size < encoded_uleb128_size) {
82 return absl::InvalidArgumentError(absl::StrCat(
83 "Read beyond the end of the OBU for parameter_id=", parameter_id));
84 }
85
86 const auto parameter_definition_it =
87 param_definition_variants.find(parameter_id);
88 if (parameter_definition_it == param_definition_variants.end()) {
89 return absl::InvalidArgumentError(
90 "Found a stray parameter block OBU (no matching parameter "
91 "definition).");
92 }
93
94 // TODO(b/359588455): Use `ReadBitBuffer::Seek` to go back to the start of the
95 // OBU. Update `ReadAndValidatePayload` to expect to read
96 // `parameter_id`.
97
98 const auto cast_to_base_pointer = [](const auto& param_definition) {
99 return static_cast<const ParamDefinition*>(¶m_definition);
100 };
101 const int64_t remaining_payload_size = payload_size - encoded_uleb128_size;
102 auto parameter_block_obu = std::make_unique<ParameterBlockObu>(
103 header, parameter_id,
104 *std::visit(cast_to_base_pointer, parameter_definition_it->second));
105
106 // TODO(b/338474387): Test reading in extension parameters.
107 RETURN_IF_NOT_OK(
108 parameter_block_obu->ReadAndValidatePayload(remaining_payload_size, rb));
109 return parameter_block_obu;
110 }
111
ParameterBlockObu(const ObuHeader & header,DecodedUleb128 parameter_id,const ParamDefinition & param_definition)112 ParameterBlockObu::ParameterBlockObu(const ObuHeader& header,
113 DecodedUleb128 parameter_id,
114 const ParamDefinition& param_definition)
115 : ObuBase(header, kObuIaParameterBlock),
116 parameter_id_(parameter_id),
117 param_definition_(param_definition) {}
118
InterpolateMixGainParameterData(const MixGainParameterData * mix_gain_parameter_data,InternalTimestamp start_time,InternalTimestamp end_time,InternalTimestamp target_time,float & target_mix_gain_db)119 absl::Status ParameterBlockObu::InterpolateMixGainParameterData(
120 const MixGainParameterData* mix_gain_parameter_data,
121 InternalTimestamp start_time, InternalTimestamp end_time,
122 InternalTimestamp target_time, float& target_mix_gain_db) {
123 return InterpolateMixGainValue(
124 mix_gain_parameter_data->animation_type,
125 MixGainParameterData::kAnimateStep, MixGainParameterData::kAnimateLinear,
126 MixGainParameterData::kAnimateBezier,
127 [&mix_gain_parameter_data]() {
128 return std::get<AnimationStepInt16>(mix_gain_parameter_data->param_data)
129 .start_point_value;
130 },
131 [&mix_gain_parameter_data]() {
132 return std::get<AnimationLinearInt16>(
133 mix_gain_parameter_data->param_data)
134 .start_point_value;
135 },
136 [&mix_gain_parameter_data]() {
137 return std::get<AnimationLinearInt16>(
138 mix_gain_parameter_data->param_data)
139 .end_point_value;
140 },
141 [&mix_gain_parameter_data]() {
142 return std::get<AnimationBezierInt16>(
143 mix_gain_parameter_data->param_data)
144 .start_point_value;
145 },
146 [&mix_gain_parameter_data]() {
147 return std::get<AnimationBezierInt16>(
148 mix_gain_parameter_data->param_data)
149 .end_point_value;
150 },
151 [&mix_gain_parameter_data]() {
152 return std::get<AnimationBezierInt16>(
153 mix_gain_parameter_data->param_data)
154 .control_point_value;
155 },
156 [&mix_gain_parameter_data]() {
157 return std::get<AnimationBezierInt16>(
158 mix_gain_parameter_data->param_data)
159 .control_point_relative_time;
160 },
161 start_time, end_time, target_time, target_mix_gain_db);
162 }
163
GetDuration() const164 DecodedUleb128 ParameterBlockObu::GetDuration() const {
165 if (param_definition_.param_definition_mode_ == 1) {
166 return duration_;
167 } else {
168 return param_definition_.duration_;
169 }
170 }
171
GetConstantSubblockDuration() const172 DecodedUleb128 ParameterBlockObu::GetConstantSubblockDuration() const {
173 if (param_definition_.param_definition_mode_ == 1) {
174 return constant_subblock_duration_;
175 } else {
176 return param_definition_.constant_subblock_duration_;
177 }
178 }
179
GetNumSubblocks() const180 DecodedUleb128 ParameterBlockObu::GetNumSubblocks() const {
181 const DecodedUleb128 duration = GetDuration();
182 const DecodedUleb128 constant_subblock_duration =
183 GetConstantSubblockDuration();
184
185 DecodedUleb128 num_subblocks;
186 if (constant_subblock_duration != 0) {
187 // Get the implicit value of `num_subblocks` using `ceil(duration /
188 // constant_subblock_duration)`. Integer division with ceil is equivalent.
189 num_subblocks = duration / constant_subblock_duration;
190 if (duration % constant_subblock_duration != 0) {
191 num_subblocks += 1;
192 }
193 return num_subblocks;
194 }
195
196 // The subblocks is explicitly in the OBU or `param_definition_`.
197 if (param_definition_.param_definition_mode_ == 1) {
198 num_subblocks = num_subblocks_;
199 } else {
200 num_subblocks = param_definition_.GetNumSubblocks();
201 }
202 return num_subblocks;
203 }
204
GetSubblockDuration(int subblock_index) const205 absl::StatusOr<DecodedUleb128> ParameterBlockObu::GetSubblockDuration(
206 int subblock_index) const {
207 return GetParameterSubblockDuration<DecodedUleb128>(
208 subblock_index, GetNumSubblocks(), GetConstantSubblockDuration(),
209 GetDuration(), param_definition_.param_definition_mode_,
210 [this](int i) { return *this->subblocks_[i].subblock_duration; },
211 [this](int i) { return this->param_definition_.GetSubblockDuration(i); });
212 }
213
SetSubblockDuration(int subblock_index,DecodedUleb128 duration)214 absl::Status ParameterBlockObu::SetSubblockDuration(int subblock_index,
215 DecodedUleb128 duration) {
216 CHECK_NE(param_definition_.param_definition_mode_, 0)
217 << "Calling ParameterBlockObu::SetSubblockDuration() is disallowed when "
218 << "`param_definition_mode_ == 0`";
219
220 const DecodedUleb128 num_subblocks = GetNumSubblocks();
221 if (subblock_index > num_subblocks) {
222 return absl::InvalidArgumentError(absl::StrCat(
223 "Setting subblock duration for subblock_index = ", subblock_index,
224 " but there are only num_subblocks = ", num_subblocks));
225 }
226 const DecodedUleb128 constant_subblock_duration =
227 GetConstantSubblockDuration();
228
229 // Resets the subblock duration to not holding any value.
230 subblocks_[subblock_index].subblock_duration.reset();
231
232 if (constant_subblock_duration == 0) {
233 // Overwrite the value in the parameter block.
234 subblocks_[subblock_index].subblock_duration = duration;
235 }
236 return absl::OkStatus();
237 }
238
GetLinearMixGain(InternalTimestamp obu_relative_time,float & linear_mix_gain) const239 absl::Status ParameterBlockObu::GetLinearMixGain(
240 InternalTimestamp obu_relative_time, float& linear_mix_gain) const {
241 if (param_definition_.GetType() !=
242 ParamDefinition::kParameterDefinitionMixGain) {
243 return absl::InvalidArgumentError("Expected Mix Gain Parameter Definition");
244 }
245
246 const DecodedUleb128 num_subblocks = GetNumSubblocks();
247 int target_subblock_index = -1;
248 InternalTimestamp target_subblock_start_time = -1;
249 InternalTimestamp subblock_relative_start_time = 0;
250 InternalTimestamp subblock_relative_end_time = 0;
251 for (int i = 0; i < num_subblocks; i++) {
252 const auto subblock_duration = GetSubblockDuration(i);
253 if (!subblock_duration.ok()) {
254 return subblock_duration.status();
255 }
256
257 if (subblock_relative_start_time <= obu_relative_time &&
258 obu_relative_time <
259 (subblock_relative_start_time + subblock_duration.value())) {
260 target_subblock_index = i;
261 target_subblock_start_time = subblock_relative_start_time;
262 subblock_relative_end_time =
263 subblock_relative_start_time + subblock_duration.value();
264 break;
265 }
266 subblock_relative_start_time += subblock_duration.value();
267 }
268
269 if (target_subblock_index == -1) {
270 return absl::UnknownError(
271 absl::StrCat("Trying to get mix gain for target_subblock_index = -1, "
272 "with num_subblocks = ",
273 num_subblocks));
274 }
275
276 float mix_gain_db = 0;
277 RETURN_IF_NOT_OK(InterpolateMixGainParameterData(
278 static_cast<const MixGainParameterData*>(
279 subblocks_[target_subblock_index].param_data.get()),
280 subblock_relative_start_time, subblock_relative_end_time,
281 obu_relative_time, mix_gain_db));
282
283 // Mix gain data is in dB and stored in Q7.8. Convert to the linear value.
284 linear_mix_gain = std::pow(10.0f, mix_gain_db / 20.0f);
285 return absl::OkStatus();
286 }
287
InitializeSubblocks(DecodedUleb128 duration,DecodedUleb128 constant_subblock_duration,DecodedUleb128 num_subblocks)288 absl::Status ParameterBlockObu::InitializeSubblocks(
289 DecodedUleb128 duration, DecodedUleb128 constant_subblock_duration,
290 DecodedUleb128 num_subblocks) {
291 CHECK_EQ(param_definition_.param_definition_mode_, 1)
292 << "InitializeSubblocks() with input arguments should only "
293 << "be called when `param_definition_mode_ == 1`";
294
295 SetDuration(duration);
296 SetConstantSubblockDuration(constant_subblock_duration);
297 SetNumSubblocks(num_subblocks);
298 subblocks_.resize(static_cast<size_t>(GetNumSubblocks()));
299 init_status_ = absl::OkStatus();
300 return init_status_;
301 }
302
InitializeSubblocks()303 absl::Status ParameterBlockObu::InitializeSubblocks() {
304 CHECK_EQ(param_definition_.param_definition_mode_, 0)
305 << "InitializeSubblocks() without input arguments should only "
306 << "be called when `param_definition_mode_ == 0`";
307
308 subblocks_.resize(static_cast<size_t>(GetNumSubblocks()));
309 init_status_ = absl::OkStatus();
310 return absl::OkStatus();
311 }
312
PrintObu() const313 void ParameterBlockObu::PrintObu() const {
314 if (!init_status_.ok()) {
315 LOG(ERROR) << "This OBU failed to initialize with error= " << init_status_;
316 }
317
318 LOG(INFO) << "Parameter Block OBU:";
319 LOG(INFO) << " // param_definition:";
320 param_definition_.Print();
321
322 LOG(INFO) << " parameter_id= " << parameter_id_;
323 if (param_definition_.param_definition_mode_ == 1) {
324 LOG(INFO) << " duration= " << duration_;
325 LOG(INFO) << " constant_subblock_duration= "
326 << constant_subblock_duration_;
327 if (constant_subblock_duration_ == 0) {
328 LOG(INFO) << " num_subblocks= " << num_subblocks_;
329 }
330 }
331
332 const DecodedUleb128 num_subblocks = GetNumSubblocks();
333 for (int i = 0; i < num_subblocks; i++) {
334 LOG(INFO) << " subblocks[" << i << "]";
335 subblocks_[i].Print();
336 }
337 }
338
SetDuration(DecodedUleb128 duration)339 void ParameterBlockObu::SetDuration(DecodedUleb128 duration) {
340 CHECK_NE(param_definition_.param_definition_mode_, 0)
341 << "Calling ParameterBlockObu::SetDuration() is disallowed when "
342 << "`param_definition_mode_ == 0`";
343 duration_ = duration;
344 }
345
SetConstantSubblockDuration(DecodedUleb128 constant_subblock_duration)346 void ParameterBlockObu::SetConstantSubblockDuration(
347 DecodedUleb128 constant_subblock_duration) {
348 CHECK_NE(param_definition_.param_definition_mode_, 0)
349 << "Calling ParameterBlockObu::SetConstantSubblockDuration() is "
350 << "disallowed when `param_definition_mode_ == 0`";
351 constant_subblock_duration_ = constant_subblock_duration;
352 }
353
SetNumSubblocks(DecodedUleb128 num_subblocks)354 void ParameterBlockObu::SetNumSubblocks(DecodedUleb128 num_subblocks) {
355 CHECK_NE(param_definition_.param_definition_mode_, 0)
356 << "Calling ParameterBlockObu::SetNumSubblocks() is "
357 << "disallowed when `param_definition_mode_ == 0`";
358 if (GetConstantSubblockDuration() != 0) {
359 // Nothing to do. The field is implicit.
360 return;
361 }
362 num_subblocks_ = num_subblocks;
363 }
364
ValidateAndWritePayload(WriteBitBuffer & wb) const365 absl::Status ParameterBlockObu::ValidateAndWritePayload(
366 WriteBitBuffer& wb) const {
367 if (!init_status_.ok()) {
368 LOG(ERROR) << "Cannot write a Parameter Block OBU whose initialization "
369 << "did not run successfully. init_status_= " << init_status_;
370 return init_status_;
371 }
372
373 RETURN_IF_NOT_OK(wb.WriteUleb128(parameter_id_));
374
375 // Initialized from OBU or `param_definition_` depending on
376 // `param_definition_mode_`.
377 // Write fields that are conditional on `param_definition_mode_`.
378 bool validate_total_subblock_durations = false;
379 if (param_definition_.param_definition_mode_ != 0) {
380 RETURN_IF_NOT_OK(wb.WriteUleb128(duration_));
381 RETURN_IF_NOT_OK(wb.WriteUleb128(constant_subblock_duration_));
382 if (constant_subblock_duration_ == 0) {
383 RETURN_IF_NOT_OK(wb.WriteUleb128(num_subblocks_));
384 validate_total_subblock_durations = true;
385 }
386 }
387
388 // Validate the associated `param_definition`.
389 RETURN_IF_NOT_OK(param_definition_.Validate());
390
391 // Loop through to write the `subblocks_` vector and validate the total
392 // subblock duration if needed.
393 int64_t total_subblock_durations = 0;
394 for (const auto& subblock : subblocks_) {
395 if (validate_total_subblock_durations) {
396 total_subblock_durations += *subblock.subblock_duration;
397 }
398 RETURN_IF_NOT_OK(subblock.Write(wb));
399 }
400
401 // Check total duration matches expected duration.
402 if (validate_total_subblock_durations &&
403 total_subblock_durations != duration_) {
404 return absl::InvalidArgumentError(absl::StrCat(
405 "Expected total_subblock_durations = ", total_subblock_durations,
406 " to match the expected duration_ = ", duration_));
407 }
408
409 return absl::OkStatus();
410 }
411
ReadAndValidatePayloadDerived(int64_t,ReadBitBuffer & rb)412 absl::Status ParameterBlockObu::ReadAndValidatePayloadDerived(
413 int64_t /*payload_size*/, ReadBitBuffer& rb) {
414 // Validate the associated `param_definition`.
415 RETURN_IF_NOT_OK(param_definition_.Validate());
416
417 if (param_definition_.param_definition_mode_) {
418 RETURN_IF_NOT_OK(rb.ReadULeb128(duration_));
419 RETURN_IF_NOT_OK(rb.ReadULeb128(constant_subblock_duration_));
420 if (constant_subblock_duration_ == 0) {
421 RETURN_IF_NOT_OK(rb.ReadULeb128(num_subblocks_));
422 }
423 }
424
425 const auto num_subblocks = GetNumSubblocks();
426 subblocks_.resize(num_subblocks);
427
428 // `subblock_duration` is conditionally included based on
429 // `param_definition_mode_` and `constant_subblock_duration_`.
430 const bool include_subblock_duration =
431 param_definition_.param_definition_mode_ &&
432 constant_subblock_duration_ == 0;
433 int64_t total_subblock_durations = 0;
434 for (auto& subblock : subblocks_) {
435 if (include_subblock_duration) {
436 // First make `subblock_duration` contain a value so it will be read in.
437 subblock.subblock_duration = 0;
438 }
439 RETURN_IF_NOT_OK(subblock.ReadAndValidate(param_definition_, rb));
440 if (include_subblock_duration) {
441 total_subblock_durations += *subblock.subblock_duration;
442 }
443 }
444
445 if (include_subblock_duration && total_subblock_durations != duration_) {
446 return absl::InvalidArgumentError(
447 "Subblock durations do not match the total duration.");
448 }
449
450 init_status_ = absl::OkStatus();
451 return absl::OkStatus();
452 }
453
454 } // namespace iamf_tools
455