• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, 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 #ifndef COMMON_UTILS_OBU_UTIL_H_
13 #define COMMON_UTILS_OBU_UTIL_H_
14 
15 #include <cmath>
16 #include <cstdint>
17 
18 #include "absl/functional/any_invocable.h"
19 #include "absl/log/check.h"
20 #include "absl/log/log.h"
21 #include "absl/status/status.h"
22 #include "absl/status/statusor.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/string_view.h"
25 #include "iamf/common/utils/numeric_utils.h"
26 #include "iamf/obu/types.h"
27 
28 namespace iamf_tools {
29 
30 /*!\brief Gets the duration of a parameter subblock.
31  *
32  * The Spec defines a complex logic of getting the final subblock duration from
33  * multiple potential sources, including:
34  *   - The constant subblock duration recorded in the parameter block.
35  *   - The duration recorded in the parameter block's subblock at index i.
36  *   - The constant subblock duration recorded in the parameter definition.
37  *   - The subblock duration at index i recorded in the parameter definition.
38  *
39  * \param subblock_index Index of the subblock to get the duration of.
40  * \param num_subblocks Number of subblocks.
41  * \param constant_subblock_duration Constant subblock duration.
42  * \param subblock_duration_getter_from_parameter_block Getter function
43  *        that returns the subblock duration recorded inside a parameter block,
44  *        indexed at `subblock_index`.
45  * \param subblock_duration_getter_from_parameter_definition Getter function
46  *        that returns the subblock duration recorded inside a parameter
47  *        definition, indexed at `subblock_index`.
48  * \return Duration of the subblock or `absl::InvalidArgumentError()` on
49  *         failure.
50  */
51 template <typename T>
GetParameterSubblockDuration(int subblock_index,T num_subblocks,T constant_subblock_duration,T total_duration,uint8_t param_definition_mode,absl::AnyInvocable<absl::StatusOr<T> (int)> subblock_duration_getter_from_parameter_block,absl::AnyInvocable<absl::StatusOr<T> (int)> subblock_duration_getter_from_parameter_definition)52 absl::StatusOr<T> GetParameterSubblockDuration(
53     int subblock_index, T num_subblocks, T constant_subblock_duration,
54     T total_duration, uint8_t param_definition_mode,
55     absl::AnyInvocable<absl::StatusOr<T>(int)>
56         subblock_duration_getter_from_parameter_block,
57     absl::AnyInvocable<absl::StatusOr<T>(int)>
58         subblock_duration_getter_from_parameter_definition) {
59   if (subblock_index > num_subblocks) {
60     return absl::InvalidArgumentError("subblock_index > num_subblocks");
61   }
62 
63   if (constant_subblock_duration == 0) {
64     if (param_definition_mode == 1) {
65       // The durations are explicitly specified in the parameter block.
66       return subblock_duration_getter_from_parameter_block(subblock_index);
67     } else {
68       // The durations are explicitly specified in the parameter definition.
69       return subblock_duration_getter_from_parameter_definition(subblock_index);
70     }
71   }
72 
73   // Otherwise the duration is implicit.
74   if (subblock_index == num_subblocks - 1 &&
75       num_subblocks * constant_subblock_duration > total_duration) {
76     // Sometimes the last subblock duration is shorter. The spec describes how
77     // to calculate the special case: "If NS x CSD > D, the actual duration of
78     // the last subblock SHALL be D - (NS - 1) x CSD."
79     return (total_duration - (num_subblocks - 1) * constant_subblock_duration);
80   } else {
81     // Otherwise the duration is based on `constant_subblock_duration`.
82     return constant_subblock_duration;
83   }
84 }
85 
86 /*!\brief Interpolates a mix gain value in dB.
87  *
88  * The logic is used to partition parameter block protocol buffers as well as
89  * to query the gain value at a specific timestamp during mixing.
90  *
91  * \param animation_type Type of animation applied to the mix gain values.
92  * \param step_enum Enum value representing a step animation.
93  * \param linear_enum Enum value representing a linear animation.
94  * \param bezier_enum Enum value representing a Bezier animation.
95  * \param step_start_point_getter Getter function of the start point value
96  *        of a step animation.
97  * \param linear_start_point_getter Getter function of the start point value
98  *        of a linear animation.
99  * \param linear_end_point_getter Getter function of the end point value
100  *        of a linear animation.
101  * \param bezier_start_point_getter Getter function of the start point value
102  *        of a Bezier animation.
103  * \param bezier_end_point_getter Getter function of the end point value
104  *        of a Bezier animation.
105  * \param bezier_control_point_getter Getter function of the middle control
106  *        point value of a Bezier animation.
107  * \param bezier_control_point_relative_time_getter Getter function of the
108  *        time of the middle control point of a Bezier animation.
109  * \param start_time Start time of the `MixGainParameterData`.
110  * \param end_time End time of the `MixGainParameterData`.
111  * \param target_time Target time to the get interpolated value of.
112  * \param target_mix_gain_db Output inteprolated mix gain value in dB.
113  * \return `absl::OkStatus()` on success. A specific status on failure.
114  */
115 template <typename AnimationEnumType>
InterpolateMixGainValue(AnimationEnumType animation_type,AnimationEnumType step_enum,AnimationEnumType linear_enum,AnimationEnumType bezier_enum,absl::AnyInvocable<int16_t ()> step_start_point_getter,absl::AnyInvocable<int16_t ()> linear_start_point_getter,absl::AnyInvocable<int16_t ()> linear_end_point_getter,absl::AnyInvocable<int16_t ()> bezier_start_point_getter,absl::AnyInvocable<int16_t ()> bezier_end_point_getter,absl::AnyInvocable<int16_t ()> bezier_control_point_getter,absl::AnyInvocable<int16_t ()> bezier_control_point_relative_time_getter,InternalTimestamp start_time,InternalTimestamp end_time,InternalTimestamp target_time,float & target_mix_gain_db)116 absl::Status InterpolateMixGainValue(
117     AnimationEnumType animation_type, AnimationEnumType step_enum,
118     AnimationEnumType linear_enum, AnimationEnumType bezier_enum,
119     absl::AnyInvocable<int16_t()> step_start_point_getter,
120     absl::AnyInvocable<int16_t()> linear_start_point_getter,
121     absl::AnyInvocable<int16_t()> linear_end_point_getter,
122     absl::AnyInvocable<int16_t()> bezier_start_point_getter,
123     absl::AnyInvocable<int16_t()> bezier_end_point_getter,
124     absl::AnyInvocable<int16_t()> bezier_control_point_getter,
125     absl::AnyInvocable<int16_t()> bezier_control_point_relative_time_getter,
126     InternalTimestamp start_time, InternalTimestamp end_time,
127     InternalTimestamp target_time, float& target_mix_gain_db) {
128   if (target_time < start_time || target_time > end_time ||
129       start_time > end_time) {
130     return absl::InvalidArgumentError(absl::StrCat(
131         "Cannot interpolate mix gain value with start time = ", start_time,
132         ", target_time = ", target_time, " and end_time = ", end_time));
133   }
134 
135   // Shift times so start_time=0 to simplify calculations.
136   end_time -= start_time;
137   target_time -= start_time;
138   start_time = 0;
139 
140   // TODO(b/283281856): Support resampling parameter blocks.
141   const int sample_rate_ratio = 1;
142   const int n_0 = start_time * sample_rate_ratio;
143   const int n = target_time * sample_rate_ratio;
144   const int n_2 = end_time * sample_rate_ratio;
145 
146   if (animation_type == step_enum) {
147     // No interpolation is needed for step.
148     target_mix_gain_db = Q7_8ToFloat(step_start_point_getter());
149   } else if (animation_type == linear_enum) {
150     // Interpolate using the exact formula from the spec.
151     const float a = (float)n / (float)n_2;
152     const float p_0 = Q7_8ToFloat(linear_start_point_getter());
153     const float p_2 = Q7_8ToFloat(linear_end_point_getter());
154     target_mix_gain_db = (1 - a) * p_0 + a * p_2;
155   } else if (animation_type == bezier_enum) {
156     const float control_point_float =
157         Q0_8ToFloat(bezier_control_point_relative_time_getter());
158     // Using the definition of `round` in the IAMF spec.
159     const int n_1 = std::floor((end_time * control_point_float) + 0.5);
160 
161     const float p_0 = Q7_8ToFloat(bezier_start_point_getter());
162     const float p_1 = Q7_8ToFloat(bezier_control_point_getter());
163     const float p_2 = Q7_8ToFloat(bezier_end_point_getter());
164 
165     const float alpha = n_0 - 2 * n_1 + n_2;
166     const float beta = 2 * (n_1 - n_0);
167     const float gamma = n_0 - n;
168     const float a = alpha == 0
169                         ? -gamma / beta
170                         : (-beta + std::sqrt(beta * beta - 4 * alpha * gamma)) /
171                               (2 * alpha);
172     target_mix_gain_db =
173         (1 - a) * (1 - a) * p_0 + 2 * (1 - a) * a * p_1 + a * a * p_2;
174   } else {
175     return absl::InvalidArgumentError(
176         absl::StrCat("Unknown animation_type = ", animation_type));
177   }
178 
179   return absl::OkStatus();
180 }
181 
182 }  // namespace iamf_tools
183 
184 #endif  // COMMON_UTILS_OBU_UTIL_H_
185