| /* |
| * Copyright (c) 2023, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 3-Clause Clear License |
| * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear |
| * License was not distributed with this source code in the LICENSE file, you |
| * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the |
| * Alliance for Open Media Patent License 1.0 was not distributed with this |
| * source code in the PATENTS file, you can obtain it at |
| * www.aomedia.org/license/patent. |
| */ |
| #include "iamf/cli/global_timing_module.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| #include <variant> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/log/log.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "iamf/cli/audio_element_with_data.h" |
| #include "iamf/cli/cli_util.h" |
| #include "iamf/common/utils/macros.h" |
| #include "iamf/common/utils/validation_utils.h" |
| #include "iamf/obu/audio_element.h" |
| #include "iamf/obu/codec_config.h" |
| #include "iamf/obu/param_definition_variant.h" |
| #include "iamf/obu/types.h" |
| |
| namespace iamf_tools { |
| |
| namespace { |
| |
| absl::Status 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) { |
| // TODO(b/283281856): Handle cases where `parameter_rate` and `sample_rate` |
| // differ. |
| for (const auto& [unused_id, audio_element] : audio_elements) { |
| // Initialize all substream IDs to start at 0 even if the substreams do not |
| // actually appear in the bitstream. |
| for (const auto& audio_substream_id : |
| audio_element.obu.audio_substream_ids_) { |
| const uint32_t sample_rate = |
| audio_element.codec_config->GetOutputSampleRate(); |
| RETURN_IF_NOT_OK( |
| ValidateNotEqual(sample_rate, uint32_t{0}, "sample rate")); |
| |
| const auto [unused_iter, inserted] = audio_frame_timing_data.insert( |
| {audio_substream_id, {.rate = sample_rate, .timestamp = 0}}); |
| |
| if (!inserted) { |
| return absl::InvalidArgumentError( |
| absl::StrCat("Audio substream ID: ", audio_substream_id, |
| " already exists in the Global Timing Module")); |
| } |
| } |
| } |
| |
| // Initialize all parameter IDs to start with a timestamp 0. |
| for (const auto& [parameter_id, param_definition_variant] : |
| param_definition_variants) { |
| const DecodedUleb128 parameter_rate = std::visit( |
| [](const auto& param_definition) { |
| return param_definition.parameter_rate_; |
| }, |
| param_definition_variant); |
| RETURN_IF_NOT_OK( |
| ValidateNotEqual(parameter_rate, DecodedUleb128(0), "parameter rate")); |
| |
| const auto [unused_iter, inserted] = parameter_block_timing_data.insert( |
| {parameter_id, {.rate = parameter_rate, .timestamp = 0}}); |
| if (!inserted) { |
| return absl::InvalidArgumentError( |
| absl::StrCat("Parameter ID: ", parameter_id, |
| " already exists in the Global Timing Module")); |
| } |
| } |
| |
| return absl::OkStatus(); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<GlobalTimingModule> GlobalTimingModule::Create( |
| const absl::flat_hash_map<DecodedUleb128, AudioElementWithData>& |
| audio_elements, |
| const absl::flat_hash_map<DecodedUleb128, ParamDefinitionVariant>& |
| param_definition_variants) { |
| absl::flat_hash_map<DecodedUleb128, TimingData> audio_frame_timing_data; |
| absl::flat_hash_map<DecodedUleb128, TimingData> parameter_block_timing_data; |
| const auto status = |
| InitializeInternal(audio_elements, param_definition_variants, |
| audio_frame_timing_data, parameter_block_timing_data); |
| if (!status.ok()) { |
| LOG(ERROR) << status; |
| return nullptr; |
| } |
| |
| return absl::WrapUnique( |
| new GlobalTimingModule(std::move(audio_frame_timing_data), |
| std::move(parameter_block_timing_data))); |
| } |
| |
| absl::Status GlobalTimingModule::GetNextAudioFrameTimestamps( |
| const DecodedUleb128 audio_substream_id, const uint32_t duration, |
| InternalTimestamp& start_timestamp, InternalTimestamp& end_timestamp) { |
| return GetTimestampsForId(audio_substream_id, duration, |
| audio_frame_timing_data_, start_timestamp, |
| end_timestamp); |
| } |
| |
| absl::Status GlobalTimingModule::GetNextParameterBlockTimestamps( |
| const uint32_t parameter_id, const InternalTimestamp input_start_timestamp, |
| const uint32_t duration, InternalTimestamp& start_timestamp, |
| InternalTimestamp& end_timestamp) { |
| RETURN_IF_NOT_OK(GetTimestampsForId(parameter_id, duration, |
| parameter_block_timing_data_, |
| start_timestamp, end_timestamp)); |
| return CompareTimestamps( |
| input_start_timestamp, start_timestamp, |
| absl::StrCat("In GetNextParameterBlockTimestamps() for param ID= ", |
| parameter_id, ": ")); |
| } |
| |
| absl::Status GlobalTimingModule::GetGlobalAudioFrameTimestamp( |
| std::optional<InternalTimestamp>& global_timestamp) const { |
| if (audio_frame_timing_data_.empty()) { |
| return absl::InvalidArgumentError("No audio frames to get timestamps for"); |
| } |
| |
| const InternalTimestamp common_timestamp = |
| audio_frame_timing_data_.begin()->second.timestamp; |
| for (const auto& [unused_id, timing_data] : audio_frame_timing_data_) { |
| if (common_timestamp != timing_data.timestamp) { |
| // Some audio frames have not advance their timestamps yet, return OK |
| // but let `global_timestamp` hold no value. |
| global_timestamp = std::nullopt; |
| return absl::OkStatus(); |
| } |
| } |
| |
| global_timestamp = common_timestamp; |
| return absl::OkStatus(); |
| } |
| |
| absl::Status GlobalTimingModule::GetTimestampsForId( |
| const DecodedUleb128 id, const uint32_t duration, |
| absl::flat_hash_map<DecodedUleb128, TimingData>& id_to_timing_data, |
| InternalTimestamp& start_timestamp, InternalTimestamp& end_timestamp) { |
| auto timing_data_iter = id_to_timing_data.find(id); |
| if (timing_data_iter == id_to_timing_data.end()) { |
| // This allows generating timing information when |
| // `IGNORE_ERRORS_USE_ONLY_FOR_IAMF_TEST_SUITE` is defined. |
| // TODO(b/278865608): Find better solutions to generate negative test |
| // vectors. |
| start_timestamp = 0; |
| end_timestamp = duration; |
| return absl::InvalidArgumentError( |
| absl::StrCat("Timestamps for ID: ", id, " not found")); |
| } |
| |
| auto& timing_data = timing_data_iter->second; |
| start_timestamp = timing_data.timestamp; |
| end_timestamp = start_timestamp + duration; |
| timing_data.timestamp += duration; |
| return absl::OkStatus(); |
| } |
| |
| } // namespace iamf_tools |