blob: 7005b1583da88c13fb8b2cbc825a822f08e18919 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "codec_adapter_lc3_encoder.h"
#include <lib/media/codec_impl/codec_port.h>
#include <lib/media/codec_impl/log.h>
#include <unordered_set>
#include <safemath/safe_math.h>
#include "codec_adapter_sw.h"
#include "lc3.h"
namespace {
bool IsAcceptableSamplingFrequency(const uint32_t freq) {
// See LC3 specification v1.0 section 2.2 and 2.4 for acceptable sampling
// frequency values for the uncompressed audio.
static const std::unordered_set<uint32_t> supported{8000, 16000, 24000, 32000, 44100, 48000};
return supported.find(freq) != supported.end();
}
bool IsAcceptableBitsPerUncompAudioSample(const uint32_t bits_per_sample) {
// See LC3 specification v1.0 section 2.2 and 2.4 for acceptable bits per
// audio sample.
// Fuchsia LC3 codec does not support 32 bit.
static const std::unordered_set<uint32_t> supported{16, 24};
return supported.find(bits_per_sample) != supported.end();
}
Lc3EncoderParams CreateEncoderParams(const fuchsia::media::FormatDetails& format_details) {
auto& input_format = format_details.domain().audio().uncompressed().pcm();
auto& settings = format_details.encoder_settings().lc3();
auto num_channels = static_cast<int>(input_format.channel_map.size());
std::vector<Lc3CodecContainer<lc3_encoder_t>> encoders;
int frame_us =
(settings.frame_duration() == fuchsia::media::Lc3FrameDuration::D10_MS) ? 10000 : 7500;
// According to LC3 Specification v1.0 section 2.1, when the sampling frequency of the
// input signal is 44.1 kHz, the same frame length is used as for 48 kHz.
int sampling_freq = (input_format.frames_per_second == 44100)
? 48000
: static_cast<int>(input_format.frames_per_second);
encoders.reserve(num_channels);
auto encoder_size = lc3_encoder_size(frame_us, sampling_freq);
// Set up an encoder for each channel to account for multi-channeled interleaved audio.
for (int i = 0; i < num_channels; ++i) {
encoders.emplace_back(
[&frame_us, &sampling_freq](void* mem) {
// Sets up and returns the pointer to the encoder struct. The pointer has the same value
// as `mem`.
return lc3_setup_encoder(frame_us, sampling_freq, 0, mem);
},
encoder_size);
}
return Lc3EncoderParams{
.encoders = std::move(encoders),
.num_channels = num_channels,
.dt_us = frame_us,
.sr_hz = sampling_freq,
.nbytes = static_cast<int>(settings.nbytes()),
.fmt = (input_format.bits_per_sample == 16) ? LC3_PCM_FORMAT_S16 : LC3_PCM_FORMAT_S24,
};
}
} // namespace
CodecAdapterLc3Encoder::CodecAdapterLc3Encoder(std::mutex& lock,
CodecAdapterEvents* codec_adapter_events)
: CodecAdapterSWImpl(lock, codec_adapter_events) {}
std::pair<fuchsia::media::FormatDetails, size_t> CodecAdapterLc3Encoder::OutputFormatDetails() {
ZX_DEBUG_ASSERT(codec_params_);
fuchsia::media::AudioCompressedFormatLc3 lc3;
fuchsia::media::AudioCompressedFormat compressed_format;
compressed_format.set_lc3(std::move(lc3));
fuchsia::media::AudioFormat audio_format;
audio_format.set_compressed(std::move(compressed_format));
fuchsia::media::FormatDetails format_details;
format_details.set_mime_type(kLc3MimeType);
format_details.mutable_domain()->set_audio(std::move(audio_format));
return {std::move(format_details), MinOutputBufferSize()};
}
CodecAdapterLc3Encoder::InputLoopStatus CodecAdapterLc3Encoder::ProcessFormatDetails(
const fuchsia::media::FormatDetails& format_details) {
if (!format_details.has_domain() || !format_details.domain().is_audio() ||
!format_details.domain().audio().is_uncompressed() ||
!format_details.domain().audio().uncompressed().is_pcm()) {
events_->onCoreCodecFailCodec(
"LC3 Encoder received input that was not uncompressed pcm audio.");
return kShouldTerminate;
}
if (!format_details.has_encoder_settings() || !format_details.encoder_settings().is_lc3()) {
events_->onCoreCodecFailCodec("LC3 Encoder did not receive encoder settings.");
return kShouldTerminate;
}
auto& input_format = format_details.domain().audio().uncompressed().pcm();
auto& settings = format_details.encoder_settings().lc3();
if (input_format.pcm_mode != fuchsia::media::AudioPcmMode::LINEAR ||
!IsAcceptableBitsPerUncompAudioSample(input_format.bits_per_sample)) {
events_->onCoreCodecFailCodec("Unsupported bits per sample LC3 Encoder.");
return kShouldTerminate;
}
if (!IsAcceptableSamplingFrequency(input_format.frames_per_second)) {
events_->onCoreCodecFailCodec("Unsupported sampling frequency for LC3 Encoder.");
return kShouldTerminate;
}
if (settings.nbytes() < kMinExternalByteCount || settings.nbytes() > kMaxExternalByteCount) {
events_->onCoreCodecFailCodec("Byte count should be between [20 ... 400] bytes per channel.");
return kShouldTerminate;
}
codec_params_.emplace(CreateEncoderParams(format_details));
InitChunkInputStream(format_details);
return kOk;
}
// Encode input buffer of 16 byte size to produce one byte of output data.
int CodecAdapterLc3Encoder::ProcessInputChunkData(const uint8_t* input_data, size_t input_data_size,
uint8_t* output_buffer,
size_t output_buffer_size) {
ZX_DEBUG_ASSERT(codec_params_);
const lc3_pcm_format pcm_fmt = codec_params_->fmt;
int nch = codec_params_->num_channels;
uint8_t* buffer = output_buffer;
int bytes_produced = 0;
for (int ich = 0; ich < nch; ich++) {
// Ensure we have enough space.
ZX_DEBUG_ASSERT(static_cast<int>(output_buffer_size) >= bytes_produced + codec_params_->nbytes);
// In the case of 24 bits per input sample, we pad each sample to 32 bits with low 8 bits zero.
const void* pcm =
(pcm_fmt == LC3_PCM_FORMAT_S16)
? static_cast<const void*>(reinterpret_cast<const int16_t*>(input_data) + ich)
: static_cast<const void*>(reinterpret_cast<const int32_t*>(input_data) + ich);
if (lc3_encode(codec_params_->encoders[ich].GetCodec(), pcm_fmt, pcm, nch,
codec_params_->nbytes, buffer) != 0) {
return -1;
}
bytes_produced += codec_params_->nbytes;
buffer += codec_params_->nbytes;
}
return bytes_produced;
}
fuchsia::sysmem::BufferCollectionConstraints CodecAdapterLc3Encoder::BufferCollectionConstraints(
CodecPort port) {
fuchsia::sysmem::BufferCollectionConstraints c;
if (port == kInputPort) {
c.min_buffer_count_for_camping = kMinInputBufferCountForCamping;
c.buffer_memory_constraints.min_size_bytes = zx_system_get_page_size();
c.buffer_memory_constraints.max_size_bytes = kInputPerPacketBufferBytesMax;
} else {
c.min_buffer_count_for_camping = kMinOutputBufferCountForCamping;
ZX_ASSERT(codec_params_.has_value());
c.buffer_memory_constraints.min_size_bytes = static_cast<uint32_t>(MinOutputBufferSize());
c.buffer_memory_constraints.max_size_bytes = 0xFFFFFFFF; // arbitrary value.
}
return c;
}
size_t CodecAdapterLc3Encoder::InputChunkSize() {
ZX_DEBUG_ASSERT(codec_params_);
int num_bytes_per_sample = (codec_params_->fmt == LC3_PCM_FORMAT_S16) ? 2 : 4;
// LC3 Spec v1.0 section 2.2. Encoder Interfaces.
// The total size of an input frame is specified by the session
// configured number of channels, the frame size in samples, and the
// configured encoder PCM bits per audio sample.
// Frame size in samples is duration in microseconds * sampling frequency in hz / 1000 / 1000.
return codec_params_->num_channels * codec_params_->dt_us * codec_params_->sr_hz *
num_bytes_per_sample / 1000 / 1000;
}
size_t CodecAdapterLc3Encoder::MinOutputBufferSize() {
ZX_DEBUG_ASSERT(codec_params_);
// LC3 Spec v1.0 section 2.2. Encoder Interfaces.
// Expected output frame size is combined byte_count for all the channels.
return codec_params_->nbytes * codec_params_->num_channels;
}
TimestampExtrapolator CodecAdapterLc3Encoder::CreateTimestampExtrapolator(
const fuchsia::media::FormatDetails& format_details) {
ZX_DEBUG_ASSERT(codec_params_);
if (format_details.has_timebase()) {
auto& input_format = format_details.domain().audio().uncompressed().pcm();
// Bytes per second is sampling frequency * number_of_channels * bytes_per_sample.
// Note that we use codec_params_->sr_hz instead of input_format.frames_per_second here
// because when the sampling frequency of the input signal is 44.1 kHz, we coerce it
// into 48 kHz per the spec.
// Note that this results in the slightly longer actual frame duration of 10.884 ms for the 10
// ms frame interval and of 8.16 ms for the 7.5 ms frame interval. See LC3 specification v1.0
// section 2.1 for more details.
const size_t bytes_per_second = input_format.channel_map.size() * codec_params_->sr_hz *
(input_format.bits_per_sample / 8ull);
return TimestampExtrapolator(format_details.timebase(), bytes_per_second);
}
return TimestampExtrapolator();
}