blob: 481922addbeec7fe8d52025b92068b174c28d5ec [file] [log] [blame]
/*
* 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/obu/codec_config.h"
#include <cstdint>
#include <utility>
#include <variant>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "iamf/common/read_bit_buffer.h"
#include "iamf/common/utils/macros.h"
#include "iamf/common/write_bit_buffer.h"
#include "iamf/obu/decoder_config/aac_decoder_config.h"
#include "iamf/obu/decoder_config/flac_decoder_config.h"
#include "iamf/obu/decoder_config/lpcm_decoder_config.h"
#include "iamf/obu/decoder_config/opus_decoder_config.h"
#include "iamf/obu/obu_base.h"
#include "iamf/obu/obu_header.h"
#include "iamf/obu/types.h"
namespace iamf_tools {
namespace {
absl::Status ValidateNumSamplesPerFrame(uint32_t num_samples_per_frame) {
if (num_samples_per_frame == 0) {
return absl::InvalidArgumentError(
"Number of samples per frame must be non-zero.");
}
return absl::OkStatus();
}
absl::Status OverrideAudioRollDistance(CodecConfig::CodecId codec_id,
uint32_t num_samples_per_frame,
int16_t& output_audio_roll_distance) {
switch (codec_id) {
using enum CodecConfig::CodecId;
case CodecConfig::kCodecIdOpus: {
auto audio_roll_distance =
OpusDecoderConfig::GetRequiredAudioRollDistance(
num_samples_per_frame);
if (!audio_roll_distance.ok()) {
return audio_roll_distance.status();
}
output_audio_roll_distance = *audio_roll_distance;
return absl::OkStatus();
}
case kCodecIdLpcm:
output_audio_roll_distance =
LpcmDecoderConfig::GetRequiredAudioRollDistance();
return absl::OkStatus();
case kCodecIdFlac:
output_audio_roll_distance =
FlacDecoderConfig::GetRequiredAudioRollDistance();
return absl::OkStatus();
case kCodecIdAacLc:
output_audio_roll_distance =
AacDecoderConfig::GetRequiredAudioRollDistance();
return absl::OkStatus();
default:
return absl::InvalidArgumentError(
absl::StrCat("Unknown codec_id: ", codec_id));
}
}
absl::Status SetSampleRatesAndBitDepths(
uint32_t codec_id, const DecoderConfig& decoder_config,
uint32_t& output_sample_rate, uint32_t& input_sample_rate,
uint8_t& bit_depth_to_measure_loudness) {
switch (codec_id) {
using enum CodecConfig::CodecId;
case kCodecIdOpus: {
const auto& opus_decoder_config =
std::get<OpusDecoderConfig>(decoder_config);
output_sample_rate = opus_decoder_config.GetOutputSampleRate();
input_sample_rate = opus_decoder_config.GetInputSampleRate();
bit_depth_to_measure_loudness =
OpusDecoderConfig::GetBitDepthToMeasureLoudness();
return absl::OkStatus();
}
case kCodecIdLpcm: {
const auto& lpcm_decoder_config =
std::get<LpcmDecoderConfig>(decoder_config);
RETURN_IF_NOT_OK(
lpcm_decoder_config.GetOutputSampleRate(output_sample_rate));
input_sample_rate = output_sample_rate;
RETURN_IF_NOT_OK(lpcm_decoder_config.GetBitDepthToMeasureLoudness(
bit_depth_to_measure_loudness));
return absl::OkStatus();
}
case kCodecIdAacLc:
RETURN_IF_NOT_OK(std::get<AacDecoderConfig>(decoder_config)
.GetOutputSampleRate(output_sample_rate));
input_sample_rate = output_sample_rate;
bit_depth_to_measure_loudness =
AacDecoderConfig::GetBitDepthToMeasureLoudness();
return absl::OkStatus();
case kCodecIdFlac: {
const auto& flac_decoder_config =
std::get<FlacDecoderConfig>(decoder_config);
RETURN_IF_NOT_OK(
flac_decoder_config.GetOutputSampleRate(output_sample_rate));
input_sample_rate = output_sample_rate;
RETURN_IF_NOT_OK(flac_decoder_config.GetBitDepthToMeasureLoudness(
bit_depth_to_measure_loudness));
return absl::OkStatus();
}
default:
return absl::InvalidArgumentError(
absl::StrCat("Unknown codec_id: ", codec_id));
}
}
} // namespace
CodecConfigObu::CodecConfigObu(const ObuHeader& header,
const DecodedUleb128 codec_config_id,
const CodecConfig& codec_config)
: ObuBase(header, kObuIaCodecConfig),
codec_config_id_(codec_config_id),
codec_config_(std::move(codec_config)) {}
absl::StatusOr<CodecConfigObu> CodecConfigObu::CreateFromBuffer(
const ObuHeader& header, int64_t payload_size, ReadBitBuffer& rb) {
CodecConfigObu codec_config_obu(header);
RETURN_IF_NOT_OK(codec_config_obu.ReadAndValidatePayload(payload_size, rb));
RETURN_IF_NOT_OK(codec_config_obu.Initialize());
return codec_config_obu;
}
absl::Status CodecConfigObu::ValidateAndWriteDecoderConfig(
WriteBitBuffer& wb) const {
if (!init_status_.ok()) {
return init_status_;
}
// Write the `decoder_config` struct portion. This is codec specific.
const int16_t audio_roll_distance = codec_config_.audio_roll_distance;
const uint32_t num_samples_per_frame = codec_config_.num_samples_per_frame;
switch (codec_config_.codec_id) {
using enum CodecConfig::CodecId;
case kCodecIdOpus:
return std::get<OpusDecoderConfig>(codec_config_.decoder_config)
.ValidateAndWrite(num_samples_per_frame, audio_roll_distance, wb);
case kCodecIdLpcm:
return std::get<LpcmDecoderConfig>(codec_config_.decoder_config)
.ValidateAndWrite(audio_roll_distance, wb);
case kCodecIdAacLc:
return std::get<AacDecoderConfig>(codec_config_.decoder_config)
.ValidateAndWrite(audio_roll_distance, wb);
case kCodecIdFlac:
return std::get<FlacDecoderConfig>(codec_config_.decoder_config)
.ValidateAndWrite(num_samples_per_frame, audio_roll_distance, wb);
default:
return absl::InvalidArgumentError(
absl::StrCat("Unknown codec_id: ", codec_config_.codec_id));
}
}
absl::Status CodecConfigObu::ValidateAndWritePayload(WriteBitBuffer& wb) const {
if (!init_status_.ok()) {
return init_status_;
}
RETURN_IF_NOT_OK(wb.WriteUleb128(codec_config_id_));
RETURN_IF_NOT_OK(wb.WriteUnsignedLiteral(codec_config_.codec_id, 32));
RETURN_IF_NOT_OK(
ValidateNumSamplesPerFrame(codec_config_.num_samples_per_frame));
RETURN_IF_NOT_OK(wb.WriteUleb128(codec_config_.num_samples_per_frame));
RETURN_IF_NOT_OK(wb.WriteSigned16(codec_config_.audio_roll_distance));
// Write the `decoder_config_`. This is codec specific.
RETURN_IF_NOT_OK(ValidateAndWriteDecoderConfig(wb));
return absl::OkStatus();
}
absl::Status CodecConfigObu::ReadAndValidateDecoderConfig(ReadBitBuffer& rb) {
const int16_t audio_roll_distance = codec_config_.audio_roll_distance;
const uint32_t num_samples_per_frame = codec_config_.num_samples_per_frame;
// Read the `decoder_config` struct portion. This is codec specific.
switch (codec_config_.codec_id) {
using enum CodecConfig::CodecId;
case kCodecIdOpus: {
OpusDecoderConfig opus_decoder_config;
RETURN_IF_NOT_OK(opus_decoder_config.ReadAndValidate(
num_samples_per_frame, audio_roll_distance, rb));
codec_config_.decoder_config = opus_decoder_config;
return absl::OkStatus();
}
case kCodecIdLpcm: {
LpcmDecoderConfig lpcm_decoder_config;
RETURN_IF_NOT_OK(
lpcm_decoder_config.ReadAndValidate(audio_roll_distance, rb));
codec_config_.decoder_config = lpcm_decoder_config;
return absl::OkStatus();
}
case kCodecIdAacLc: {
AacDecoderConfig aac_decoder_config;
RETURN_IF_NOT_OK(
aac_decoder_config.ReadAndValidate(audio_roll_distance, rb));
codec_config_.decoder_config = aac_decoder_config;
return absl::OkStatus();
}
case kCodecIdFlac: {
FlacDecoderConfig flac_decoder_config;
RETURN_IF_NOT_OK(flac_decoder_config.ReadAndValidate(
num_samples_per_frame, audio_roll_distance, rb));
codec_config_.decoder_config = flac_decoder_config;
return absl::OkStatus();
}
default:
return absl::InvalidArgumentError(
absl::StrCat("Unknown codec_id: ", codec_config_.codec_id));
}
return absl::OkStatus();
}
absl::Status CodecConfigObu::ReadAndValidatePayloadDerived(
int64_t /*payload_size*/, ReadBitBuffer& rb) {
RETURN_IF_NOT_OK(rb.ReadULeb128(codec_config_id_));
uint64_t codec_id;
RETURN_IF_NOT_OK(rb.ReadUnsignedLiteral(32, codec_id));
codec_config_.codec_id = static_cast<CodecConfig::CodecId>(codec_id);
RETURN_IF_NOT_OK(rb.ReadULeb128(codec_config_.num_samples_per_frame));
RETURN_IF_NOT_OK(
ValidateNumSamplesPerFrame(codec_config_.num_samples_per_frame));
RETURN_IF_NOT_OK(rb.ReadSigned16(codec_config_.audio_roll_distance));
// Read the `decoder_config_`. This is codec specific.
RETURN_IF_NOT_OK(ReadAndValidateDecoderConfig(rb));
return absl::OkStatus();
}
void CodecConfigObu::PrintObu() const {
if (!init_status_.ok()) {
LOG(ERROR) << "This OBU failed to initialize with error= " << init_status_;
}
LOG(INFO) << "Codec Config OBU:";
LOG(INFO) << " codec_config_id= " << codec_config_id_;
LOG(INFO) << " codec_config:";
LOG(INFO) << " codec_id= " << codec_config_.codec_id;
LOG(INFO) << " num_samples_per_frame= " << GetNumSamplesPerFrame();
LOG(INFO) << " audio_roll_distance= " << codec_config_.audio_roll_distance;
// Print the `decoder_config_`. This is codec specific.
switch (codec_config_.codec_id) {
using enum CodecConfig::CodecId;
case kCodecIdLpcm:
std::get<LpcmDecoderConfig>(codec_config_.decoder_config).Print();
break;
case kCodecIdOpus:
std::get<OpusDecoderConfig>(codec_config_.decoder_config).Print();
break;
case kCodecIdFlac:
std::get<FlacDecoderConfig>(codec_config_.decoder_config).Print();
break;
case kCodecIdAacLc:
std::get<AacDecoderConfig>(codec_config_.decoder_config).Print();
break;
default:
LOG(ERROR) << "Unknown codec_id: " << codec_config_.codec_id;
break;
}
LOG(INFO) << " // input_sample_rate_= " << input_sample_rate_;
LOG(INFO) << " // output_sample_rate_= " << output_sample_rate_;
LOG(INFO) << " // bit_depth_to_measure_loudness_= "
<< absl::StrCat(bit_depth_to_measure_loudness_);
}
absl::Status CodecConfigObu::Initialize(
bool automatically_override_roll_distance) {
init_status_ = SetSampleRatesAndBitDepths(
codec_config_.codec_id, codec_config_.decoder_config, output_sample_rate_,
input_sample_rate_, bit_depth_to_measure_loudness_);
if (automatically_override_roll_distance) {
init_status_.Update(OverrideAudioRollDistance(
codec_config_.codec_id, codec_config_.num_samples_per_frame,
codec_config_.audio_roll_distance));
}
if (!init_status_.ok()) {
PrintObu();
}
return init_status_;
}
absl::Status CodecConfigObu::SetCodecDelay(uint16_t codec_delay) {
switch (codec_config_.codec_id) {
using enum CodecConfig::CodecId;
case kCodecIdLpcm:
case kCodecIdFlac:
case kCodecIdAacLc:
// Ok the `decoder_config` does not have a field for codec delay.
return absl::OkStatus();
case kCodecIdOpus: {
OpusDecoderConfig* opus_decoder_config =
std::get_if<OpusDecoderConfig>(&codec_config_.decoder_config);
if (opus_decoder_config == nullptr) {
return absl::InvalidArgumentError(
"OpusDecoderConfig is not set in CodecConfig.");
}
opus_decoder_config->pre_skip_ = codec_delay;
return absl::OkStatus();
}
}
LOG(FATAL) << "Unknown codec_id: " << codec_config_.codec_id;
}
bool CodecConfigObu::IsLossless() const {
using enum CodecConfig::CodecId;
return codec_config_.codec_id == kCodecIdFlac ||
codec_config_.codec_id == kCodecIdLpcm;
}
} // namespace iamf_tools