blob: 1efa748d2c8ef5003ea02ee7ace70ab2a6b85201 [file] [log] [blame]
// Copyright 2022 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_cvsd_decoder.h"
#include <safemath/safe_math.h>
namespace {
static constexpr char kPcmMimeType[] = "audio/pcm";
static constexpr uint8_t kPcmBitsPerSample = 16;
// The implementations are based off of the CVSD codec algorithm defined in
// Bluetooth Core Spec v5.3, Vol 2, Part B Sec 9.2.
int16_t SingleDecode(CvsdParams* params, const uint8_t& compressed_bit) {
UpdateCvsdParams(params, compressed_bit);
int16_t x = Round(params->accumulator);
return x;
}
} // namespace
CodecAdapterCvsdDecoder::CodecAdapterCvsdDecoder(std::mutex& lock,
CodecAdapterEvents* codec_adapter_events)
: CodecAdapterSWImpl(lock, codec_adapter_events) {}
std::pair<fuchsia::media::FormatDetails, size_t> CodecAdapterCvsdDecoder::OutputFormatDetails() {
ZX_DEBUG_ASSERT(codec_params_);
fuchsia::media::PcmFormat out;
out.pcm_mode = fuchsia::media::AudioPcmMode::LINEAR;
out.bits_per_sample = kPcmBitsPerSample;
out.frames_per_second = kExpectedSamplingFreq;
// For CVSD, we assume MONO channel.
out.channel_map = {fuchsia::media::AudioChannelId::LF};
fuchsia::media::AudioUncompressedFormat uncompressed;
uncompressed.set_pcm(out);
fuchsia::media::AudioFormat audio_format;
audio_format.set_uncompressed(std::move(uncompressed));
fuchsia::media::FormatDetails format_details;
format_details.set_mime_type(kPcmMimeType);
format_details.mutable_domain()->set_audio(std::move(audio_format));
return {std::move(format_details), kOutputFrameSize};
}
CodecAdapterCvsdDecoder::InputLoopStatus CodecAdapterCvsdDecoder::ProcessFormatDetails(
const fuchsia::media::FormatDetails& format_details) {
if (!format_details.has_mime_type() || format_details.mime_type() != kCvsdMimeType) {
events_->onCoreCodecFailCodec(
"CVSD Decoder received input that was not compressed cvsd audio.");
return kShouldTerminate;
}
InitCvsdParams(codec_params_);
InitChunkInputStream(format_details);
return kOk;
}
// Decode input buffer of 1 byte size to produce 16 bytes of output data.
int CodecAdapterCvsdDecoder::ProcessInputChunkData(const uint8_t* input_data,
size_t input_data_size, uint8_t* output_buffer,
size_t output_buffer_size) {
// Since decode does 1 bit to 2 byte (16 bits) decompression, we cast to int16_t
// pointer.
int16_t* output = reinterpret_cast<int16_t*>(output_buffer);
auto num_output_bytes_produced = 0;
for (int idx = 0; idx < static_cast<int>(input_data_size); idx += 1) {
// Process one byte of input data.
uint8_t input_byte = input_data[idx];
uint8_t mask = 0b10000000;
for (uint32_t idx = 0; idx < 8; idx++) {
uint8_t encoded_bit = input_byte & mask ? 1 : 0;
*output = SingleDecode(&(*codec_params_), encoded_bit);
mask >>= 1;
output++;
}
num_output_bytes_produced += kOutputFrameSize;
ZX_DEBUG_ASSERT((int)output_buffer_size >= num_output_bytes_produced);
}
return num_output_bytes_produced;
}
fuchsia::sysmem::BufferCollectionConstraints CodecAdapterCvsdDecoder::BufferCollectionConstraints(
CodecPort port) {
fuchsia::sysmem::BufferCollectionConstraints c;
c.min_buffer_count_for_camping = kMinBufferCountForCamping;
// Actual minimum buffer size is 1 byte for input and 16 bytes for output since we're doing
// 1:16 decompression for decoding. However, sysmem will basically allocate at least a
// 4KiB page per buffer even if the client is also ok with less, so we default to
// system page size for now.
if (port == kInputPort) {
c.buffer_memory_constraints.min_size_bytes =
std::max(zx_system_get_page_size(), kInputFrameSize);
c.buffer_memory_constraints.max_size_bytes = kInputPerPacketBufferBytesMax;
} else {
// TODO(dayeonglee): consider requiring a larger buffer, based on what
// seems like a minimum reasonable time duration per buffer, to keep the
// buffers per second <= ~120.
c.buffer_memory_constraints.min_size_bytes =
std::max(zx_system_get_page_size(), (uint32_t)MinOutputBufferSize());
// Set to some arbitrary value.
c.buffer_memory_constraints.max_size_bytes = 0xFFFFFFFF;
}
return c;
}