parent
9f8cfaa7a8
commit
9176ef1872
@ -1,6 +1,7 @@ |
||||
idf_component_register( |
||||
SRCS "audio_decoder.cpp" "fatfs_audio_input.cpp" "audio_task.cpp" |
||||
SRCS "audio_decoder.cpp" "audio_task.cpp" "fatfs_audio_input.cpp" "chunk.cpp" |
||||
"i2s_audio_output.cpp" "stream_info.cpp" |
||||
INCLUDE_DIRS "include" |
||||
REQUIRES "codecs" "drivers") |
||||
REQUIRES "codecs" "drivers" "cbor") |
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) |
||||
|
@ -0,0 +1,115 @@ |
||||
#include "chunk.hpp" |
||||
|
||||
#include "cbor_encoder.hpp" |
||||
#include "cbor_decoder.hpp" |
||||
#include <string.h> |
||||
#include <cstdint> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
#include "stream_message.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
/*
|
||||
* The maximum size that we expect a header to take up. |
||||
*/ |
||||
// TODO: tune this.
|
||||
static const size_t kMaxHeaderSize = 64; |
||||
|
||||
auto WriteChunksToStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeWriteResult { |
||||
while (1) { |
||||
// First, ask the callback for some data to write.
|
||||
size_t chunk_size = |
||||
callback( |
||||
working_buffer + kMaxHeaderSize, |
||||
working_buffer_length - kMaxHeaderSize); |
||||
|
||||
if (chunk_size == 0) { |
||||
// They had nothing for us, so bail out.
|
||||
return CHUNK_OUT_OF_DATA; |
||||
} |
||||
|
||||
// Put together a header.
|
||||
cbor::Encoder encoder(cbor::CONTAINER_ARRAY, 3, working_buffer, working_buffer_length); |
||||
encoder.WriteUnsigned(TYPE_CHUNK_HEADER); |
||||
// Note here that we need to write the offset of the chunk into the header.
|
||||
// We could be smarter here and write the actual header size, allowing us to
|
||||
// pack slightly more data into each message, but this is hard so I haven't
|
||||
// done it. Please make my code better for me.
|
||||
encoder.WriteUnsigned(kMaxHeaderSize); |
||||
encoder.WriteUnsigned(chunk_size); |
||||
if (encoder.Finish().has_error()) { |
||||
return CHUNK_ENCODING_ERROR; |
||||
}; |
||||
|
||||
// Try to write to the buffer. Note the return type here will be either 0 or
|
||||
// kMaxHeaderSize + chunk_size, as MessageBuffer doesn't allow partial
|
||||
// writes.
|
||||
size_t actual_write_size = |
||||
xMessageBufferSend( |
||||
*stream, working_buffer, kMaxHeaderSize + chunk_size, max_wait); |
||||
|
||||
if (actual_write_size == 0) { |
||||
// We failed to write in time, so bail out. This is techinically data loss
|
||||
// unless the caller wants to go and parse our working buffer, but we
|
||||
// assume the caller has a good reason to time us out.
|
||||
return CHUNK_WRITE_TIMEOUT; |
||||
} |
||||
} |
||||
} |
||||
|
||||
auto ReadChunksFromStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeReadResult { |
||||
// Spillover if the previous iteration did not consume all of the input.
|
||||
size_t leftover_bytes = 0; |
||||
while (1) { |
||||
// First, wait for a message to arrive over the buffer.
|
||||
size_t read_size = |
||||
xMessageBufferReceive( |
||||
*stream, working_buffer + leftover_bytes, working_buffer_length - leftover_bytes, max_wait); |
||||
|
||||
if (read_size == 0) { |
||||
return CHUNK_READ_TIMEOUT; |
||||
} |
||||
|
||||
auto decoder = cbor::MapDecoder::Create(working_buffer + leftover_bytes, read_size); |
||||
if (decoder.has_error()) { |
||||
// Weird; this implies someone is shoving invalid data into the buffer.
|
||||
return CHUNK_DECODING_ERROR; |
||||
} |
||||
|
||||
MessageType type = decoder.value().ParseUnsigned().value_or(TYPE_UNKNOWN); |
||||
if (type != TYPE_CHUNK_HEADER) { |
||||
// This message wasn't for us, so put it in a consistent place and let the
|
||||
// caller handle it.
|
||||
memmove(working_buffer, working_buffer + leftover_bytes, read_size); |
||||
return CHUNK_STREAM_ENDED; |
||||
} |
||||
|
||||
// Work the size and position of the chunk (don't assume it's at
|
||||
// kMaxHeaderSize offset for future-proofing).
|
||||
header_length = decoder.ParseUnsigned().value_or(0); |
||||
chunk_length = decoder.ParseUnsigned().value_or(0); |
||||
if (decoder.Failed()) { |
||||
return CHUNK_DECODING_ERROR; |
||||
} |
||||
|
||||
// Now we need to stick the end of the last chunk (if it exists) onto the
|
||||
// front of the new chunk. Do it this way around bc we assume the old chunk
|
||||
// is shorter, and therefore faster to move.
|
||||
uint8_t *combined_buffer = working_buffer + header_length - leftover_bytes; |
||||
size_t combined_buffer_size = leftover_bytes + chunk_length; |
||||
if (leftover_bytes > 0) { |
||||
memmove(combined_buffer, working_buffer, leftover_bytes); |
||||
} |
||||
|
||||
// Tell the callback about the new data.
|
||||
size_t amount_processed = callback(combined_buffer, combined_buffer_size); |
||||
|
||||
// Prepare for the next iteration.
|
||||
leftover_bytes = combined_buffer_size - amount_processed; |
||||
if (leftover_bytes > 0) { |
||||
memmove(working_buffer, combined_buffer + amount_processed, leftover_bytes); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,57 @@ |
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <optional> |
||||
#include <string> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
#include "freertos/portmacro.h" |
||||
#include "result.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
enum ChunkWriteResult { |
||||
// Returned when the callback does not write any data.
|
||||
CHUNK_OUT_OF_DATA, |
||||
// Returned when there is an error encoding a chunk header using cbor.
|
||||
CHUNK_ENCODING_ERROR, |
||||
// Returned when max_wait expires without room in the stream buffer becoming
|
||||
// available.
|
||||
CHUNK_WRITE_TIMEOUT, |
||||
}; |
||||
|
||||
/*
|
||||
* Invokes the given callback to receive data, breaks the received data up into |
||||
* chunks with headers, and writes those chunks to the given output stream. |
||||
* |
||||
* The callback will be invoked with a byte buffer and its size. The callback |
||||
* should write as much data as it can to this buffer, and then return the |
||||
* number of bytes it wrote. Return a value of 0 to indicate that there is no |
||||
* more input to read. |
||||
*/ |
||||
auto WriteChunksToStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeWriteResult; |
||||
|
||||
enum ChunkReadResult { |
||||
// Returned an error in parsing the cbor-encoded header.
|
||||
CHUNK_DECODING_ERROR, |
||||
// Returned when max_wait expired before any data was read.
|
||||
CHUNK_READ_TIMEOUT, |
||||
// Returned when a non-chunk message is received.
|
||||
CHUNK_STREAM_ENDED, |
||||
}; |
||||
|
||||
/*
|
||||
* Reads chunks of data from the given input stream, and invokes the given |
||||
* callback to process each of them in turn. |
||||
*
|
||||
* The callback will be invoked with a byte buffer and its size. The callback |
||||
* should process as much data as it can from this buffer, and then return the |
||||
* number of bytes it was able to read. Any leftover bytes will be added as a |
||||
* prefix to the next chunk. |
||||
* |
||||
* If this function encounters a message in the stream that is not a chunk, it |
||||
* will place the message at the start of the working_buffer and then return. |
||||
*/ |
||||
auto ReadChunksFromStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeReadResult; |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,44 @@ |
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
#include <optional> |
||||
#include <string> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
#include "result.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
class StreamInfo { |
||||
public: |
||||
enum ParseError { |
||||
WRONG_TYPE, |
||||
MISSING_MAP, |
||||
}; |
||||
|
||||
static auto Create(const uint8_t *buffer, size_t length) -> cpp::result<StreamInfo, ParseError>; |
||||
StreamInfo(CborValue& map); |
||||
|
||||
StreamInfo() = default; |
||||
StreamInfo(const StreamInfo&) = default; |
||||
|
||||
~StreamInfo() = default; |
||||
|
||||
auto Path() const -> const std::optional<std::string>& { return path_; } |
||||
auto Channels() const -> const std::optional<uint8_t>& { return channels_; } |
||||
auto BitsPerSample() const -> const std::optional<uint8_t>& { return bits_per_sample_; } |
||||
auto SampleRate() const -> const std::optional<uint16_t>& { return sample_rate_; } |
||||
|
||||
enum EncodeError { |
||||
OUT_OF_MEMORY, |
||||
}; |
||||
|
||||
auto WriteToStream(CborEncoder encoder) -> cpp::result<void, EncodeError>; |
||||
private: |
||||
|
||||
std::optional<std::string> path_; |
||||
std::optional<uint8_t> channels_; |
||||
std::optional<uint8_t> bits_per_sample_; |
||||
std::optional<uint16_t> sample_rate_; |
||||
}; |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,11 @@ |
||||
#pragma once |
||||
|
||||
namespace audio { |
||||
|
||||
enum MessageType { |
||||
TYPE_UNKNOWN, |
||||
TYPE_CHUNK_HEADER, |
||||
TYPE_STREAM_INFO, |
||||
}; |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,90 @@ |
||||
#include "stream_info.hpp" |
||||
#include "stream_message.hpp" |
||||
#include <cstdint> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
|
||||
namespace audio { |
||||
|
||||
static const char* kKeyPath = "p"; |
||||
static const char* kKeyChannels = "c"; |
||||
static const char* kKeyBitsPerSample = "b"; |
||||
static const char* kKeySampleRate = "r"; |
||||
|
||||
static auto find_uint64(CborValue &map, char *key) -> cpp::optional<uint64_t> { |
||||
CborValue val; |
||||
cbor_value_map_find_value(&map, key, &val); |
||||
if (cbor_value_is_unsigned_integer(&val)) { |
||||
uint64_t raw_val; |
||||
cbor_value_get_uint64(&val, &raw_val); |
||||
return raw_val; |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
|
||||
static auto write_uint64(CborEncoder &map, const char *key, const optional<uint64_t> &val) -> cpp::result<void, StreamInfo::EncodeError> { |
||||
if (val) { |
||||
cbor_encode_byte_string(&map, key, 1); |
||||
cbor_encode_uint(&map, *val); |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
static auto StreamInfo::Create(const uint8_t *buffer, size_t length) -> cpp::result<StreamInfo, ParseError> { |
||||
CborParser parser; |
||||
CborValue value; |
||||
|
||||
cbor_parser_init(buffer, len, 0, &parser, &value); |
||||
|
||||
uint8_t type = 0; |
||||
if (!cbor_value_is_integer(&value) |
||||
|| !cbor_value_get_integer(&value, &type) |
||||
|| type != STREAM_INFO) { |
||||
return cpp::fail(WRONG_TYPE); |
||||
} |
||||
|
||||
cbor_value_advance_fixed(&value); |
||||
|
||||
if (!cbor_value_is_map(&value)) { |
||||
return cpp::fail(MISSING_MAP); |
||||
} |
||||
|
||||
return StreamInfo(value); |
||||
} |
||||
|
||||
StreamInfo::StreamInfo(CborValue& map) { |
||||
// TODO: this method is n^2, which seems less than ideal. But you don't do it
|
||||
// that frequently, so maybe it's okay? Needs investigation.
|
||||
channels_ = find_uint64(map, kKeyChannels); |
||||
bits_per_sample_ = find_uint64(map, kKeyBitsPerSample); |
||||
sample_rate_ = find_uint64(map, kKeySampleRate); |
||||
|
||||
CborValue val; |
||||
cbor_value_map_find_value(&map, kKeyPath, &val); |
||||
if (cbor_value_is_text_string(&val)) { |
||||
size_t len; |
||||
char *str; |
||||
cbor_value_dup_text_string(&val, &str, &len, &val); |
||||
path_ = std::string(str, len); |
||||
free(str); |
||||
} |
||||
} |
||||
|
||||
auto StreamInfo::WriteToStream(CborEncoder encoder) -> cpp::result<void, EncodeError> { |
||||
cbor_encode_int(&encoder, STREAM_INFO); |
||||
|
||||
CborEncoder map; |
||||
cbor_encoder_create_map(&encoder, &map, length); |
||||
|
||||
write_uint64(&map, kKeyChannels, channels_); |
||||
write_uint64(&map, kKeyBitsPerSample, bits_per_sample_); |
||||
write_uint64(&map, kKeySampleRate, sample_rate_); |
||||
|
||||
if (path_) { |
||||
cbor_encode_text_string(&map, path_->c_str(), path_->size()); |
||||
} |
||||
|
||||
cbor_encoder_close_container(&encoder, &map); |
||||
} |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,6 @@ |
||||
idf_component_register( |
||||
SRCS "cbor_decoder.cpp" "cbor_encoder.cpp" |
||||
INCLUDE_DIRS "include" |
||||
REQUIRES "cbor" "result") |
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) |
@ -0,0 +1,158 @@ |
||||
#include "cbor_decoder.hpp" |
||||
#include <cstdint> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
#include "include/cbor_decoder.hpp" |
||||
|
||||
namespace cbor { |
||||
|
||||
static auto ArrayDecoder::Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<ArrayDecoder>, CborError> { |
||||
auto decoder = std::make_unique<ArrayDecoder>(); |
||||
cbor_parser_init(buffer, buffer_len, &decoder->parser_, &decoder->root_); |
||||
if (!cbor_value_is_array(&decoder->root_)) { |
||||
return cpp::fail(CborErrorIllegalType); |
||||
} |
||||
CborError err = cbor_value_enter_container(&decoder->root_, &decoder->it_); |
||||
if (err != CborNoError) { |
||||
return cpp::fail(err); |
||||
} |
||||
return std::move(decoder); |
||||
} |
||||
|
||||
|
||||
auto ArrayDecoder::ParseString() -> cpp::result<std::string, CborError> { |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
|
||||
if (!cbor_value_is_byte_string(&it_)) { |
||||
error_ = CborErrorIllegalType; |
||||
return cpp::fail(error_); |
||||
} |
||||
uint8_t *buf; size_t len; CborValue new_val; |
||||
error_ = cbor_value_dup_byte_string(&it_, &buf, &len, &new_val); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
std::string ret(buf, len); |
||||
free(buf); |
||||
val_ = new_val; |
||||
return ret; |
||||
} |
||||
|
||||
auto ArrayDecoder::ParseUnsigned() -> cpp::result<uint32_t, CborError> { |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
|
||||
if (!cbor_value_is_unsigned_integer(&it_)) { |
||||
error_ = CborErrorIllegalType; |
||||
return cpp::fail(error_); |
||||
} |
||||
uint64_t ret; |
||||
error_ = cbor_value_get_uint64(&it_, &ret); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
error_ = cbor_value_advance(&it_); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
auto ArrayDecoder::ParseSigned() -> cpp::result<int32_t, CborError> { |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
if (!cbor_value_is_unsigned_integer(&it_)) { |
||||
error_ = CborErrorIllegalType; |
||||
return cpp::fail(error_); |
||||
} |
||||
uint64_t ret; |
||||
error_ = cbor_value_get_uint64(&it_, &ret); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
error_ = cbor_value_advance(&it_); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
static auto MapDecoder::Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<MapDecoder>, CborError> { |
||||
auto decoder = std::make_unique<MapDecoder>(); |
||||
cbor_parser_init(buffer, buffer_len, &decoder->parser_, &decoder->root_); |
||||
if (!cbor_value_is_map(&decoder->root_)) { |
||||
return cpp::fail(CborErrorIllegalType); |
||||
} |
||||
CborError err = cbor_value_enter_container(&decoder->root_, &decoder->it_); |
||||
if (err != CborNoError) { |
||||
return cpp::fail(err); |
||||
} |
||||
return std::move(decoder); |
||||
} |
||||
|
||||
auto MapDecoder::FindString(const std::string &key) -> std::optional<std::string> { |
||||
CborValue val; |
||||
if (error_ != CborNoError) { |
||||
return {}; |
||||
} |
||||
if (cbor_value_map_find_value(&it_, key.c_str(), &val) != CborNoError) { |
||||
return {}; |
||||
} |
||||
if (!cbor_value_is_byte_string(&val)) { |
||||
error_ = CborErrorIllegalType; |
||||
return {}; |
||||
} |
||||
uint8_t *buf; size_t len; |
||||
error_ = cbor_value_dup_byte_string(&val, &buf, &len, NULL); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
std::string ret(buf, len); |
||||
free(buf); |
||||
return ret; |
||||
} |
||||
|
||||
auto MapDecoder::FindUnsigned(const std::string &key) -> std::optional<uint32_t> { |
||||
CborValue val; |
||||
if (error_ != CborNoError) { |
||||
return {}; |
||||
} |
||||
if (cbor_value_map_find_value(&it_, key.c_str(), &val) != CborNoError) { |
||||
return {}; |
||||
} |
||||
if (!cbor_value_is_unsigned_integer(&val)) { |
||||
error_ = CborErrorIllegalType; |
||||
return {}; |
||||
} |
||||
uint64_t ret; |
||||
error_ = cbor_value_get_uint64(&val, &ret); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
auto MapDecoder::FindSigned(const std::string &key) -> std::optional<int32_t> { |
||||
CborValue val; |
||||
if (error_ != CborNoError) { |
||||
return {}; |
||||
} |
||||
if (cbor_value_map_find_value(&it_, key.c_str(), &val) != CborNoError) { |
||||
return {}; |
||||
} |
||||
if (!cbor_value_is_integer(&val)) { |
||||
error_ = CborErrorIllegalType; |
||||
return {}; |
||||
} |
||||
int32_t ret; |
||||
error_ = cbor_value_get_int(&val, &ret); |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
} // namespace cbor
|
@ -0,0 +1,53 @@ |
||||
#include "cbor_encoder.hpp" |
||||
#include <cstdint> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
|
||||
namespace cbor { |
||||
|
||||
static const int kEncoderFlags = 0; |
||||
|
||||
Encoder::Encoder(ContainerType type, uint32_t container_len, uint8_t *buffer, size_t buffer_len) { |
||||
cbor_encoder_init(&root_encoder, buffer, buffer_len, kEncoderFlags); |
||||
switch (type) { |
||||
case CONTAINER_ARRAY: |
||||
error_ = cbor_encoder_create_array(&encoder, &container_encoder_, container_len); |
||||
break; |
||||
case CONTAINER_MAP: |
||||
error_ = cbor_encoder_create_map(&encoder, &container_encoder_, container_len); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
auto Encoder::WriteString(const std::string &val) -> void { |
||||
if (error_ != CborNoError) { |
||||
return; |
||||
} |
||||
error_ = cbor_encode_byte_string(&container_encoder_, val.c_str(), val.size()); |
||||
} |
||||
|
||||
auto Encoder::WriteUnsigned(uint32_t val) -> void { |
||||
if (error_ != CborNoError) { |
||||
return; |
||||
} |
||||
error_ = cbor_encode_uint(&container_encoder_, val); |
||||
} |
||||
|
||||
auto Encoder::WriteSigned(int32_t val) -> void { |
||||
if (error_ != CborNoError) { |
||||
return; |
||||
} |
||||
error_ = cbor_encode_int(&container_encoder_, val); |
||||
} |
||||
|
||||
auto Encoder::Finish() -> cpp::result<size_t, CborError> { |
||||
if (error_ != CborNoError) { |
||||
return cpp::fail(error_); |
||||
} |
||||
if (CborError final_error = cbor_encoder_close_container(&root_encoder, &container_encoder_) != CborNoError) { |
||||
return cpp::fail(final_error); |
||||
} |
||||
return cbor_encoder_get_buffer_size(&root_encoder); |
||||
} |
||||
|
||||
} // namespace cbor
|
||||
|
@ -0,0 +1,47 @@ |
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
namespace cbor { |
||||
|
||||
class ArrayDecoder { |
||||
public: |
||||
static auto Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<ArrayDecoder>, CborError>; |
||||
|
||||
auto ParseString() -> cpp::result<std::string, CborError>; |
||||
auto ParseUnsigned() -> cpp::result<uint32_t, CborError>; |
||||
auto ParseSigned() -> cpp::result<int32_t, CborError>; |
||||
|
||||
auto Failed() -> CborError { return error_; } |
||||
|
||||
ArrayDecoder(const ArrayDecoder&) = delete; |
||||
ArrayDecoder& operator=(const ArrayDecoder&) = delete; |
||||
private: |
||||
CborParser parser_; |
||||
CborValue root_; |
||||
|
||||
CborValue it_; |
||||
CborError error_ = CborNoError; |
||||
}; |
||||
|
||||
class MapDecoder { |
||||
public: |
||||
static auto Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<MapDecoder>, CborError>; |
||||
|
||||
auto FindString(const std::string &key) -> std::optional<std::string>; |
||||
auto FindUnsigned(const std::string &key) -> std::optional<uint32_t>; |
||||
auto FindSigned(const std::string &key) -> std::optional<int32_t>; |
||||
|
||||
auto Failed() -> CborError { return error_; } |
||||
|
||||
MapDecoder(const MapDecoder&) = delete; |
||||
MapDecoder& operator=(const MapDecoder&) = delete; |
||||
private: |
||||
CborParser parser_; |
||||
CborValue root_; |
||||
|
||||
CborValue it_; |
||||
CborError error_ = CborNoError; |
||||
}; |
||||
|
||||
|
||||
} // namespace cbor
|
@ -0,0 +1,30 @@ |
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
#include "esp-idf/components/cbor/tinycbor/src/cbor.h" |
||||
namespace cbor { |
||||
|
||||
class Encoder { |
||||
public: |
||||
enum ContainerType { |
||||
CONTAINER_ARRAY, |
||||
CONTAINER_MAP |
||||
}; |
||||
Encoder(ContainerType type, uint32_t container_len, uint8_t *buffer, size_t buffer_len); |
||||
|
||||
auto WriteString(const std::string &val) -> void; |
||||
auto WriteUnsigned(uint32_t val) -> void; |
||||
auto WriteSigned(int32_t val) -> void; |
||||
|
||||
auto Finish() -> cpp::result<size_t, CborError>; |
||||
|
||||
Encoder(const Encoder&) = delete; |
||||
Encoder& operator=(const Encoder&) = delete; |
||||
private: |
||||
CborEncoder root_encoder_; |
||||
CborEncoder container_encoder_; |
||||
|
||||
CborError error_ = CborNoError; |
||||
}; |
||||
|
||||
} // namespace cbor
|
@ -0,0 +1,12 @@ |
||||
#pragma once |
||||
|
||||
#include <string> |
||||
|
||||
namespace codecs { |
||||
|
||||
enum StreamType { |
||||
STREAM_MP3, |
||||
}; |
||||
|
||||
auto GetStreamTypeFromFilename(std::string filename); |
||||
} |
@ -1,6 +1,6 @@ |
||||
idf_component_register( |
||||
SRCS "dac.cpp" "gpio_expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp" |
||||
"audio_playback.cpp" "i2s_audio_output.cpp" "display.cpp" "display_init.cpp" "spi.cpp" |
||||
"spi.cpp" "display.cpp" "display_init.cpp" |
||||
INCLUDE_DIRS "include" |
||||
REQUIRES "esp_adc_cal" "fatfs" "result" "lvgl") |
||||
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) |
||||
|
Loading…
Reference in new issue