blob: 874761599f402f1e7cf99bb0b084a5429344ef76 [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/cli/codec/flac_encoder.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <list>
#include <memory>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/span.h"
#include "iamf/cli/audio_frame_with_data.h"
#include "iamf/cli/proto/codec_config.pb.h"
#include "iamf/common/utils/macros.h"
#include "iamf/common/utils/sample_processing_utils.h"
#include "iamf/obu/decoder_config/flac_decoder_config.h"
#include "include/FLAC/format.h"
#include "include/FLAC/ordinals.h"
#include "include/FLAC/stream_encoder.h"
namespace iamf_tools {
namespace {
absl::Status Configure(
const iamf_tools_cli_proto::FlacEncoderMetadata& encoder_metadata,
const FlacDecoderConfig& decoder_config, int num_channels,
uint32_t num_samples_per_frame, uint32_t output_sample_rate,
uint8_t input_pcm_bit_depth_, FLAC__StreamEncoder* const encoder) {
FLAC__bool ok = true;
// Configure values based on the associated Codec Config OBU.
ok &= FLAC__stream_encoder_set_channels(encoder, num_channels);
ok &= FLAC__stream_encoder_set_bits_per_sample(
encoder, static_cast<uint32_t>(input_pcm_bit_depth_));
ok &= FLAC__stream_encoder_set_sample_rate(encoder, output_sample_rate);
// IAMF requires a constant block size.
ok &= FLAC__stream_encoder_set_blocksize(encoder, num_samples_per_frame);
uint64_t total_samples_in_stream;
RETURN_IF_NOT_OK(
decoder_config.GetTotalSamplesInStream(total_samples_in_stream));
ok &= FLAC__stream_encoder_set_total_samples_estimate(
encoder, total_samples_in_stream);
// Set arguments configured by the user-provided `encoder_metadata_`.
ok &= FLAC__stream_encoder_set_compression_level(
encoder, encoder_metadata.compression_level());
ok &= FLAC__stream_encoder_set_verify(encoder, true);
if (!ok) {
return absl::UnknownError("Failed to configure Flac encoder.");
}
return absl::OkStatus();
}
} // namespace
FLAC__StreamEncoderWriteStatus LibFlacWriteCallback(
const FLAC__StreamEncoder* /*encoder*/, const FLAC__byte buffer[],
size_t bytes, unsigned int samples, unsigned int current_frame,
void* client_data) {
const unsigned int kLibFlacMetadataSentinel = 0;
if (samples == kLibFlacMetadataSentinel) {
// `libflac` uses a value of `0` to indicate this callback is for metadata.
LOG(INFO) << "`iamf_tools` currently ignores all additional FLAC metadata.";
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
auto flac_encoder = static_cast<FlacEncoder*>(client_data);
absl::MutexLock lock(&flac_encoder->mutex_);
auto flac_frame_iter =
flac_encoder->frame_index_to_frame_.find(current_frame);
if (flac_frame_iter == flac_encoder->frame_index_to_frame_.end()) {
LOG(ERROR) << "Failed to find a frame with index " << current_frame
<< " in Flac encoder. Data may be lost or corrupted.";
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
}
// Append to `audio_frame_` and track how many samples it represents. It will
// be finalized later to ensure frames are finalized in chronological order.
FlacFrame& flac_frame = flac_frame_iter->second;
flac_frame.audio_frame_with_data->obu.audio_frame_.insert(
flac_frame.audio_frame_with_data->obu.audio_frame_.end(), buffer,
buffer + bytes);
flac_frame.num_samples += samples;
if (flac_frame.num_samples == flac_encoder->num_samples_per_frame_) {
// A frame has been completed; move to the finalized frames.
flac_encoder->finalized_audio_frames_.emplace_back(
std::move(*flac_frame_iter->second.audio_frame_with_data));
// The frame is fully processed and no longer needed.
flac_encoder->frame_index_to_frame_.erase(flac_frame_iter);
}
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
void LibFlacMetadataCallback(const FLAC__StreamEncoder* /*encoder*/,
const FLAC__StreamMetadata* metadata,
void* client_data) {
LOG_FIRST_N(INFO, 1) << "Begin `LibFlacMetadataCallback`.";
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
LOG(INFO) << "Received `STREAMINFO` metadata.";
// Just validate we got the `STREAMINFO` metadata at some point. IAMF
// requires some fields to be set constant and different from what will be
// returned by `libflac`.
auto flac_encoder = static_cast<FlacEncoder*>(client_data);
absl::MutexLock lock(&flac_encoder->mutex_);
flac_encoder->finished_ = true;
}
}
FlacEncoder::~FlacEncoder() {
FLAC__stream_encoder_delete(encoder_);
absl::MutexLock lock(&mutex_);
if (!frame_index_to_frame_.empty()) {
LOG(ERROR) << "Some frames were not fully processed. Maybe `Finalize()` "
"was not called.";
}
}
absl::Status FlacEncoder::EncodeAudioFrame(
int input_bit_depth, const std::vector<std::vector<int32_t>>& samples,
std::unique_ptr<AudioFrameWithData> partial_audio_frame_with_data) {
RETURN_IF_NOT_OK(ValidateNotFinalized());
RETURN_IF_NOT_OK(ValidateInputSamples(samples));
const int num_samples_per_channel = static_cast<int>(num_samples_per_frame_);
LOG_FIRST_N(INFO, 1) << "num_samples_per_channel: "
<< num_samples_per_channel;
LOG_FIRST_N(INFO, 1) << "num_channels: " << num_channels_;
// FLAC requires a right-justified sign extended value. Calculate what the
// mask is to sign extend a `input_bit_depth`-bit value.
uint32_t base_sign_extension_mask = 0;
for (int i = 31; i > input_bit_depth - 1; --i) {
base_sign_extension_mask |= 1 << i;
}
const absl::AnyInvocable<absl::Status(int32_t, int32_t&) const>
kLeftJustifiedToRightJustified =
[base_sign_extension_mask, input_bit_depth](int32_t input,
int32_t& output) {
// Only apply the sign extension mask when the left-justified value
// has '1' in the MSB.
const uint32_t sign_extension_mask =
(input & 0x80000000) ? base_sign_extension_mask : 0;
// Shift the input value to be right-justified.
output = static_cast<uint32_t>(input) >> (32 - input_bit_depth) |
sign_extension_mask;
return absl::OkStatus();
};
// Convert input to the array that will be passed to `flac_encode`.
std::vector<FLAC__int32> encoder_input_pcm;
RETURN_IF_NOT_OK(ConvertTimeChannelToInterleaved(
absl::MakeConstSpan(samples), kLeftJustifiedToRightJustified,
encoder_input_pcm));
LOG_FIRST_N(INFO, 1) << "Encoding " << encoder_input_pcm.size() * 4
<< " bytes representing " << num_samples_per_channel
<< " x " << num_channels_ << " samples.";
if (!FLAC__stream_encoder_process_interleaved(
encoder_, encoder_input_pcm.data(), num_samples_per_channel)) {
return absl::UnknownError("Flac failed to encode.");
}
absl::MutexLock lock(&mutex_);
// Transfer ownership of the partial audio frame so it can be finalized later.
frame_index_to_frame_[next_frame_index_++].audio_frame_with_data =
std::move(partial_audio_frame_with_data);
return absl::OkStatus();
}
absl::Status FlacEncoder::Finalize() {
// Signal to `libflac` the encoder is finished.
if (!FLAC__stream_encoder_finish(encoder_)) {
return absl::UnknownError("Failed to finalize Flac encoder.");
}
return absl::OkStatus();
}
absl::Status FlacEncoder::InitializeEncoder() {
// Initialize the encoder.
encoder_ = FLAC__stream_encoder_new();
if (encoder_ == nullptr) {
return absl::UnknownError("Failed to initialize Flac encoder.");
}
// Configure the FLAC encoder based on user input data.
RETURN_IF_NOT_OK(Configure(encoder_metadata_, decoder_config_, num_channels_,
num_samples_per_frame_, output_sample_rate_,
input_pcm_bit_depth_, encoder_));
// Initialize the FLAC encoder.
FLAC__StreamEncoderInitStatus init_status = FLAC__stream_encoder_init_stream(
encoder_, LibFlacWriteCallback, /*seek_callback=*/nullptr,
/*tell_callback=*/nullptr, LibFlacMetadataCallback,
static_cast<void*>(this));
if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
return absl::UnknownError(
absl::StrCat("Failed to initialize Flac stream: ", init_status));
}
return absl::OkStatus();
}
} // namespace iamf_tools