|
|
|
@ -5,6 +5,7 @@ |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include "opus.hpp" |
|
|
|
|
|
|
|
|
|
#include <stdint.h> |
|
|
|
|
#include <sys/_stdint.h> |
|
|
|
|
|
|
|
|
@ -12,36 +13,101 @@ |
|
|
|
|
#include <cstring> |
|
|
|
|
#include <optional> |
|
|
|
|
|
|
|
|
|
#include "esp_heap_caps.h" |
|
|
|
|
#include "mad.h" |
|
|
|
|
|
|
|
|
|
#include "codec.hpp" |
|
|
|
|
#include "esp_log.h" |
|
|
|
|
#include "opus.h" |
|
|
|
|
#include "opus_types.h" |
|
|
|
|
#include "result.hpp" |
|
|
|
|
#include "sample.hpp" |
|
|
|
|
#include "types.hpp" |
|
|
|
|
|
|
|
|
|
namespace codecs { |
|
|
|
|
|
|
|
|
|
static constexpr std::size_t kSampleBufferSize = 5760 * sizeof(float); |
|
|
|
|
static constexpr char kTag[] = "opus"; |
|
|
|
|
|
|
|
|
|
// "If this is less than the maximum packet duration (120ms; 5760 for 48kHz),
|
|
|
|
|
// this function will not be capable of decoding some packets"
|
|
|
|
|
static constexpr size_t kSampleBufferSize = 5760; |
|
|
|
|
|
|
|
|
|
XiphOpusDecoder::XiphOpusDecoder() { |
|
|
|
|
int err; |
|
|
|
|
opus_ = opus_decoder_create(48000, 2, &err); |
|
|
|
|
assert(err == OPUS_OK); |
|
|
|
|
|
|
|
|
|
pos_in_buffer_ = 0; |
|
|
|
|
sample_buffer_ = {reinterpret_cast<opus_int16*>( |
|
|
|
|
heap_caps_calloc(kSampleBufferSize, sizeof(opus_int16), |
|
|
|
|
MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM)), |
|
|
|
|
kSampleBufferSize}; |
|
|
|
|
} |
|
|
|
|
XiphOpusDecoder::~XiphOpusDecoder() { |
|
|
|
|
opus_decoder_destroy(opus_); |
|
|
|
|
heap_caps_free(sample_buffer_.data()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto XiphOpusDecoder::BeginStream(const cpp::span<const std::byte> input) |
|
|
|
|
-> Result<OutputFormat> {} |
|
|
|
|
-> Result<OutputFormat> { |
|
|
|
|
return {0, OutputFormat{ |
|
|
|
|
.num_channels = 2, |
|
|
|
|
.sample_rate_hz = 48000, |
|
|
|
|
}}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto read_uint32(cpp::span<const std::byte> src) -> uint32_t { |
|
|
|
|
return static_cast<uint32_t>(src[0] << 24) | |
|
|
|
|
static_cast<uint32_t>(src[1] << 16) | |
|
|
|
|
static_cast<uint32_t>(src[2] << 8) | |
|
|
|
|
static_cast<uint32_t>(src[3] << 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto XiphOpusDecoder::ContinueStream(cpp::span<const std::byte> input, |
|
|
|
|
cpp::span<std::byte> output) |
|
|
|
|
cpp::span<sample::Sample> output) |
|
|
|
|
-> Result<OutputInfo> { |
|
|
|
|
int samples_decoded = opus_decode_float( |
|
|
|
|
opus_, reinterpret_cast<const unsigned char*>(input.data()), |
|
|
|
|
input.size_bytes(), sample_buffer_, sample_buffer_len_, 0); |
|
|
|
|
size_t bytes_used = 0; |
|
|
|
|
if (pos_in_buffer_ >= samples_in_buffer_) { |
|
|
|
|
ESP_LOGI(kTag, "sample buffer is empty. parsing more."); |
|
|
|
|
if (input.size() < 4) { |
|
|
|
|
return {0, cpp::fail(Error::kOutOfInput)}; |
|
|
|
|
} |
|
|
|
|
uint32_t payload_length = read_uint32(input); |
|
|
|
|
ESP_LOGI(kTag, "payload length is %lu", payload_length); |
|
|
|
|
|
|
|
|
|
if (input.size() - 4 < payload_length) { |
|
|
|
|
ESP_LOGI(kTag, "input too small for payload"); |
|
|
|
|
return {0, cpp::fail(Error::kOutOfInput)}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Next 4 bytes are the 'final range'.
|
|
|
|
|
// uint32_t enc_final_range = read_uint32(input.subspan(4));
|
|
|
|
|
|
|
|
|
|
bytes_used = payload_length + 8; |
|
|
|
|
|
|
|
|
|
pos_in_buffer_ = 0; |
|
|
|
|
samples_in_buffer_ = opus_decode( |
|
|
|
|
opus_, reinterpret_cast<const unsigned char*>(input.data() + 8), |
|
|
|
|
payload_length, sample_buffer_.data(), sample_buffer_.size(), 0); |
|
|
|
|
|
|
|
|
|
if (samples_in_buffer_ < 0) { |
|
|
|
|
ESP_LOGE(kTag, "error decoding stream"); |
|
|
|
|
return {bytes_used, cpp::fail(Error::kMalformedData)}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t samples_written = 0; |
|
|
|
|
while (pos_in_buffer_ < samples_in_buffer_ && |
|
|
|
|
samples_written < output.size()) { |
|
|
|
|
output[samples_written++] = |
|
|
|
|
sample::FromSigned(sample_buffer_[pos_in_buffer_++], 16); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return {bytes_used, |
|
|
|
|
OutputInfo{ |
|
|
|
|
.samples_written = samples_written, |
|
|
|
|
.is_finished_writing = pos_in_buffer_ >= samples_in_buffer_, |
|
|
|
|
}}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
auto XiphOpusDecoder::SeekStream(cpp::span<const std::byte> input, |
|
|
|
|