summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-08-08 23:14:42 +1000
committerjacqueline <me@jacqueline.id.au>2023-08-08 23:14:42 +1000
commit1b6811663caf07717ce15f3d3bbb1195397a1a33 (patch)
tree2babe8ab0769d97cd8a785b26c3dfc499a6924d8 /src
parent6c3501dbcbd1095293d8a4d4b83311e94a7df9a8 (diff)
downloadtangara-fw-1b6811663caf07717ce15f3d3bbb1195397a1a33.tar.gz
Add libogg for handling opus streams reasonably
Diffstat (limited to 'src')
-rw-r--r--src/codecs/CMakeLists.txt11
-rw-r--r--src/codecs/include/ogg.hpp36
-rw-r--r--src/codecs/include/opus.hpp3
-rw-r--r--src/codecs/ogg.cpp0
-rw-r--r--src/codecs/opus.cpp66
5 files changed, 83 insertions, 33 deletions
diff --git a/src/codecs/CMakeLists.txt b/src/codecs/CMakeLists.txt
index f84c46a3..e30dce06 100644
--- a/src/codecs/CMakeLists.txt
+++ b/src/codecs/CMakeLists.txt
@@ -9,6 +9,15 @@ idf_component_register(
target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS})
+set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
+
+set(INSTALL_DOCS OFF)
+set(INSTALL_PKG_CONFIG_MODULE OFF)
+set(INSTALL_CMAKE_PACKAGE_MODULE OFF)
+
+add_subdirectory($ENV{PROJ_PATH}/lib/ogg ${CMAKE_CURRENT_BINARY_DIR}/ogg)
+target_link_libraries(${COMPONENT_LIB} PUBLIC ogg)
+
set(OPUS_FIXED_POINT ON)
set(OPUS_ENABLE_FLOAT_API OFF)
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
@@ -16,7 +25,5 @@ set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
set(OPUS_BUILD_TESTING OFF)
set(OPUS_BUILD_SHARED_LIBS OFF)
-set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
-
add_subdirectory($ENV{PROJ_PATH}/lib/opus ${CMAKE_CURRENT_BINARY_DIR}/opus)
target_link_libraries(${COMPONENT_LIB} PUBLIC opus)
diff --git a/src/codecs/include/ogg.hpp b/src/codecs/include/ogg.hpp
new file mode 100644
index 00000000..2d6ea8c5
--- /dev/null
+++ b/src/codecs/include/ogg.hpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <sys/_stdint.h>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+
+#include "ogg/ogg.h"
+#include "span.hpp"
+
+namespace codecs {
+
+class OggContainer {
+ public:
+ OggContainer();
+ ~OggContainer();
+
+ auto AddBytes(cpp::span<const std::byte>) -> void;
+ auto HasNextPacket() -> bool;
+ auto NextPacket() -> cpp::span<uint8_t>;
+ auto PeekPacket() -> cpp::span<uint8_t>;
+
+ private:
+ ogg_sync_state sync_;
+ ogg_stream_state stream_;
+ ogg_page page_;
+ ogg_packet packet_;
+};
+
+} // namespace codecs \ No newline at end of file
diff --git a/src/codecs/include/opus.hpp b/src/codecs/include/opus.hpp
index f824a7cb..50717b73 100644
--- a/src/codecs/include/opus.hpp
+++ b/src/codecs/include/opus.hpp
@@ -13,6 +13,8 @@
#include <string>
#include <utility>
+#include "ogg.hpp"
+#include "ogg/ogg.h"
#include "opus.h"
#include "sample.hpp"
#include "span.hpp"
@@ -44,6 +46,7 @@ class XiphOpusDecoder : public ICodec {
-> Result<void> override;
private:
+ OggContainer ogg_;
OpusDecoder* opus_;
cpp::span<int16_t> sample_buffer_;
int32_t pos_in_buffer_;
diff --git a/src/codecs/ogg.cpp b/src/codecs/ogg.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/codecs/ogg.cpp
diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp
index 791c8e74..2529d9ec 100644
--- a/src/codecs/opus.cpp
+++ b/src/codecs/opus.cpp
@@ -18,6 +18,7 @@
#include "codec.hpp"
#include "esp_log.h"
+#include "ogg/ogg.h"
#include "opus.h"
#include "opus_types.h"
#include "result.hpp"
@@ -32,11 +33,7 @@ static constexpr char kTag[] = "opus";
// 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);
-
+XiphOpusDecoder::XiphOpusDecoder() : opus_(nullptr) {
pos_in_buffer_ = 0;
sample_buffer_ = {reinterpret_cast<opus_int16*>(
heap_caps_calloc(kSampleBufferSize, sizeof(opus_int16),
@@ -44,23 +41,36 @@ XiphOpusDecoder::XiphOpusDecoder() {
kSampleBufferSize};
}
XiphOpusDecoder::~XiphOpusDecoder() {
- opus_decoder_destroy(opus_);
+ if (opus_ != nullptr) {
+ opus_decoder_destroy(opus_);
+ }
heap_caps_free(sample_buffer_.data());
}
auto XiphOpusDecoder::BeginStream(const cpp::span<const std::byte> input)
-> Result<OutputFormat> {
- return {0, OutputFormat{
- .num_channels = 2,
- .sample_rate_hz = 48000,
- }};
-}
+ ogg_.AddBytes(input);
+ if (!ogg_.HasNextPacket()) {
+ return {input.size(), cpp::fail(Error::kOutOfInput)};
+ }
+ auto packet = ogg_.NextPacket();
+ int num_channels = opus_packet_get_nb_channels(packet.data());
+ if (num_channels > 2) {
+ // Too many channels; we can't handle this.
+ // TODO: better error
+ return {input.size(), cpp::fail(Error::kMalformedData)};
+ }
-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);
+ int err;
+ opus_ = opus_decoder_create(48000, num_channels, &err);
+ if (err != OPUS_OK) {
+ return {input.size(), cpp::fail(Error::kInternalError)};
+ }
+
+ return {input.size(), OutputFormat{
+ .num_channels = static_cast<uint8_t>(num_channels),
+ .sample_rate_hz = 48000,
+ }};
}
auto XiphOpusDecoder::ContinueStream(cpp::span<const std::byte> input,
@@ -69,26 +79,20 @@ auto XiphOpusDecoder::ContinueStream(cpp::span<const std::byte> input,
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)};
+ if (!ogg_.HasNextPacket()) {
+ bytes_used = input.size();
+ ogg_.AddBytes(input);
}
- 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)};
+ if (!ogg_.HasNextPacket()) {
+ return {bytes_used, 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;
+ auto packet = ogg_.NextPacket();
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);
+ samples_in_buffer_ =
+ opus_decode(opus_, packet.data(), packet.size_bytes(),
+ sample_buffer_.data(), sample_buffer_.size(), 0);
if (samples_in_buffer_ < 0) {
ESP_LOGE(kTag, "error decoding stream");