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/cli/global_timing_module.h"
13
14 #include <cstdint>
15 #include <memory>
16 #include <optional>
17 #include <utility>
18 #include <variant>
19
20 #include "absl/container/flat_hash_map.h"
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/log/log.h"
23 #include "absl/memory/memory.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "iamf/cli/audio_element_with_data.h"
27 #include "iamf/cli/cli_util.h"
28 #include "iamf/common/utils/macros.h"
29 #include "iamf/common/utils/validation_utils.h"
30 #include "iamf/obu/audio_element.h"
31 #include "iamf/obu/codec_config.h"
32 #include "iamf/obu/param_definition_variant.h"
33 #include "iamf/obu/types.h"
34
35 namespace iamf_tools {
36
37 namespace {
38
InitializeInternal(const absl::flat_hash_map<DecodedUleb128,AudioElementWithData> & audio_elements,const absl::flat_hash_map<DecodedUleb128,ParamDefinitionVariant> & param_definition_variants,auto & audio_frame_timing_data,auto & parameter_block_timing_data)39 absl::Status InitializeInternal(
40 const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>&
41 audio_elements,
42 const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>&
43 param_definition_variants,
44 auto& audio_frame_timing_data, auto& parameter_block_timing_data) {
45 // TODO(b/283281856): Handle cases where `parameter_rate` and `sample_rate`
46 // differ.
47 for (const auto& [unused_id, audio_element] : audio_elements) {
48 // Initialize all substream IDs to start at 0 even if the substreams do not
49 // actually appear in the bitstream.
50 for (const auto& audio_substream_id :
51 audio_element.obu.audio_substream_ids_) {
52 const uint32_t sample_rate =
53 audio_element.codec_config->GetOutputSampleRate();
54 RETURN_IF_NOT_OK(
55 ValidateNotEqual(sample_rate, uint32_t{0}, "sample rate"));
56
57 const auto [unused_iter, inserted] = audio_frame_timing_data.insert(
58 {audio_substream_id, {.rate = sample_rate, .timestamp = 0}});
59
60 if (!inserted) {
61 return absl::InvalidArgumentError(
62 absl::StrCat("Audio substream ID: ", audio_substream_id,
63 " already exists in the Global Timing Module"));
64 }
65 }
66 }
67
68 // Initialize all parameter IDs to start with a timestamp 0.
69 for (const auto& [parameter_id, param_definition_variant] :
70 param_definition_variants) {
71 const DecodedUleb128 parameter_rate = std::visit(
72 [](const auto& param_definition) {
73 return param_definition.parameter_rate_;
74 },
75 param_definition_variant);
76 RETURN_IF_NOT_OK(
77 ValidateNotEqual(parameter_rate, DecodedUleb128(0), "parameter rate"));
78
79 const auto [unused_iter, inserted] = parameter_block_timing_data.insert(
80 {parameter_id, {.rate = parameter_rate, .timestamp = 0}});
81 if (!inserted) {
82 return absl::InvalidArgumentError(
83 absl::StrCat("Parameter ID: ", parameter_id,
84 " already exists in the Global Timing Module"));
85 }
86 }
87
88 return absl::OkStatus();
89 }
90
91 } // namespace
92
Create(const absl::flat_hash_map<DecodedUleb128,AudioElementWithData> & audio_elements,const absl::flat_hash_map<DecodedUleb128,ParamDefinitionVariant> & param_definition_variants)93 std::unique_ptr<GlobalTimingModule> GlobalTimingModule::Create(
94 const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>&
95 audio_elements,
96 const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>&
97 param_definition_variants) {
98 absl::flat_hash_map<DecodedUleb128, TimingData> audio_frame_timing_data;
99 absl::flat_hash_map<DecodedUleb128, TimingData> parameter_block_timing_data;
100 const auto status =
101 InitializeInternal(audio_elements, param_definition_variants,
102 audio_frame_timing_data, parameter_block_timing_data);
103 if (!status.ok()) {
104 LOG(ERROR) << status;
105 return nullptr;
106 }
107
108 return absl::WrapUnique(
109 new GlobalTimingModule(std::move(audio_frame_timing_data),
110 std::move(parameter_block_timing_data)));
111 }
112
GetNextAudioFrameTimestamps(const DecodedUleb128 audio_substream_id,const uint32_t duration,InternalTimestamp & start_timestamp,InternalTimestamp & end_timestamp)113 absl::Status GlobalTimingModule::GetNextAudioFrameTimestamps(
114 const DecodedUleb128 audio_substream_id, const uint32_t duration,
115 InternalTimestamp& start_timestamp, InternalTimestamp& end_timestamp) {
116 return GetTimestampsForId(audio_substream_id, duration,
117 audio_frame_timing_data_, start_timestamp,
118 end_timestamp);
119 }
120
GetNextParameterBlockTimestamps(const uint32_t parameter_id,const InternalTimestamp input_start_timestamp,const uint32_t duration,InternalTimestamp & start_timestamp,InternalTimestamp & end_timestamp)121 absl::Status GlobalTimingModule::GetNextParameterBlockTimestamps(
122 const uint32_t parameter_id, const InternalTimestamp input_start_timestamp,
123 const uint32_t duration, InternalTimestamp& start_timestamp,
124 InternalTimestamp& end_timestamp) {
125 RETURN_IF_NOT_OK(GetTimestampsForId(parameter_id, duration,
126 parameter_block_timing_data_,
127 start_timestamp, end_timestamp));
128 return CompareTimestamps(
129 input_start_timestamp, start_timestamp,
130 absl::StrCat("In GetNextParameterBlockTimestamps() for param ID= ",
131 parameter_id, ": "));
132 }
133
GetGlobalAudioFrameTimestamp(std::optional<InternalTimestamp> & global_timestamp) const134 absl::Status GlobalTimingModule::GetGlobalAudioFrameTimestamp(
135 std::optional<InternalTimestamp>& global_timestamp) const {
136 if (audio_frame_timing_data_.empty()) {
137 return absl::InvalidArgumentError("No audio frames to get timestamps for");
138 }
139
140 const InternalTimestamp common_timestamp =
141 audio_frame_timing_data_.begin()->second.timestamp;
142 for (const auto& [unused_id, timing_data] : audio_frame_timing_data_) {
143 if (common_timestamp != timing_data.timestamp) {
144 // Some audio frames have not advance their timestamps yet, return OK
145 // but let `global_timestamp` hold no value.
146 global_timestamp = std::nullopt;
147 return absl::OkStatus();
148 }
149 }
150
151 global_timestamp = common_timestamp;
152 return absl::OkStatus();
153 }
154
GetTimestampsForId(const DecodedUleb128 id,const uint32_t duration,absl::flat_hash_map<DecodedUleb128,TimingData> & id_to_timing_data,InternalTimestamp & start_timestamp,InternalTimestamp & end_timestamp)155 absl::Status GlobalTimingModule::GetTimestampsForId(
156 const DecodedUleb128 id, const uint32_t duration,
157 absl::flat_hash_map<DecodedUleb128, TimingData>& id_to_timing_data,
158 InternalTimestamp& start_timestamp, InternalTimestamp& end_timestamp) {
159 auto timing_data_iter = id_to_timing_data.find(id);
160 if (timing_data_iter == id_to_timing_data.end()) {
161 // This allows generating timing information when
162 // `IGNORE_ERRORS_USE_ONLY_FOR_IAMF_TEST_SUITE` is defined.
163 // TODO(b/278865608): Find better solutions to generate negative test
164 // vectors.
165 start_timestamp = 0;
166 end_timestamp = duration;
167 return absl::InvalidArgumentError(
168 absl::StrCat("Timestamps for ID: ", id, " not found"));
169 }
170
171 auto& timing_data = timing_data_iter->second;
172 start_timestamp = timing_data.timestamp;
173 end_timestamp = start_timestamp + duration;
174 timing_data.timestamp += duration;
175 return absl::OkStatus();
176 }
177
178 } // namespace iamf_tools
179