From d8fc77101dcf80a3643a00b3446dca1e390ce997 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 10 Aug 2023 15:33:00 +1000 Subject: Give codecs complete control of their input files --- src/codecs/opus.cpp | 135 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 48 deletions(-) (limited to 'src/codecs/opus.cpp') diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp index a71c5fc0..70ec9e45 100644 --- a/src/codecs/opus.cpp +++ b/src/codecs/opus.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -27,23 +28,49 @@ namespace codecs { static constexpr char kTag[] = "opus"; -int read_cb(void* instance, unsigned char* ptr, int nbytes) { - XiphOpusDecoder* dec = reinterpret_cast(instance); - auto input = dec->ReadCallback(); - size_t amount_to_read = std::min(nbytes, input.size_bytes()); - std::memcpy(ptr, input.data(), amount_to_read); - dec->AfterReadCallback(amount_to_read); - return amount_to_read; +static int read_cb(void* src, unsigned char* ptr, int nbytes) { + IStream* source = reinterpret_cast(src); + return source->Read( + {reinterpret_cast(ptr), static_cast(nbytes)}); +} + +static int seek_cb(void* src, int64_t offset, int whence) { + IStream* source = reinterpret_cast(src); + if (!source->CanSeek()) { + return -1; + } + IStream::SeekFrom from; + switch (whence) { + case SEEK_CUR: + from = IStream::SeekFrom::kCurrentPosition; + break; + case SEEK_END: + from = IStream::SeekFrom::kEndOfStream; + break; + case SEEK_SET: + from = IStream::SeekFrom::kStartOfStream; + break; + default: + return -1; + } + source->SeekTo(offset, from); + return 0; +} + +static int64_t tell_cb(void* src) { + IStream* source = reinterpret_cast(src); + return source->CurrentPosition(); } static const OpusFileCallbacks kCallbacks{ .read = read_cb, - .seek = NULL, - .tell = NULL, // Not seekable + .seek = seek_cb, + .tell = tell_cb, .close = NULL, }; -XiphOpusDecoder::XiphOpusDecoder() : opus_(nullptr) {} +XiphOpusDecoder::XiphOpusDecoder() + : input_(nullptr), opus_(nullptr), num_channels_() {} XiphOpusDecoder::~XiphOpusDecoder() { if (opus_ != nullptr) { @@ -51,12 +78,12 @@ XiphOpusDecoder::~XiphOpusDecoder() { } } -auto XiphOpusDecoder::BeginStream(const cpp::span input) - -> Result { +auto XiphOpusDecoder::OpenStream(std::shared_ptr input) + -> cpp::result { + input_ = input; + int res; - opus_ = op_open_callbacks( - this, &kCallbacks, reinterpret_cast(input.data()), - input.size(), &res); + opus_ = op_open_callbacks(input.get(), &kCallbacks, nullptr, 0, &res); if (res < 0) { std::string err; @@ -64,60 +91,72 @@ auto XiphOpusDecoder::BeginStream(const cpp::span input) case OP_EREAD: err = "OP_EREAD"; break; + case OP_EFAULT: + err = "OP_EFAULT"; + break; + case OP_EIMPL: + err = "OP_EIMPL"; + break; + case OP_EINVAL: + err = "OP_EINVAL"; + break; + case OP_ENOTFORMAT: + err = "OP_ENOTFORMAT"; + break; + case OP_EBADHEADER: + err = "OP_EBADHEADER"; + break; + case OP_EVERSION: + err = "OP_EVERSION"; + break; + case OP_EBADLINK: + err = "OP_EBADLINK"; + break; + case OP_EBADTIMESTAMP: + err = "OP_BADTIMESTAMP"; + break; default: err = "unknown"; } ESP_LOGE(kTag, "error beginning stream: %s", err.c_str()); - return {input.size(), cpp::fail(Error::kMalformedData)}; + return cpp::fail(Error::kMalformedData); } - return {input.size(), OutputFormat{ - .num_channels = 2, - .sample_rate_hz = 48000, - }}; + num_channels_ = std::min(2, op_channel_count(opus_, -1)); + + return OutputFormat{ + .num_channels = num_channels_, + .sample_rate_hz = 48000, + }; } -auto XiphOpusDecoder::ContinueStream(cpp::span input, - cpp::span output) - -> Result { +auto XiphOpusDecoder::DecodeTo(cpp::span output) + -> cpp::result { cpp::span staging_buffer{ reinterpret_cast(output.subspan(output.size() / 2).data()), output.size_bytes() / 2}; - input_ = input; - pos_in_input_ = 0; - - int bytes_written = + int samples_written = op_read_stereo(opus_, staging_buffer.data(), staging_buffer.size()); - if (bytes_written < 0) { - ESP_LOGE(kTag, "read failed %i", bytes_written); - return {pos_in_input_, cpp::fail(Error::kMalformedData)}; - } else if (bytes_written == 0) { - return {pos_in_input_, cpp::fail(Error::kOutOfInput)}; + + if (samples_written < 0) { + ESP_LOGE(kTag, "read failed %i", samples_written); + return cpp::fail(Error::kMalformedData); } - for (int i = 0; i < bytes_written / 2; i++) { + samples_written *= num_channels_; + for (int i = 0; i < samples_written; i++) { output[i] = sample::FromSigned(staging_buffer[i], 16); } - return {pos_in_input_, - OutputInfo{ - .samples_written = static_cast(bytes_written / 2), - .is_finished_writing = bytes_written == 0, - }}; + return OutputInfo{ + .samples_written = static_cast(samples_written / 2), + .is_stream_finished = samples_written == 0, + }; } -auto XiphOpusDecoder::SeekStream(cpp::span input, - std::size_t target_sample) -> Result { +auto XiphOpusDecoder::SeekTo(size_t target) -> cpp::result { return {}; } -auto XiphOpusDecoder::ReadCallback() -> cpp::span { - return input_.subspan(pos_in_input_); -} - -auto XiphOpusDecoder::AfterReadCallback(size_t bytes_read) -> void { - pos_in_input_ += bytes_read; -} - } // namespace codecs -- cgit v1.2.3