• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*>(&param_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