mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 20:17:04 +03:00
update cspot
This commit is contained in:
@@ -8,7 +8,6 @@ idf_component_register(
|
||||
LDFRAGMENTS "linker.lf"
|
||||
)
|
||||
|
||||
add_definitions(-DBELL_USE_MBEDTLS)
|
||||
add_definitions(-Wno-unused-variable -Wno-unused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation)
|
||||
|
||||
set(BELL_DISABLE_CODECS ON)
|
||||
|
||||
@@ -116,6 +116,7 @@ static void cspotTask(void *pvParameters) {
|
||||
spircController = std::make_shared<SpircController>(mercuryManager, cspot.blob->username, audioSink);
|
||||
|
||||
spircController->setEventHandler([](CSpotEvent &event) {
|
||||
ESP_LOGI(TAG, "Getting Spotify event %d ", (int) event.eventType);
|
||||
switch (event.eventType) {
|
||||
case CSpotEventType::TRACK_INFO: {
|
||||
TrackInfo track = std::get<TrackInfo>(event.data);
|
||||
@@ -293,18 +294,18 @@ bool NVSFile::flush() {
|
||||
* Shim HTTP server for spirc
|
||||
*/
|
||||
static esp_err_t handlerWrapper(httpd_req_t *req) {
|
||||
bell::HTTPRequest request = { };
|
||||
std::unique_ptr<bell::HTTPRequest> request = std::make_unique<bell::HTTPRequest>();
|
||||
char *query = NULL, *body = NULL;
|
||||
bell::httpHandler *handler = (bell::httpHandler*) req->user_ctx;
|
||||
size_t query_len = httpd_req_get_url_query_len(req);
|
||||
|
||||
request.connection = httpd_req_to_sockfd(req);
|
||||
request->connection = httpd_req_to_sockfd(req);
|
||||
|
||||
// get body if any (add '\0' at the end if used as string)
|
||||
if (req->content_len) {
|
||||
body = (char*) calloc(1, req->content_len + 1);
|
||||
int size = httpd_req_recv(req, body, req->content_len);
|
||||
request.body = body;
|
||||
request->body = body;
|
||||
ESP_LOGD(TAG,"wrapper received body %d/%d", size, req->content_len);
|
||||
}
|
||||
|
||||
@@ -324,7 +325,7 @@ static esp_err_t handlerWrapper(httpd_req_t *req) {
|
||||
while (key) {
|
||||
char *value = strchr(key, '=');
|
||||
*value++ = '\0';
|
||||
request.queryParams[key] = value;
|
||||
request->queryParams[key] = value;
|
||||
ESP_LOGD(TAG,"wrapper received key:%s value:%s", key, value);
|
||||
key = strtok(NULL, "&");
|
||||
};
|
||||
@@ -337,12 +338,12 @@ static esp_err_t handlerWrapper(httpd_req_t *req) {
|
||||
and then we'll return. So we can't obtain the response to be sent, as esp_http_server
|
||||
normally expects, instead respond() will use raw socket and close connection
|
||||
*/
|
||||
(*handler)(request);
|
||||
(*handler)(std::move(request));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ShimHTTPServer::registerHandler(bell::RequestType requestType, const std::string &routeUrl, bell::httpHandler handler) {
|
||||
void ShimHTTPServer::registerHandler(bell::RequestType requestType, const std::string &routeUrl, bell::httpHandler handler, bool readDataToStr) {
|
||||
httpd_uri_t request = {
|
||||
.uri = routeUrl.c_str(),
|
||||
.method = (requestType == bell::RequestType::GET ? HTTP_GET : HTTP_POST),
|
||||
|
||||
@@ -44,7 +44,6 @@ private:
|
||||
|
||||
public:
|
||||
ShimHTTPServer(httpd_handle_t server, int port) { serverHandle = server; serverPort = port; }
|
||||
|
||||
void registerHandler(bell::RequestType requestType, const std::string &, bell::httpHandler);
|
||||
void registerHandler(bell::RequestType requestType, const std::string &, bell::httpHandler, bool readDataToStr = false);
|
||||
void respond(const bell::HTTPResponse &);
|
||||
};
|
||||
|
||||
@@ -6,11 +6,21 @@ project(cspot)
|
||||
set(CSPOT_EXTERNAL_BELL "" CACHE STRING "External bell library target name, optional")
|
||||
|
||||
# CMake options
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_definitions(NOMINMAX _WINSOCK_DEPRECATED_NO_WARNINGS _CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(/wd4068 /wd4244 /wd4018 /wd4101 /wd4102 /wd4142 /wd4996)
|
||||
endif()
|
||||
|
||||
# Main library sources
|
||||
file(GLOB SOURCES "src/*.cpp" "src/*.c")
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES "mdnssvc/mdns.c" "mdnssvc/mdnsd.c")
|
||||
list(APPEND EXTRA_INCLUDES "mdnssvc")
|
||||
endif()
|
||||
|
||||
# Use externally specified bell library or the submodule
|
||||
if(CSPOT_EXTERNAL_BELL)
|
||||
list(APPEND EXTRA_LIBS ${CSPOT_EXTERNAL_BELL})
|
||||
@@ -26,21 +36,15 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
# Build protobuf code
|
||||
#set(NANOPB_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
#file(GLOB PROTOS protobuf/*.proto)
|
||||
#nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS RELPATH ${CMAKE_CURRENT_SOURCE_DIR} ${PROTOS})
|
||||
#add_custom_target(generate_proto_sources DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
#set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS}
|
||||
# PROPERTIES GENERATED TRUE)
|
||||
|
||||
file(GLOB SOURCES "src/*.cpp" "src/*.c" "protobuf/*.c")
|
||||
message("BEWARE => NOT GENERATING PROTOBUF")
|
||||
set(GENERATED_INCLUDES ".")
|
||||
set(NANOPB_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
file(GLOB PROTOS protobuf/*.proto)
|
||||
nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS RELPATH ${CMAKE_CURRENT_SOURCE_DIR} ${PROTOS})
|
||||
add_custom_target(generate_proto_sources DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS} PROPERTIES GENERATED TRUE)
|
||||
|
||||
add_library(cspot STATIC ${SOURCES} ${PROTO_SRCS})
|
||||
# PUBLIC to propagate includes from bell to cspot dependents
|
||||
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC)
|
||||
target_compile_definitions(bell PUBLIC PB_FIELD_32BIT)
|
||||
target_link_libraries(cspot PUBLIC ${EXTRA_LIBS})
|
||||
#target_include_directories(cspot PUBLIC "include" ${CMAKE_CURRENT_BINARY_DIR} ${NANOPB_INCLUDE_DIRS})
|
||||
target_include_directories(cspot PUBLIC "include" ${GENERATED_INCLUDES} ${NANOPB_INCLUDE_DIRS})
|
||||
target_include_directories(cspot PUBLIC "include" ${CMAKE_CURRENT_BINARY_DIR} ${NANOPB_INCLUDE_DIRS} ${EXTRA_INCLUDES})
|
||||
|
||||
BIN
components/spotify/cspot/bell/.DS_Store
vendored
BIN
components/spotify/cspot/bell/.DS_Store
vendored
Binary file not shown.
126
components/spotify/cspot/bell/.gitignore
vendored
Normal file
126
components/spotify/cspot/bell/.gitignore
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/c,c++,cmake,macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=c,c++,cmake,macos
|
||||
|
||||
### C ###
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
### C++ ###
|
||||
# Prerequisites
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
|
||||
# Precompiled Headers
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
|
||||
# Executables
|
||||
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/c,c++,cmake,macos
|
||||
|
||||
build/
|
||||
3
components/spotify/cspot/bell/.gitmodules
vendored
3
components/spotify/cspot/bell/.gitmodules
vendored
@@ -5,3 +5,6 @@
|
||||
[submodule "cJSON"]
|
||||
path = cJSON
|
||||
url = https://github.com/DaveGamble/cJSON
|
||||
[submodule "nanopb"]
|
||||
path = nanopb
|
||||
url = https://github.com/nanopb/nanopb
|
||||
|
||||
@@ -4,79 +4,205 @@ cmake_policy(SET CMP0077 NEW)
|
||||
project(bell)
|
||||
|
||||
# Configurable options
|
||||
option(BELL_DISABLE_CODECS "Disable libhelix AAC and MP3 codecs" OFF)
|
||||
option(BELL_DISABLE_SINKS "Disable built-in audio sink implementations" OFF)
|
||||
option(BELL_USE_ALSA "Enable ALSA sink" OFF)
|
||||
option(BELL_USE_PORTAUDIO "Enable PortAudio sink" OFF)
|
||||
option(BELL_DISABLE_CODECS "Disable the entire audio codec wrapper" OFF)
|
||||
option(BELL_CODEC_AAC "Support libhelix-aac codec" ON)
|
||||
option(BELL_CODEC_MP3 "Support libhelix-mp3 codec" ON)
|
||||
option(BELL_CODEC_VORBIS "Support tremor Vorbis codec" ON)
|
||||
option(BELL_CODEC_ALAC "Support Apple ALAC codec" ON)
|
||||
option(BELL_CODEC_OPUS "Support Opus codec" ON)
|
||||
option(BELL_DISABLE_SINKS "Disable all built-in audio sink implementations" OFF)
|
||||
# These are default OFF, as they're OS-dependent (ESP32 sinks are always enabled - no external deps)
|
||||
option(BELL_SINK_ALSA "Enable ALSA audio sink" OFF)
|
||||
option(BELL_SINK_PORTAUDIO "Enable PortAudio sink" OFF)
|
||||
# cJSON wrapper
|
||||
option(BELL_DISABLE_CJSON "Disable cJSON and JSONObject completely" OFF)
|
||||
set(BELL_EXTERNAL_CJSON "" CACHE STRING "External cJSON library target name, optional")
|
||||
set(BELL_EXTERNAL_TREMOR "" CACHE STRING "External tremor library target name, optional")
|
||||
|
||||
if(BELL_EXTERNAL_MBEDTLS)
|
||||
set(MbedTLS_DIR ${BELL_EXTERNAL_MBEDTLS})
|
||||
message(STATUS "Setting local mbedtls ${MbedTLS_DIR}")
|
||||
endif()
|
||||
|
||||
# Backwards compatibility with deprecated options
|
||||
if(BELL_EXTERNAL_TREMOR)
|
||||
message(WARNING "Deprecated Bell options used, replace BELL_EXTERNAL_TREMOR with BELL_CODEC_VORBIS=OFF")
|
||||
set(BELL_CODEC_VORBIS OFF)
|
||||
endif()
|
||||
if(BELL_USE_ALSA)
|
||||
message(WARNING "Deprecated Bell options used, replace BELL_USE_ALSA with BELL_SINK_ALSA")
|
||||
set(BELL_SINK_ALSA ${BELL_USE_ALSA})
|
||||
endif()
|
||||
if(BELL_USE_PORTAUDIO)
|
||||
message(WARNING "Deprecated Bell options used, replace BELL_USE_PORTAUDIO with BELL_SINK_PORTAUDIO")
|
||||
set(BELL_SINK_PORTAUDIO ${BELL_USE_PORTAUDIO})
|
||||
endif()
|
||||
|
||||
message(STATUS "Bell options:")
|
||||
message(STATUS " Disable all codecs: ${BELL_DISABLE_CODECS}")
|
||||
if(NOT BELL_DISABLE_CODECS)
|
||||
message(STATUS " - AAC audio codec: ${BELL_CODEC_AAC}")
|
||||
message(STATUS " - MP3 audio codec: ${BELL_CODEC_MP3}")
|
||||
message(STATUS " - Vorbis audio codec: ${BELL_CODEC_VORBIS}")
|
||||
message(STATUS " - Opus audio codec: ${BELL_CODEC_OPUS}")
|
||||
message(STATUS " - ALAC audio codec: ${BELL_CODEC_ALAC}")
|
||||
endif()
|
||||
message(STATUS " Disable built-in audio sinks: ${BELL_DISABLE_SINKS}")
|
||||
if(NOT BELL_DISABLE_SINKS)
|
||||
message(STATUS " - ALSA sink: ${BELL_SINK_ALSA}")
|
||||
message(STATUS " - PortAudio sink: ${BELL_SINK_PORTAUDIO}")
|
||||
endif()
|
||||
message(STATUS " Disable cJSON and JSONObject: ${BELL_DISABLE_CJSON}")
|
||||
|
||||
# Include nanoPB library
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/nanopb/extra)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/nanopb/extra")
|
||||
find_package(Nanopb REQUIRED)
|
||||
list(APPEND EXTRA_INCLUDES ${NANOPB_INCLUDE_DIRS})
|
||||
|
||||
# CMake options
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
add_definitions(-DUSE_DEFAULT_STDLIB=1)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(AUDIO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/audio")
|
||||
add_definitions("-DUSE_DEFAULT_STDLIB=1")
|
||||
|
||||
# Main library sources
|
||||
file(GLOB SOURCES "src/*.cpp" "src/*.c" "nanopb/*.c")
|
||||
list(APPEND EXTRA_INCLUDES "include/platform")
|
||||
list(APPEND EXTRA_INCLUDES "include/audio/container")
|
||||
|
||||
# Add platform specific sources
|
||||
if(ESP_PLATFORM)
|
||||
file(GLOB ESP_PLATFORM_SOURCES "src/platform/esp/*.cpp" "src/platform/esp/*.c" "src/asm/biquad_f32_ae32.S")
|
||||
list(APPEND SOURCES ${ESP_PLATFORM_SOURCES})
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
file(GLOB UNIX_PLATFORM_SOURCES "src/platform/unix/*.cpp" "src/platform/linux/TLSSocket.cpp" "src/platform/unix/*.c")
|
||||
file(GLOB UNIX_PLATFORM_SOURCES "src/platform/unix/*.cpp" "src/platform/unix/*.c")
|
||||
list(APPEND SOURCES ${UNIX_PLATFORM_SOURCES})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
file(GLOB APPLE_PLATFORM_SOURCES "src/platform/apple/*.cpp" "src/platform/linux/TLSSocket.cpp" "src/platform/apple/*.c")
|
||||
file(GLOB APPLE_PLATFORM_SOURCES "src/platform/apple/*.cpp" "src/platform/apple/*.c")
|
||||
list(APPEND SOURCES ${APPLE_PLATFORM_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "/usr/local/opt/mbedtls@3/include")
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
file(GLOB LINUX_PLATFORM_SOURCES "src/platform/linux/*.cpp" "src/platform/linux/*.c")
|
||||
list(APPEND SOURCES ${LINUX_PLATFORM_SOURCES})
|
||||
endif()
|
||||
if(WIN32)
|
||||
file(GLOB WIN32_PLATFORM_SOURCES "src/platform/win32/*.cpp" "src/platform/win32/*.c")
|
||||
list(APPEND SOURCES ${WIN32_PLATFORM_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "include/platform/win32")
|
||||
endif()
|
||||
|
||||
# A hack to make Opus keep quiet
|
||||
function(message)
|
||||
if(NOT MESSAGE_QUIET)
|
||||
_message(${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(ESP_PLATFORM)
|
||||
# Use MBedTLS on ESP32
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoOpenSSL.cpp)
|
||||
idf_build_set_property(COMPILE_DEFINITIONS "-DBELL_USE_MBEDTLS" APPEND)
|
||||
list(APPEND EXTRA_LIBS idf::mbedtls idf::pthread idf::mdns)
|
||||
add_definitions(-Wunused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation)
|
||||
else()
|
||||
# Use OpenSSL elsewhere
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoMbedTLS.cpp)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
if(OPENSSL_FOUND)
|
||||
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
endif()
|
||||
list(APPEND EXTRA_LIBS OpenSSL::Crypto OpenSSL::SSL Threads::Threads)
|
||||
find_package(Threads REQUIRED)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
list(APPEND EXTRA_LIBS Threads::Threads)
|
||||
|
||||
find_package(MbedTLS REQUIRED)
|
||||
get_target_property(MBEDTLS_INFO MbedTLS::mbedtls INTERFACE_INCLUDE_DIRECTORIES)
|
||||
list(APPEND EXTRA_INCLUDES ${MBEDTLS_INFO})
|
||||
|
||||
# try to handle mbedtls when not system-wide installed
|
||||
if(BELL_EXTERNAL_MBEDTLS)
|
||||
if(MSVC)
|
||||
set(MBEDTLS_RELEASE "RELEASE" CACHE STRING "local mbedtls version")
|
||||
else()
|
||||
set(MBEDTLS_RELEASE "NOCONFIG" CACHE STRING "local mbedtls version")
|
||||
endif()
|
||||
message(STATUS "using local mbedtls version ${MBEDTLS_RELEASE}")
|
||||
get_target_property(MBEDTLS_INFO MbedTLS::mbedtls IMPORTED_LOCATION_${MBEDTLS_RELEASE})
|
||||
list(APPEND EXTRA_LIBS ${MBEDTLS_INFO})
|
||||
get_target_property(MBEDTLS_INFO MbedTLS::mbedx509 IMPORTED_LOCATION_${MBEDTLS_RELEASE})
|
||||
list(APPEND EXTRA_LIBS ${MBEDTLS_INFO})
|
||||
get_target_property(MBEDTLS_INFO MbedTLS::mbedcrypto IMPORTED_LOCATION_${MBEDTLS_RELEASE})
|
||||
list(APPEND EXTRA_LIBS ${MBEDTLS_INFO})
|
||||
else()
|
||||
list(APPEND EXTRA_LIBS mbedtls mbedcrypto mbedx509)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_compile_definitions(NOMINMAX _CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(/wd4068 /wd4244 /wd4018 /wd4101 /wd4102 /wd4142)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BELL_DISABLE_CODECS)
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/DecoderGlobals.cpp)
|
||||
else()
|
||||
file(GLOB LIBHELIX_AAC_SOURCES "libhelix-aac/*.c")
|
||||
file(GLOB LIBHELIX_MP3_SOURCES "libhelix-mp3/*.c")
|
||||
list(APPEND EXTRA_INCLUDES "libhelix-aac" "libhelix-mp3")
|
||||
list(APPEND SOURCES ${LIBHELIX_MP3_SOURCES} ${LIBHELIX_AAC_SOURCES})
|
||||
if(NOT BELL_DISABLE_CODECS)
|
||||
file(GLOB EXTRA_SOURCES "src/audio/container/*.cpp")
|
||||
list(APPEND SOURCES "${EXTRA_SOURCES}")
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/DecoderGlobals.cpp")
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/BaseCodec.cpp")
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/AudioCodecs.cpp")
|
||||
list(APPEND EXTRA_INCLUDES "include/audio/codec")
|
||||
# AAC-LC codec
|
||||
if(BELL_CODEC_AAC)
|
||||
file(GLOB LIBHELIX_AAC_SOURCES "libhelix-aac/*.c")
|
||||
list(APPEND LIBHELIX_SOURCES ${LIBHELIX_AAC_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "libhelix-aac")
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/AACDecoder.cpp")
|
||||
list(APPEND CODEC_FLAGS "-DBELL_CODEC_AAC")
|
||||
endif()
|
||||
# MP3 codec
|
||||
if(BELL_CODEC_MP3)
|
||||
file(GLOB LIBHELIX_MP3_SOURCES "libhelix-mp3/*.c")
|
||||
list(APPEND LIBHELIX_SOURCES ${LIBHELIX_MP3_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "libhelix-mp3")
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/MP3Decoder.cpp")
|
||||
list(APPEND CODEC_FLAGS "-DBELL_CODEC_MP3")
|
||||
endif()
|
||||
|
||||
# MP3 codec
|
||||
if(BELL_CODEC_ALAC)
|
||||
file(GLOB ALAC_SOURCES "alac/*.c" "alac/*.cpp")
|
||||
list(APPEND ALAC_SOURCES ${ALAC_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "alac")
|
||||
# list(APPEND SOURCES "${AUDIO_DIR}/codec/ALACDecoder.cpp")
|
||||
list(APPEND CODEC_FLAGS "-DBELL_CODEC_ALAC")
|
||||
endif()
|
||||
# libhelix Cygwin workaround
|
||||
if(CYGWIN)
|
||||
# Both Cygwin and ESP are Unix-like so this seems to work (or, at least, compile)
|
||||
set_source_files_properties(src/DecoderGlobals.cpp PROPERTIES COMPILE_FLAGS -DESP_PLATFORM)
|
||||
set_source_files_properties(${LIBHELIX_AAC_SOURCES} PROPERTIES COMPILE_FLAGS -DESP_PLATFORM)
|
||||
set_source_files_properties(${LIBHELIX_MP3_SOURCES} PROPERTIES COMPILE_FLAGS -DESP_PLATFORM)
|
||||
set_source_files_properties("${AUDIO_DIR}/codec/DecoderGlobals.cpp" ${LIBHELIX_SOURCES} PROPERTIES COMPILE_FLAGS "-DESP_PLATFORM")
|
||||
endif()
|
||||
list(APPEND SOURCES ${LIBHELIX_SOURCES})
|
||||
list(APPEND SOURCES ${ALAC_SOURCES})
|
||||
# Vorbis codec
|
||||
if(BELL_CODEC_VORBIS)
|
||||
file(GLOB TREMOR_SOURCES "tremor/*.c")
|
||||
list(REMOVE_ITEM TREMOR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tremor/ivorbisfile_example.c")
|
||||
list(APPEND SOURCES ${TREMOR_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "tremor")
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/VorbisDecoder.cpp")
|
||||
list(APPEND CODEC_FLAGS "-DBELL_CODEC_VORBIS")
|
||||
endif()
|
||||
# Opus codec
|
||||
if(BELL_CODEC_OPUS)
|
||||
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF CACHE BOOL "")
|
||||
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
|
||||
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF CACHE BOOL "")
|
||||
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
|
||||
set(MESSAGE_QUIET ON)
|
||||
add_subdirectory("opus")
|
||||
unset(MESSAGE_QUIET)
|
||||
target_compile_options(opus PRIVATE "-O3")
|
||||
list(APPEND EXTRA_LIBS Opus::opus)
|
||||
list(APPEND SOURCES "${AUDIO_DIR}/codec/OPUSDecoder.cpp")
|
||||
list(APPEND CODEC_FLAGS -DBELL_CODEC_OPUS)
|
||||
endif()
|
||||
# Enable global codecs
|
||||
string(REPLACE ";" " " CODEC_FLAGS "${CODEC_FLAGS}")
|
||||
set_source_files_properties("${AUDIO_DIR}/codec/AudioCodecs.cpp" PROPERTIES COMPILE_FLAGS "${CODEC_FLAGS}")
|
||||
elseif(BELL_EXTERNAL_TREMOR)
|
||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_TREMOR})
|
||||
endif()
|
||||
|
||||
if(NOT BELL_DISABLE_SINKS)
|
||||
@@ -85,45 +211,52 @@ if(NOT BELL_DISABLE_SINKS)
|
||||
set(PLATFORM "esp")
|
||||
endif()
|
||||
# Add all built-in audio sinks
|
||||
file(GLOB SINK_SOURCES "src/sinks/${PLATFORM}/*.cpp" "src/sinks/${PLATFORM}/*.c")
|
||||
file(GLOB SINK_SOURCES "${AUDIO_DIR}/sinks/${PLATFORM}/*.cpp" "${AUDIO_DIR}/sinks/${PLATFORM}/*.c")
|
||||
list(APPEND EXTRA_INCLUDES "include/audio/sinks/${PLATFORM}")
|
||||
# Find ALSA if required, else remove the sink
|
||||
if(BELL_USE_ALSA)
|
||||
if(BELL_SINK_ALSA)
|
||||
find_package(ALSA REQUIRED)
|
||||
list(APPEND EXTRA_INCLUDES ${ALSA_INCLUDE_DIRS})
|
||||
list(APPEND EXTRA_LIBS ${ALSA_LIBRARIES})
|
||||
else()
|
||||
list(REMOVE_ITEM SINK_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/sinks/unix/ALSAAudioSink.cpp)
|
||||
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_DIR}/sinks/unix/ALSAAudioSink.cpp")
|
||||
endif()
|
||||
# Find PortAudio if required, else remove the sink
|
||||
if(BELL_USE_PORTAUDIO)
|
||||
find_package(portaudio REQUIRED)
|
||||
list(APPEND EXTRA_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
|
||||
list(APPEND EXTRA_LIBS ${PORTAUDIO_LIBRARIES})
|
||||
if(BELL_SINK_PORTAUDIO)
|
||||
if(WIN32)
|
||||
list(APPEND EXTRA_INCLUDES "portaudio/include")
|
||||
if(NOT "${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
|
||||
list(APPEND EXTRA_LIBS "${CMAKE_CURRENT_SOURCE_DIR}/portaudio/portaudio_win32.lib")
|
||||
else()
|
||||
list(APPEND EXTRA_LIBS "${CMAKE_CURRENT_SOURCE_DIR}/portaudio/portaudio_x64.lib")
|
||||
endif()
|
||||
else()
|
||||
find_package(portaudio REQUIRED)
|
||||
list(APPEND EXTRA_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
|
||||
list(APPEND EXTRA_LIBS ${PORTAUDIO_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
list(REMOVE_ITEM SINK_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/sinks/unix/PortAudioSink.cpp)
|
||||
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_DIR}/sinks/unix/PortAudioSink.cpp")
|
||||
endif()
|
||||
list(APPEND SOURCES ${SINK_SOURCES})
|
||||
list(APPEND EXTRA_INCLUDES "include/sinks/${PLATFORM}")
|
||||
endif()
|
||||
|
||||
if(BELL_EXTERNAL_CJSON)
|
||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
|
||||
if(BELL_DISABLE_CJSON)
|
||||
list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/JSONObject.cpp")
|
||||
else()
|
||||
list(APPEND EXTRA_INCLUDES "cJSON")
|
||||
list(APPEND SOURCES "cJSON/cJSON.c")
|
||||
endif()
|
||||
|
||||
if(BELL_EXTERNAL_TREMOR)
|
||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_TREMOR})
|
||||
else()
|
||||
file(GLOB TREMOR_SOURCES "tremor/*.c")
|
||||
list(REMOVE_ITEM TREMOR_SOURCES "tremor/ivorbisfile_example.c")
|
||||
list(APPEND EXTRA_INCLUDES "tremor")
|
||||
list(APPEND SOURCES ${TREMOR_SOURCES})
|
||||
if(BELL_EXTERNAL_CJSON)
|
||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
|
||||
else()
|
||||
list(APPEND SOURCES "cJSON/cJSON.c")
|
||||
list(APPEND EXTRA_INCLUDES "cJSON")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(bell STATIC ${SOURCES})
|
||||
# PUBLIC to propagate esp-idf includes to bell dependents
|
||||
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
|
||||
target_include_directories(bell PUBLIC "include" "include/platform" ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(bell PUBLIC "include" ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC)
|
||||
if(WIN32)
|
||||
target_compile_definitions(bell PUBLIC PB_NO_STATIC_ASSERT)
|
||||
endif()
|
||||
|
||||
@@ -121,6 +121,7 @@ set(SOURCES cJSON.c)
|
||||
option(BUILD_SHARED_AND_STATIC_LIBS "Build both shared and static libraries" Off)
|
||||
option(CJSON_OVERRIDE_BUILD_SHARED_LIBS "Override BUILD_SHARED_LIBS with CJSON_BUILD_SHARED_LIBS" OFF)
|
||||
option(CJSON_BUILD_SHARED_LIBS "Overrides BUILD_SHARED_LIBS if CJSON_OVERRIDE_BUILD_SHARED_LIBS is enabled" ON)
|
||||
option(ENABLE_CJSON_VERSION_SO "Enables cJSON so version" ON)
|
||||
|
||||
if ((CJSON_OVERRIDE_BUILD_SHARED_LIBS AND CJSON_BUILD_SHARED_LIBS) OR ((NOT CJSON_OVERRIDE_BUILD_SHARED_LIBS) AND BUILD_SHARED_LIBS))
|
||||
set(CJSON_LIBRARY_TYPE SHARED)
|
||||
@@ -155,17 +156,23 @@ install(TARGETS "${CJSON_LIB}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
|
||||
)
|
||||
if (BUILD_SHARED_AND_STATIC_LIBS)
|
||||
install(TARGETS "${CJSON_LIB}-static" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
install(TARGETS "${CJSON_LIB}-static"
|
||||
EXPORT "${CJSON_LIB}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
|
||||
)
|
||||
endif()
|
||||
if(ENABLE_TARGET_EXPORT)
|
||||
# export library information for CMake projects
|
||||
install(EXPORT "${CJSON_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON")
|
||||
endif()
|
||||
|
||||
set_target_properties("${CJSON_LIB}"
|
||||
PROPERTIES
|
||||
SOVERSION "${CJSON_VERSION_SO}"
|
||||
VERSION "${PROJECT_VERSION}")
|
||||
if(ENABLE_CJSON_VERSION_SO)
|
||||
set_target_properties("${CJSON_LIB}"
|
||||
PROPERTIES
|
||||
SOVERSION "${CJSON_VERSION_SO}"
|
||||
VERSION "${PROJECT_VERSION}")
|
||||
endif()
|
||||
|
||||
#cJSON_Utils
|
||||
option(ENABLE_CJSON_UTILS "Enable building the cJSON_Utils library." OFF)
|
||||
@@ -198,7 +205,11 @@ if(ENABLE_CJSON_UTILS)
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
|
||||
)
|
||||
if (BUILD_SHARED_AND_STATIC_LIBS)
|
||||
install(TARGETS "${CJSON_UTILS_LIB}-static" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
install(TARGETS "${CJSON_UTILS_LIB}-static"
|
||||
EXPORT "${CJSON_UTILS_LIB}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}"
|
||||
)
|
||||
endif()
|
||||
install(FILES cJSON_Utils.h DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/cjson")
|
||||
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig")
|
||||
@@ -207,10 +218,12 @@ if(ENABLE_CJSON_UTILS)
|
||||
install(EXPORT "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/cJSON")
|
||||
endif()
|
||||
|
||||
set_target_properties("${CJSON_UTILS_LIB}"
|
||||
PROPERTIES
|
||||
SOVERSION "${CJSON_UTILS_VERSION_SO}"
|
||||
VERSION "${PROJECT_VERSION}")
|
||||
if(ENABLE_CJSON_VERSION_SO)
|
||||
set_target_properties("${CJSON_UTILS_LIB}"
|
||||
PROPERTIES
|
||||
SOVERSION "${CJSON_UTILS_VERSION_SO}"
|
||||
VERSION "${PROJECT_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# create the other package config files
|
||||
|
||||
@@ -96,9 +96,9 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
|
||||
return (const char*) (global_error.json + global_error.position);
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
|
||||
{
|
||||
if (!cJSON_IsString(item))
|
||||
if (!cJSON_IsString(item))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -106,9 +106,9 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
|
||||
return item->valuestring;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
|
||||
{
|
||||
if (!cJSON_IsNumber(item))
|
||||
if (!cJSON_IsNumber(item))
|
||||
{
|
||||
return (double) NAN;
|
||||
}
|
||||
@@ -511,7 +511,7 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
memcpy(newbuffer, p->buffer, p->offset + 1);
|
||||
p->hooks.deallocate(p->buffer);
|
||||
}
|
||||
@@ -562,6 +562,10 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
|
||||
{
|
||||
length = sprintf((char*)number_buffer, "null");
|
||||
}
|
||||
else if(d == (double)item->valueint)
|
||||
{
|
||||
length = sprintf((char*)number_buffer, "%d", item->valueint);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
|
||||
@@ -1103,7 +1107,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer
|
||||
}
|
||||
|
||||
buffer.content = (const unsigned char*)value;
|
||||
buffer.length = buffer_length;
|
||||
buffer.length = buffer_length;
|
||||
buffer.offset = 0;
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
@@ -2357,6 +2361,11 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO
|
||||
cJSON_free(replacement->string);
|
||||
}
|
||||
replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
|
||||
if (replacement->string == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
replacement->type &= ~cJSON_StringIsConst;
|
||||
|
||||
return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
|
||||
@@ -2689,7 +2698,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int co
|
||||
if (a && a->child) {
|
||||
a->child->prev = n;
|
||||
}
|
||||
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
@@ -279,6 +279,13 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
|
||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
||||
cJSON_Invalid\
|
||||
)
|
||||
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
static void cjson_array_foreach_should_loop_over_arrays(void)
|
||||
{
|
||||
cJSON array[1];
|
||||
@@ -77,7 +76,6 @@ static void cjson_get_object_item_should_get_object_items(void)
|
||||
found = cJSON_GetObjectItem(item, NULL);
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
|
||||
|
||||
|
||||
found = cJSON_GetObjectItem(item, "one");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
|
||||
@@ -127,7 +125,8 @@ static void cjson_get_object_item_case_sensitive_should_get_object_items(void)
|
||||
cJSON_Delete(item);
|
||||
}
|
||||
|
||||
static void cjson_get_object_item_should_not_crash_with_array(void) {
|
||||
static void cjson_get_object_item_should_not_crash_with_array(void)
|
||||
{
|
||||
cJSON *array = NULL;
|
||||
cJSON *found = NULL;
|
||||
array = cJSON_Parse("[1]");
|
||||
@@ -138,7 +137,8 @@ static void cjson_get_object_item_should_not_crash_with_array(void) {
|
||||
cJSON_Delete(array);
|
||||
}
|
||||
|
||||
static void cjson_get_object_item_case_sensitive_should_not_crash_with_array(void) {
|
||||
static void cjson_get_object_item_case_sensitive_should_not_crash_with_array(void)
|
||||
{
|
||||
cJSON *array = NULL;
|
||||
cJSON *found = NULL;
|
||||
array = cJSON_Parse("[1]");
|
||||
@@ -302,7 +302,6 @@ static void cjson_replace_item_via_pointer_should_replace_items(void)
|
||||
cJSON_AddItemToArray(array, middle);
|
||||
cJSON_AddItemToArray(array, end);
|
||||
|
||||
|
||||
memset(replacements, '\0', sizeof(replacements));
|
||||
|
||||
/* replace beginning */
|
||||
@@ -329,7 +328,7 @@ static void cjson_replace_item_via_pointer_should_replace_items(void)
|
||||
|
||||
static void cjson_replace_item_in_object_should_preserve_name(void)
|
||||
{
|
||||
cJSON root[1] = {{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }};
|
||||
cJSON root[1] = {{NULL, NULL, NULL, 0, NULL, 0, 0, NULL}};
|
||||
cJSON *child = NULL;
|
||||
cJSON *replacement = NULL;
|
||||
cJSON_bool flag = false;
|
||||
@@ -339,7 +338,7 @@ static void cjson_replace_item_in_object_should_preserve_name(void)
|
||||
replacement = cJSON_CreateNumber(2);
|
||||
TEST_ASSERT_NOT_NULL(replacement);
|
||||
|
||||
flag = cJSON_AddItemToObject(root, "child", child);
|
||||
flag = cJSON_AddItemToObject(root, "child", child);
|
||||
TEST_ASSERT_TRUE_MESSAGE(flag, "add item to object failed");
|
||||
cJSON_ReplaceItemInObject(root, "child", replacement);
|
||||
|
||||
@@ -435,7 +434,7 @@ static void cjson_functions_should_not_crash_with_null_pointers(void)
|
||||
cJSON_Delete(item);
|
||||
}
|
||||
|
||||
static void * CJSON_CDECL failing_realloc(void *pointer, size_t size)
|
||||
static void *CJSON_CDECL failing_realloc(void *pointer, size_t size)
|
||||
{
|
||||
(void)size;
|
||||
(void)pointer;
|
||||
@@ -445,7 +444,7 @@ static void * CJSON_CDECL failing_realloc(void *pointer, size_t size)
|
||||
static void ensure_should_fail_on_failed_realloc(void)
|
||||
{
|
||||
printbuffer buffer = {NULL, 10, 0, 0, false, false, {&malloc, &free, &failing_realloc}};
|
||||
buffer.buffer = (unsigned char*)malloc(100);
|
||||
buffer.buffer = (unsigned char *)malloc(100);
|
||||
TEST_ASSERT_NOT_NULL(buffer.buffer);
|
||||
|
||||
TEST_ASSERT_NULL_MESSAGE(ensure(&buffer, 200), "Ensure didn't fail with failing realloc.");
|
||||
@@ -454,7 +453,7 @@ static void ensure_should_fail_on_failed_realloc(void)
|
||||
static void skip_utf8_bom_should_skip_bom(void)
|
||||
{
|
||||
const unsigned char string[] = "\xEF\xBB\xBF{}";
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}};
|
||||
buffer.content = string;
|
||||
buffer.length = sizeof(string);
|
||||
buffer.hooks = global_hooks;
|
||||
@@ -466,7 +465,7 @@ static void skip_utf8_bom_should_skip_bom(void)
|
||||
static void skip_utf8_bom_should_not_skip_bom_if_not_at_beginning(void)
|
||||
{
|
||||
const unsigned char string[] = " \xEF\xBB\xBF{}";
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}};
|
||||
buffer.content = string;
|
||||
buffer.length = sizeof(string);
|
||||
buffer.hooks = global_hooks;
|
||||
@@ -496,12 +495,13 @@ static void cjson_get_number_value_should_get_a_number(void)
|
||||
TEST_ASSERT_EQUAL_DOUBLE(cJSON_GetNumberValue(number), number->valuedouble);
|
||||
TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(string));
|
||||
TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(NULL));
|
||||
|
||||
|
||||
cJSON_Delete(number);
|
||||
cJSON_Delete(string);
|
||||
}
|
||||
|
||||
static void cjson_create_string_reference_should_create_a_string_reference(void) {
|
||||
static void cjson_create_string_reference_should_create_a_string_reference(void)
|
||||
{
|
||||
const char *string = "I am a string!";
|
||||
|
||||
cJSON *string_reference = cJSON_CreateStringReference(string);
|
||||
@@ -511,7 +511,8 @@ static void cjson_create_string_reference_should_create_a_string_reference(void)
|
||||
cJSON_Delete(string_reference);
|
||||
}
|
||||
|
||||
static void cjson_create_object_reference_should_create_an_object_reference(void) {
|
||||
static void cjson_create_object_reference_should_create_an_object_reference(void)
|
||||
{
|
||||
cJSON *number_reference = NULL;
|
||||
cJSON *number_object = cJSON_CreateObject();
|
||||
cJSON *number = cJSON_CreateNumber(42);
|
||||
@@ -529,7 +530,8 @@ static void cjson_create_object_reference_should_create_an_object_reference(void
|
||||
cJSON_Delete(number_reference);
|
||||
}
|
||||
|
||||
static void cjson_create_array_reference_should_create_an_array_reference(void) {
|
||||
static void cjson_create_array_reference_should_create_an_array_reference(void)
|
||||
{
|
||||
cJSON *number_reference = NULL;
|
||||
cJSON *number_array = cJSON_CreateArray();
|
||||
cJSON *number = cJSON_CreateNumber(42);
|
||||
@@ -566,7 +568,7 @@ static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_al
|
||||
{
|
||||
cJSON *object = cJSON_CreateObject();
|
||||
cJSON *number = cJSON_CreateNumber(42);
|
||||
char *name = (char*)cJSON_strdup((const unsigned char*)"number", &global_hooks);
|
||||
char *name = (char *)cJSON_strdup((const unsigned char *)"number", &global_hooks);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(object);
|
||||
TEST_ASSERT_NOT_NULL(number);
|
||||
@@ -626,7 +628,7 @@ static void cjson_set_valuestring_to_object_should_not_leak_memory(void)
|
||||
cJSON *item2 = cJSON_CreateStringReference(reference_valuestring);
|
||||
char *ptr1 = NULL;
|
||||
char *return_value = NULL;
|
||||
|
||||
|
||||
cJSON_AddItemToObject(root, "one", item1);
|
||||
cJSON_AddItemToObject(root, "two", item2);
|
||||
|
||||
@@ -650,6 +652,64 @@ static void cjson_set_valuestring_to_object_should_not_leak_memory(void)
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
static void cjson_set_bool_value_must_not_break_objects(void)
|
||||
{
|
||||
cJSON *bobj, *sobj, *oobj, *refobj = NULL;
|
||||
|
||||
TEST_ASSERT_TRUE((cJSON_SetBoolValue(refobj, 1) == cJSON_Invalid));
|
||||
|
||||
bobj = cJSON_CreateFalse();
|
||||
TEST_ASSERT_TRUE(cJSON_IsFalse(bobj));
|
||||
TEST_ASSERT_TRUE((cJSON_SetBoolValue(bobj, 1) == cJSON_True));
|
||||
TEST_ASSERT_TRUE(cJSON_IsTrue(bobj));
|
||||
cJSON_SetBoolValue(bobj, 1);
|
||||
TEST_ASSERT_TRUE(cJSON_IsTrue(bobj));
|
||||
TEST_ASSERT_TRUE((cJSON_SetBoolValue(bobj, 0) == cJSON_False));
|
||||
TEST_ASSERT_TRUE(cJSON_IsFalse(bobj));
|
||||
cJSON_SetBoolValue(bobj, 0);
|
||||
TEST_ASSERT_TRUE(cJSON_IsFalse(bobj));
|
||||
|
||||
sobj = cJSON_CreateString("test");
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(sobj));
|
||||
cJSON_SetBoolValue(sobj, 1);
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(sobj));
|
||||
cJSON_SetBoolValue(sobj, 0);
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(sobj));
|
||||
|
||||
oobj = cJSON_CreateObject();
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(oobj));
|
||||
cJSON_SetBoolValue(oobj, 1);
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(oobj));
|
||||
cJSON_SetBoolValue(oobj, 0);
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(oobj));
|
||||
|
||||
refobj = cJSON_CreateStringReference("conststring");
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(refobj));
|
||||
TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference);
|
||||
cJSON_SetBoolValue(refobj, 1);
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(refobj));
|
||||
TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference);
|
||||
cJSON_SetBoolValue(refobj, 0);
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(refobj));
|
||||
TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference);
|
||||
cJSON_Delete(refobj);
|
||||
|
||||
refobj = cJSON_CreateObjectReference(oobj);
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(refobj));
|
||||
TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference);
|
||||
cJSON_SetBoolValue(refobj, 1);
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(refobj));
|
||||
TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference);
|
||||
cJSON_SetBoolValue(refobj, 0);
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(refobj));
|
||||
TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference);
|
||||
cJSON_Delete(refobj);
|
||||
|
||||
cJSON_Delete(oobj);
|
||||
cJSON_Delete(bobj);
|
||||
cJSON_Delete(sobj);
|
||||
}
|
||||
|
||||
int CJSON_CDECL main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
@@ -679,6 +739,7 @@ int CJSON_CDECL main(void)
|
||||
RUN_TEST(cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased);
|
||||
RUN_TEST(cjson_delete_item_from_array_should_not_broken_list_structure);
|
||||
RUN_TEST(cjson_set_valuestring_to_object_should_not_leak_memory);
|
||||
RUN_TEST(cjson_set_bool_value_must_not_break_objects);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,14 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
namespace bell {
|
||||
|
||||
@@ -20,6 +27,40 @@ class ResponseReader {
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
class RequestBodyReader : public ResponseReader {
|
||||
public:
|
||||
std::vector<uint8_t> partialBuffer;
|
||||
int fd = 0;
|
||||
size_t contentLength = 0;
|
||||
size_t sizeRead = 0;
|
||||
|
||||
RequestBodyReader(size_t contentLength, int fd, std::vector<uint8_t> &partialBuffer) {
|
||||
this->contentLength = contentLength;
|
||||
this->partialBuffer = partialBuffer;
|
||||
this->fd = fd;
|
||||
};
|
||||
|
||||
|
||||
size_t read(char *buffer, size_t size) {
|
||||
if (sizeRead < partialBuffer.size()) {
|
||||
size_t toRead = std::min(size, partialBuffer.size() - sizeRead);
|
||||
memcpy(buffer, &partialBuffer[sizeRead], toRead);
|
||||
sizeRead += toRead;
|
||||
return toRead;
|
||||
} else {
|
||||
size_t toRead = std::min(size, contentLength - sizeRead);
|
||||
size_t read = recv(fd, buffer, toRead, 0);
|
||||
sizeRead += read;
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
void close() {
|
||||
}
|
||||
|
||||
size_t getTotalSize() { return contentLength; }
|
||||
};
|
||||
|
||||
class FileResponseReader : public ResponseReader {
|
||||
public:
|
||||
FILE *file;
|
||||
@@ -30,7 +71,7 @@ class FileResponseReader : public ResponseReader {
|
||||
fileSize = ftell(file); // get current file pointer
|
||||
fseek(file, 0, SEEK_SET); // seek back to beginning of file
|
||||
};
|
||||
~FileResponseReader() { fclose(file); };
|
||||
|
||||
|
||||
size_t read(char *buffer, size_t size) {
|
||||
return fread(buffer, 1, size, file);
|
||||
@@ -48,10 +89,13 @@ enum class RequestType { GET, POST };
|
||||
struct HTTPRequest {
|
||||
std::map<std::string, std::string> urlParams;
|
||||
std::map<std::string, std::string> queryParams;
|
||||
std::unique_ptr<ResponseReader> responseReader = std::unique_ptr<RequestBodyReader>(nullptr);
|
||||
|
||||
std::string body;
|
||||
std::string url;
|
||||
int handlerId;
|
||||
int connection;
|
||||
int contentLength;
|
||||
};
|
||||
|
||||
struct HTTPResponse {
|
||||
@@ -64,19 +108,22 @@ struct HTTPResponse {
|
||||
std::unique_ptr<ResponseReader> responseReader;
|
||||
};
|
||||
|
||||
typedef std::function<void(HTTPRequest &)> httpHandler;
|
||||
typedef std::function<void(std::unique_ptr<bell::HTTPRequest>)> httpHandler;
|
||||
|
||||
struct HTTPRoute {
|
||||
RequestType requestType;
|
||||
httpHandler handler;
|
||||
bool readBodyToStr;
|
||||
};
|
||||
|
||||
struct HTTPConnection {
|
||||
int fd = 0;
|
||||
std::vector<uint8_t> buffer;
|
||||
std::string currentLine = "";
|
||||
std::vector<uint8_t> partialBuffer = std::vector<uint8_t>();
|
||||
int contentLength = 0;
|
||||
bool isReadingBody = false;
|
||||
std::string httpMethod;
|
||||
bool toBeClosed = false;
|
||||
bool headersRead = false;
|
||||
bool isEventConnection = false;
|
||||
bool isCaptivePortal = false;
|
||||
};
|
||||
@@ -96,10 +143,11 @@ public:
|
||||
*
|
||||
* @param requestType GET or POST
|
||||
* @param endpoint registering under
|
||||
* @param readResponseToStr if true, response will be read to string, otherwise it will return a reader object
|
||||
* httpHandler lambda to be called when given endpoint gets executed
|
||||
*/
|
||||
virtual void registerHandler(RequestType requestType, const std::string & endpoint,
|
||||
httpHandler) = 0;
|
||||
httpHandler, bool readResponseToStr = true) = 0;
|
||||
|
||||
/**
|
||||
* Writes given response to a fd
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
@@ -18,7 +19,7 @@ namespace bell
|
||||
virtual void info(std::string filename, int line, std::string submodule, const char *format, ...) = 0;
|
||||
};
|
||||
|
||||
extern std::shared_ptr<bell::AbstractLogger> bellGlobalLogger;
|
||||
extern bell::AbstractLogger* bellGlobalLogger;
|
||||
class BellLogger : public bell::AbstractLogger
|
||||
{
|
||||
public:
|
||||
@@ -81,23 +82,27 @@ namespace bell
|
||||
|
||||
void printFilename(std::string filename)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::string basenameStr(filename.substr(filename.rfind("\\") + 1));
|
||||
#else
|
||||
std::string basenameStr(filename.substr(filename.rfind("/") + 1));
|
||||
#endif
|
||||
unsigned long hash = 5381;
|
||||
for (char const &c : basenameStr)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
}
|
||||
|
||||
printf("\e[0;%dm", allColors[hash % NColors]);
|
||||
printf("\033[0;%dm", allColors[hash % NColors]);
|
||||
|
||||
printf("%s", basenameStr.c_str());
|
||||
printf(colorReset);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr const char *colorReset = "\e[0m";
|
||||
static constexpr const char *colorRed = "\e[0;31m";
|
||||
static constexpr const char *colorBlue = "\e[0;34m";
|
||||
static constexpr const char *colorReset = "\033[0m";
|
||||
static constexpr const char *colorRed = "\033[0;31m";
|
||||
static constexpr const char *colorBlue = "\033[0;34m";
|
||||
static constexpr const int NColors = 15;
|
||||
static constexpr int allColors[NColors] = {31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97};
|
||||
};
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <freertos/task.h>
|
||||
#elif _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
|
||||
namespace bell
|
||||
@@ -62,14 +65,23 @@ namespace bell
|
||||
esp_pthread_set_cfg(&cfg);
|
||||
}
|
||||
#endif
|
||||
#if _WIN32
|
||||
thread = CreateThread(NULL, stackSize, (LPTHREAD_START_ROUTINE) taskEntryFunc, this, 0, NULL);
|
||||
return thread != NULL;
|
||||
#else
|
||||
return (pthread_create(&thread, NULL, taskEntryFunc, this) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void runTask() = 0;
|
||||
|
||||
private:
|
||||
#if _WIN32
|
||||
HANDLE thread;
|
||||
#else
|
||||
pthread_t thread;
|
||||
#endif
|
||||
#ifdef ESP_PLATFORM
|
||||
int priority;
|
||||
StaticTask_t *xTaskBuffer;
|
||||
@@ -96,7 +108,11 @@ namespace bell
|
||||
{
|
||||
Task* self = (Task*) This;
|
||||
self->runTask();
|
||||
#if _WIN32
|
||||
WaitForSingleObject(self->thread, INFINITE);
|
||||
#else
|
||||
pthread_join(self->thread, NULL);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
@@ -18,6 +18,9 @@ void freeAndNull(void *&ptr);
|
||||
#define BELL_SLEEP_MS(ms) vTaskDelay(ms / portTICK_PERIOD_MS)
|
||||
#define BELL_YIELD() taskYIELD()
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#define BELL_SLEEP_MS(ms) Sleep(ms)
|
||||
#define BELL_YIELD() ;
|
||||
#else
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
127
components/spotify/cspot/bell/include/BufferedStream.h
Normal file
127
components/spotify/cspot/bell/include/BufferedStream.h
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-7.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ByteStream.h"
|
||||
#include "BellTask.h"
|
||||
#include "WrappedSemaphore.h"
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
/**
|
||||
* This class implements a wrapper around an arbitrary bell::ByteStream,
|
||||
* providing a circular reading buffer with configurable thresholds.
|
||||
*
|
||||
* The BufferedStream runs a bell::Task when it's started, so the caller can
|
||||
* access the buffer's data asynchronously, whenever needed. The buffer is refilled
|
||||
* automatically from source stream.
|
||||
*
|
||||
* The class implements bell::ByteStream's methods, although for proper functioning,
|
||||
* the caller code should be modified to check isReady() and isNotReady() flags.
|
||||
*
|
||||
* If the actual reading code can't be modified, waitForReady allows to wait for buffer readiness
|
||||
* during reading. Keep in mind that using the semaphore is probably more resource effective.
|
||||
*
|
||||
* The source stream (passed to open() or returned by the reader) should implement the read()
|
||||
* method correctly, such as that 0 is returned if, and only if the stream ends.
|
||||
*/
|
||||
class BufferedStream : public bell::ByteStream, bell::Task {
|
||||
public:
|
||||
typedef std::shared_ptr<bell::ByteStream> StreamPtr;
|
||||
typedef std::function<StreamPtr(uint32_t rangeStart)> StreamReader;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param taskName name to use for the reading task
|
||||
* @param bufferSize total size of the reading buffer
|
||||
* @param readThreshold how much can be read before refilling the buffer
|
||||
* @param readSize amount of bytes to read from the source each time
|
||||
* @param readyThreshold minimum amount of available bytes to report isReady()
|
||||
* @param notReadyThreshold maximum amount of available bytes to report isNotReady()
|
||||
* @param waitForReady whether to wait for the buffer to be ready during reading
|
||||
* @param endWithSource whether to end the streaming as soon as source returns 0 from read()
|
||||
*/
|
||||
BufferedStream(
|
||||
const std::string &taskName,
|
||||
uint32_t bufferSize,
|
||||
uint32_t readThreshold,
|
||||
uint32_t readSize,
|
||||
uint32_t readyThreshold,
|
||||
uint32_t notReadyThreshold,
|
||||
bool waitForReady = false);
|
||||
~BufferedStream() override;
|
||||
bool open(const StreamPtr &stream);
|
||||
bool open(const StreamReader &newReader, uint32_t initialOffset = 0);
|
||||
void close() override;
|
||||
|
||||
// inherited methods
|
||||
public:
|
||||
/**
|
||||
* Read len bytes from the buffer to dst. If waitForReady is enabled
|
||||
* and readAvailable is lower than notReadyThreshold, the function
|
||||
* will block until readyThreshold bytes is available.
|
||||
*
|
||||
* @returns number of bytes copied to dst (might be lower than len,
|
||||
* if the buffer does not contain len bytes available), or 0 if the source
|
||||
* stream is already closed and there is no reader attached.
|
||||
*/
|
||||
size_t read(uint8_t *dst, size_t len) override;
|
||||
size_t skip(size_t len) override;
|
||||
size_t position() override;
|
||||
size_t size() override;
|
||||
|
||||
// stream status
|
||||
public:
|
||||
/**
|
||||
* Total amount of bytes served to read().
|
||||
*/
|
||||
uint32_t readTotal;
|
||||
/**
|
||||
* Total amount of bytes read from source.
|
||||
*/
|
||||
uint32_t bufferTotal;
|
||||
/**
|
||||
* Amount of bytes available to read from the buffer.
|
||||
*/
|
||||
std::atomic<uint32_t> readAvailable;
|
||||
/**
|
||||
* Whether the caller should start reading the data. This indicates that a safe
|
||||
* amount (determined by readyThreshold) of data is available in the buffer.
|
||||
*/
|
||||
bool isReady() const;
|
||||
/**
|
||||
* Whether the caller should stop reading the data. This indicates that the amount of data
|
||||
* available for reading is decreasing to a non-safe value, as data is being read
|
||||
* faster than it can be buffered.
|
||||
*/
|
||||
bool isNotReady() const;
|
||||
/**
|
||||
* Semaphore that is given when the buffer becomes ready (isReady() == true). Caller can
|
||||
* wait for the semaphore instead of continuously querying isReady().
|
||||
*/
|
||||
WrappedSemaphore readySem;
|
||||
|
||||
private:
|
||||
std::mutex runningMutex;
|
||||
bool running = false;
|
||||
bool terminate = false;
|
||||
WrappedSemaphore readSem; // signal to start writing to buffer after reading from it
|
||||
std::mutex readMutex; // mutex for locking read operations during writing, and vice versa
|
||||
uint32_t bufferSize;
|
||||
uint32_t readAt;
|
||||
uint32_t readSize;
|
||||
uint32_t readyThreshold;
|
||||
uint32_t notReadyThreshold;
|
||||
bool waitForReady;
|
||||
uint8_t *buf;
|
||||
uint8_t *bufEnd;
|
||||
uint8_t *bufReadPtr;
|
||||
uint8_t *bufWritePtr;
|
||||
StreamPtr source;
|
||||
StreamReader reader;
|
||||
void runTask() override;
|
||||
void reset();
|
||||
uint32_t lengthBetween(uint8_t *me, uint8_t *other);
|
||||
};
|
||||
@@ -1,14 +1,78 @@
|
||||
#ifndef BELL_CRYPTO_H
|
||||
#define BELL_CRYPTO_H
|
||||
|
||||
#define Crypto CryptoMbedTLS
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <mbedtls/base64.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/pkcs5.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
|
||||
|
||||
#define DH_KEY_SIZE 96
|
||||
|
||||
static unsigned char DHPrime[] = {
|
||||
/* Well-known Group 1, 768-bit prime */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9,
|
||||
0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6,
|
||||
0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e,
|
||||
0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
|
||||
0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e,
|
||||
0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a,
|
||||
0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14,
|
||||
0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
|
||||
0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4,
|
||||
0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
static unsigned char DHGenerator[1] = {2};
|
||||
|
||||
class CryptoMbedTLS {
|
||||
private:
|
||||
mbedtls_md_context_t sha1Context;
|
||||
mbedtls_aes_context aesCtx;
|
||||
public:
|
||||
CryptoMbedTLS();
|
||||
~CryptoMbedTLS();
|
||||
// Base64
|
||||
std::vector<uint8_t> base64Decode(const std::string& data);
|
||||
std::string base64Encode(const std::vector<uint8_t>& data);
|
||||
|
||||
// Sha1
|
||||
void sha1Init();
|
||||
void sha1Update(const std::string& s);
|
||||
void sha1Update(const std::vector<uint8_t>& vec);
|
||||
std::string sha1Final();
|
||||
std::vector<uint8_t> sha1FinalBytes();
|
||||
|
||||
// HMAC SHA1
|
||||
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
|
||||
|
||||
// AES CTR
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* data, size_t nbytes);
|
||||
|
||||
// AES ECB
|
||||
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);
|
||||
|
||||
// Diffie Hellman
|
||||
std::vector<uint8_t> publicKey;
|
||||
std::vector<uint8_t> privateKey;
|
||||
void dhInit();
|
||||
std::vector<uint8_t> dhCalculateShared(const std::vector<uint8_t>& remoteKey);
|
||||
|
||||
// PBKDF2
|
||||
std::vector<uint8_t> pbkdf2HmacSha1(const std::vector<uint8_t>& password, const std::vector<uint8_t>& salt, int iterations, int digestSize);
|
||||
|
||||
// Random stuff
|
||||
std::vector<uint8_t> generateVectorWithRandomData(size_t length);
|
||||
};
|
||||
|
||||
#ifdef BELL_USE_MBEDTLS
|
||||
#include "CryptoMbedTLS.h"
|
||||
#define Crypto CryptoMbedTLS
|
||||
#else
|
||||
#include "CryptoOpenSSL.h"
|
||||
#define Crypto CryptoOpenSSL
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,78 +0,0 @@
|
||||
#ifndef BELL_CRYPTOMBEDTLS_H
|
||||
#define BELL_CRYPTOMBEDTLS_H
|
||||
|
||||
#ifdef BELL_USE_MBEDTLS
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <mbedtls/base64.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/pkcs5.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
|
||||
|
||||
#define DH_KEY_SIZE 96
|
||||
|
||||
static unsigned char DHPrime[] = {
|
||||
/* Well-known Group 1, 768-bit prime */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9,
|
||||
0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6,
|
||||
0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e,
|
||||
0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
|
||||
0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e,
|
||||
0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a,
|
||||
0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14,
|
||||
0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
|
||||
0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4,
|
||||
0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
static unsigned char DHGenerator[1] = {2};
|
||||
|
||||
class CryptoMbedTLS {
|
||||
private:
|
||||
mbedtls_md_context_t sha1Context;
|
||||
mbedtls_aes_context aesCtx;
|
||||
public:
|
||||
CryptoMbedTLS();
|
||||
~CryptoMbedTLS();
|
||||
// Base64
|
||||
std::vector<uint8_t> base64Decode(const std::string& data);
|
||||
std::string base64Encode(const std::vector<uint8_t>& data);
|
||||
|
||||
// Sha1
|
||||
void sha1Init();
|
||||
void sha1Update(const std::string& s);
|
||||
void sha1Update(const std::vector<uint8_t>& vec);
|
||||
std::string sha1Final();
|
||||
std::vector<uint8_t> sha1FinalBytes();
|
||||
|
||||
// HMAC SHA1
|
||||
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
|
||||
|
||||
// AES CTR
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* data, size_t nbytes);
|
||||
|
||||
// AES ECB
|
||||
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);
|
||||
|
||||
// Diffie Hellman
|
||||
std::vector<uint8_t> publicKey;
|
||||
std::vector<uint8_t> privateKey;
|
||||
void dhInit();
|
||||
std::vector<uint8_t> dhCalculateShared(const std::vector<uint8_t>& remoteKey);
|
||||
|
||||
// PBKDF2
|
||||
std::vector<uint8_t> pbkdf2HmacSha1(const std::vector<uint8_t>& password, const std::vector<uint8_t>& salt, int iterations, int digestSize);
|
||||
|
||||
// Random stuff
|
||||
std::vector<uint8_t> generateVectorWithRandomData(size_t length);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,84 +0,0 @@
|
||||
#ifndef BELL_CRYPTOOPENSSL_H
|
||||
#define BELL_CRYPTOOPENSSL_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <openssl/engine.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/modes.h>
|
||||
|
||||
#define DH_KEY_SIZE 96
|
||||
|
||||
static unsigned char DHPrime[] = {
|
||||
/* Well-known Group 1, 768-bit prime */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9,
|
||||
0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6,
|
||||
0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e,
|
||||
0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
|
||||
0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e,
|
||||
0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a,
|
||||
0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14,
|
||||
0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
|
||||
0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4,
|
||||
0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
static unsigned char DHGenerator[1] = {2};
|
||||
|
||||
class CryptoOpenSSL {
|
||||
private:
|
||||
DH* dhContext = nullptr;
|
||||
SHA_CTX sha1Context;
|
||||
public:
|
||||
CryptoOpenSSL();
|
||||
~CryptoOpenSSL();
|
||||
// Base64
|
||||
std::vector<uint8_t> base64Decode(const std::string& data);
|
||||
std::string base64Encode(const std::vector<uint8_t>& data);
|
||||
|
||||
// Sha1
|
||||
void sha1Init();
|
||||
void sha1Update(const std::string& s);
|
||||
void sha1Update(const std::vector<uint8_t>& vec);
|
||||
|
||||
void connectSSL(std::string url);
|
||||
int readSSL(uint8_t* buf, int len);
|
||||
int writeSSL(uint8_t* buf, int len);
|
||||
void closeSSL();
|
||||
|
||||
std::string sha1Final();
|
||||
std::vector<uint8_t> sha1FinalBytes();
|
||||
|
||||
// HMAC SHA1
|
||||
std::vector<uint8_t> sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message);
|
||||
|
||||
// AES CTR
|
||||
void aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes);
|
||||
|
||||
// AES ECB
|
||||
void aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data);
|
||||
|
||||
// Diffie Hellman
|
||||
std::vector<uint8_t> publicKey;
|
||||
std::vector<uint8_t> privateKey;
|
||||
void dhInit();
|
||||
std::vector<uint8_t> dhCalculateShared(const std::vector<uint8_t>& remoteKey);
|
||||
|
||||
// PBKDF2
|
||||
std::vector<uint8_t> pbkdf2HmacSha1(const std::vector<uint8_t>& password, const std::vector<uint8_t>& salt, int iterations, int digestSize);
|
||||
|
||||
// Random stuff
|
||||
std::vector<uint8_t> generateVectorWithRandomData(size_t length);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "BellSocket.h"
|
||||
#include "ByteStream.h"
|
||||
#include "TCPSocket.h"
|
||||
#include "platform/TLSSocket.h"
|
||||
#include "TLSSocket.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -11,16 +11,21 @@
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "win32shim.h"
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sstream>
|
||||
#include <BellLogger.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
#include <sys/socket.h>
|
||||
#include <string>
|
||||
#include <netdb.h>
|
||||
#include <mutex>
|
||||
#include <fcntl.h>
|
||||
#include "BaseHTTPServer.h"
|
||||
@@ -46,7 +51,7 @@ namespace bell
|
||||
std::map<int, HTTPConnection> connections;
|
||||
void writeResponse(const HTTPResponse &);
|
||||
void writeResponseEvents(int connFd);
|
||||
void findAndHandleRoute(std::string &, std::string &, int connectionFd);
|
||||
void findAndHandleRoute(HTTPConnection& connection);
|
||||
|
||||
std::vector<std::string> splitUrl(const std::string &url, char delimiter);
|
||||
std::mutex responseMutex;
|
||||
@@ -60,7 +65,7 @@ namespace bell
|
||||
public:
|
||||
HTTPServer(int serverPort);
|
||||
|
||||
void registerHandler(RequestType requestType, const std::string &, httpHandler);
|
||||
void registerHandler(RequestType requestType, const std::string &, httpHandler, bool readDataToStr = false);
|
||||
void respond(const HTTPResponse &);
|
||||
void redirectTo(const std::string&, int connectionFd);
|
||||
void publishEvent(std::string eventName, std::string eventData);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <ByteStream.h>
|
||||
#include <BellSocket.h>
|
||||
#include <TCPSocket.h>
|
||||
#include <platform/TLSSocket.h>
|
||||
#include <TLSSocket.h>
|
||||
|
||||
/*
|
||||
* HTTPStream
|
||||
|
||||
@@ -9,15 +9,21 @@
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "win32shim.h"
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <netinet/tcp.h>
|
||||
#include <BellLogger.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
namespace bell
|
||||
{
|
||||
@@ -77,18 +83,23 @@ namespace bell
|
||||
}
|
||||
|
||||
size_t read(uint8_t *buf, size_t len) {
|
||||
return recv(sockFd, buf, len, 0);
|
||||
return recv(sockFd, (char*) buf, len, 0);
|
||||
}
|
||||
|
||||
size_t write(uint8_t *buf, size_t len) {
|
||||
return send(sockFd, buf, len, 0);
|
||||
return send(sockFd, (char*) buf, len, 0);
|
||||
}
|
||||
|
||||
size_t poll() {
|
||||
int value;
|
||||
ioctl(sockFd, FIONREAD, &value);
|
||||
return value;
|
||||
}
|
||||
size_t poll() {
|
||||
#ifdef _WIN32
|
||||
unsigned long value;
|
||||
ioctlsocket(sockFd, FIONREAD, &value);
|
||||
#else
|
||||
int value;
|
||||
ioctl(sockFd, FIONREAD, &value);
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (!isClosed) {
|
||||
|
||||
@@ -8,50 +8,36 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef BELL_USE_MBEDTLS
|
||||
|
||||
#include "mbedtls/net_sockets.h"
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/debug.h"
|
||||
#else
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
namespace bell {
|
||||
class TLSSocket : public bell::Socket {
|
||||
private:
|
||||
#ifdef BELL_USE_MBEDTLS
|
||||
mbedtls_net_context server_fd;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_ssl_config conf;
|
||||
#else
|
||||
|
||||
BIO *sbio, *out;
|
||||
int len;
|
||||
char tmpbuf[1024];
|
||||
SSL_CTX *ctx;
|
||||
SSL *ssl;
|
||||
|
||||
int sockFd;
|
||||
int sslFd;
|
||||
#endif
|
||||
|
||||
bool isClosed = false;
|
||||
public:
|
||||
8
components/spotify/cspot/bell/include/TimeDefs.h
Normal file
8
components/spotify/cspot/bell/include/TimeDefs.h
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by Filip Grzywok on 28/02/2022.
|
||||
//
|
||||
|
||||
#ifndef EUPHONIUMCLI_TIMEDEFS_H
|
||||
#define EUPHONIUMCLI_TIMEDEFS_H
|
||||
|
||||
#endif // EUPHONIUMCLI_TIMEDEFS_H
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
#include "aacdec.h"
|
||||
|
||||
class AACDecoder : public BaseCodec {
|
||||
private:
|
||||
HAACDecoder aac;
|
||||
int16_t *pcmData;
|
||||
AACFrameInfo frame = {};
|
||||
|
||||
public:
|
||||
AACDecoder();
|
||||
~AACDecoder();
|
||||
bool setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||
uint8_t *decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) override;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
#include "alac_wrapper.h"
|
||||
|
||||
class ALACDecoder : public BaseCodec {
|
||||
private:
|
||||
alac_codec_s* alacCodec;
|
||||
int16_t *pcmData;
|
||||
|
||||
public:
|
||||
ALACDecoder();
|
||||
~ALACDecoder();
|
||||
bool setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||
uint8_t *decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) override;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
#include "BaseContainer.h"
|
||||
#include <memory>
|
||||
|
||||
enum class AudioCodec {
|
||||
UNKNOWN = 0,
|
||||
AAC = 1,
|
||||
MP3 = 2,
|
||||
VORBIS = 3,
|
||||
OPUS = 4,
|
||||
FLAC = 5,
|
||||
};
|
||||
|
||||
class AudioCodecs {
|
||||
public:
|
||||
static std::shared_ptr<BaseCodec> getCodec(AudioCodec type);
|
||||
static std::shared_ptr<BaseCodec> getCodec(BaseContainer *container);
|
||||
static void addCodec(AudioCodec type, const std::shared_ptr<BaseCodec> &codec);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseContainer.h"
|
||||
|
||||
class BaseCodec {
|
||||
public:
|
||||
/**
|
||||
* Setup the codec (sample rate, channel count, etc) using the specified container.
|
||||
*/
|
||||
virtual bool setup(BaseContainer *container);
|
||||
/**
|
||||
* Setup the codec manually, using the provided values.
|
||||
*/
|
||||
virtual bool setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) = 0;
|
||||
/**
|
||||
* Decode the given sample.
|
||||
*
|
||||
* @param [in] inData encoded data. Should allow nullptr, in which case nullptr should be returned.
|
||||
* @param [in] inLen size of inData, in bytes
|
||||
* @param [out] outLen size of output PCM data, in bytes
|
||||
* @return pointer to decoded raw PCM audio data, allocated inside the codec object; nullptr on failure
|
||||
*/
|
||||
virtual uint8_t *decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) = 0;
|
||||
/**
|
||||
* Read a single sample from the container, decode it, and return the result.
|
||||
*
|
||||
* @param [in] container media container to read the sample from (the container's codec must match this instance)
|
||||
* @param [out] outLen size of output PCM data, in bytes
|
||||
* @return pointer to decoded raw PCM audio data, allocated inside the codec object; nullptr on failure
|
||||
*/
|
||||
uint8_t *decode(BaseContainer *container, uint32_t &outLen);
|
||||
/**
|
||||
* Last error that occurred, this is a codec-specific value.
|
||||
* This may be set by a codec upon decoding failure.
|
||||
*/
|
||||
int lastErrno = -1;
|
||||
};
|
||||
@@ -43,7 +43,7 @@ namespace bell
|
||||
}
|
||||
};
|
||||
|
||||
extern std::shared_ptr<bell::DecodersInstance> decodersInstance;
|
||||
extern bell::DecodersInstance* decodersInstance;
|
||||
|
||||
void createDecoders();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
#include "mp3dec.h"
|
||||
|
||||
class MP3Decoder : public BaseCodec {
|
||||
private:
|
||||
HMP3Decoder mp3;
|
||||
int16_t *pcmData;
|
||||
MP3FrameInfo frame = {};
|
||||
|
||||
public:
|
||||
MP3Decoder();
|
||||
~MP3Decoder();
|
||||
bool setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||
uint8_t *decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) override;
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
|
||||
struct OpusDecoder;
|
||||
|
||||
class OPUSDecoder : public BaseCodec {
|
||||
private:
|
||||
OpusDecoder *opus;
|
||||
int16_t *pcmData;
|
||||
|
||||
public:
|
||||
OPUSDecoder();
|
||||
~OPUSDecoder();
|
||||
bool setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||
uint8_t *decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) override;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseCodec.h"
|
||||
#include "ivorbiscodec.h"
|
||||
|
||||
class VorbisDecoder : public BaseCodec {
|
||||
private:
|
||||
vorbis_info *vi = nullptr;
|
||||
vorbis_comment *vc = nullptr;
|
||||
vorbis_dsp_state *vd = nullptr;
|
||||
ogg_packet op = {};
|
||||
int16_t *pcmData;
|
||||
|
||||
public:
|
||||
VorbisDecoder();
|
||||
~VorbisDecoder();
|
||||
bool setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
||||
uint8_t *decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) override;
|
||||
bool setup(BaseContainer *container) override;
|
||||
|
||||
private:
|
||||
void setPacket(uint8_t *inData, uint32_t inLen) const;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-15.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseContainer.h"
|
||||
|
||||
class AudioContainers {
|
||||
public:
|
||||
static std::unique_ptr<BaseContainer> create(const char *mimeType);
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-7.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BinaryReader.h"
|
||||
#include "ByteStream.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* Either the media file or the requested position/offset is not loaded yet.
|
||||
*/
|
||||
#define SAMPLE_NOT_LOADED -1
|
||||
/**
|
||||
* The media file does not contain the requested position/offset.
|
||||
*/
|
||||
#define SAMPLE_NOT_FOUND -2
|
||||
/**
|
||||
* The file is not seekable (i.e. doesn't contain an index table).
|
||||
*/
|
||||
#define SAMPLE_NOT_SEEKABLE -3
|
||||
|
||||
enum class AudioCodec;
|
||||
|
||||
class BaseContainer {
|
||||
public:
|
||||
BaseContainer() = default;
|
||||
/**
|
||||
* Feed a new data source to the container.
|
||||
* @param stream ByteStream reading source data
|
||||
* @param position absolute position of the current ByteStream within the source media
|
||||
*/
|
||||
virtual void feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position);
|
||||
/**
|
||||
* Try to parse the media provided by the source stream.
|
||||
* @return whether parsing was successful
|
||||
*/
|
||||
virtual bool parse() = 0;
|
||||
/**
|
||||
* Get absolute offset within the source media for the given timestamp.
|
||||
* When seeking to a specified time, the caller should run feed() with a stream
|
||||
* reader starting at the returned offset. Depending on the container type,
|
||||
* the returned offset may not point to the exact time position (i.e. chunks with
|
||||
* headers), so seekTo() should be used afterwards.
|
||||
*
|
||||
* @param timeMs requested timestamp, in milliseconds
|
||||
* @return byte offset within the source media that should be loaded
|
||||
* in order to seek to the requested position; negative value on error
|
||||
*/
|
||||
virtual int32_t getLoadingOffset(uint32_t timeMs) = 0;
|
||||
/**
|
||||
* Try to seek to the specified position (in milliseconds), using the currently
|
||||
* loaded source stream. This method will fail if the source stream does not yield
|
||||
* data for the requested position, or block until the stream loads data for this position.
|
||||
*
|
||||
* @param timeMs requested timestamp, in milliseconds
|
||||
*/
|
||||
virtual bool seekTo(uint32_t timeMs) = 0;
|
||||
/**
|
||||
* Get the current playback position, in milliseconds. May return -1 if the track
|
||||
* is not playing (has ended or not started yet).
|
||||
*/
|
||||
virtual int32_t getCurrentTimeMs() = 0;
|
||||
/**
|
||||
* Read an encoded audio sample from the container, starting at the current position.
|
||||
*
|
||||
* @param [out] len length of the data stored in the returned pointer, in bytes
|
||||
* @return pointer to data allocated inside the container object; should not be freed or changed.
|
||||
* On failure, nullptr is returned, and len is left unchanged.
|
||||
*/
|
||||
virtual uint8_t *readSample(uint32_t &len) = 0;
|
||||
/**
|
||||
* Get optional initialization data for the specified codec. This may be used by a codec,
|
||||
* for containers that contain the setup data.
|
||||
*
|
||||
* @param [out] len length of the setup data
|
||||
* @return ptr to [len] setup data bytes, or nullptr if not available/not supported
|
||||
*/
|
||||
virtual uint8_t *getSetupData(uint32_t &len, AudioCodec matchCodec) = 0;
|
||||
|
||||
public:
|
||||
bool closed = false;
|
||||
bool isSeekable = false;
|
||||
// audio parameters
|
||||
AudioCodec codec = (AudioCodec)0;
|
||||
uint32_t sampleRate = 0;
|
||||
uint8_t channelCount = 0;
|
||||
uint8_t bitDepth = 0;
|
||||
uint32_t durationMs = 0;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<bell::BinaryReader> reader;
|
||||
std::shared_ptr<bell::ByteStream> source;
|
||||
uint32_t pos = 0;
|
||||
uint8_t readUint8();
|
||||
uint16_t readUint16();
|
||||
uint32_t readUint24();
|
||||
uint32_t readUint32();
|
||||
uint64_t readUint64();
|
||||
uint32_t readVarint32();
|
||||
uint32_t readBytes(uint8_t *dst, uint32_t num);
|
||||
uint32_t skipBytes(uint32_t num);
|
||||
uint32_t skipTo(uint32_t offset);
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-8.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class AudioSampleFormat;
|
||||
enum class MP4AObjectType;
|
||||
enum class MP4AProfile;
|
||||
|
||||
typedef struct {
|
||||
/** Absolute offset of mdat header (or moof for fMP4) */
|
||||
uint32_t start;
|
||||
/** Absolute offset of the last mdat byte */
|
||||
uint32_t end;
|
||||
/** Total duration of this fragment */
|
||||
uint32_t duration;
|
||||
} Mpeg4Fragment;
|
||||
|
||||
typedef struct {
|
||||
/** Number of chunks this descriptor applies to */
|
||||
uint16_t count;
|
||||
/** Number of samples in the described chunks */
|
||||
uint32_t samples;
|
||||
uint16_t sampleDescriptionId;
|
||||
} Mpeg4ChunkRange;
|
||||
|
||||
/** Absolute offset of the chunk data */
|
||||
typedef uint32_t Mpeg4ChunkOffset;
|
||||
|
||||
typedef struct {
|
||||
/** Abs. offset of data start in the current chunk */
|
||||
uint32_t start;
|
||||
/** Abs. offset of data end in the current chunk */
|
||||
uint32_t end;
|
||||
/** Abs. offset of the next chunk data, or 0 for last chunk in a fragment */
|
||||
uint32_t nextStart;
|
||||
} Mpeg4Chunk;
|
||||
|
||||
typedef struct {
|
||||
/** Number of samples this descriptor applies to */
|
||||
uint32_t count;
|
||||
/** Duration of the described samples */
|
||||
uint32_t duration;
|
||||
} Mpeg4SampleRange;
|
||||
|
||||
/** Size of a single sample */
|
||||
typedef uint32_t Mpeg4SampleSize;
|
||||
|
||||
/** Flags for a sample */
|
||||
typedef uint32_t SampleFlags;
|
||||
|
||||
/** Default values for samples in the movie/fragment */
|
||||
typedef struct {
|
||||
/** Absolute offset of first mdat byte */
|
||||
uint32_t offset;
|
||||
uint32_t sampleDescriptionId;
|
||||
uint32_t duration;
|
||||
uint32_t size;
|
||||
SampleFlags flags;
|
||||
} SampleDefaults;
|
||||
|
||||
/** Sample Description Table */
|
||||
typedef struct {
|
||||
uint16_t dataReferenceIndex;
|
||||
AudioSampleFormat format;
|
||||
// params for MPEG-4 Elementary Stream Descriptors
|
||||
MP4AObjectType mp4aObjectType;
|
||||
MP4AProfile mp4aProfile;
|
||||
// atom header for unknown descriptors
|
||||
uint32_t dataType;
|
||||
// codec-specific data (either DecoderSpecificInfo or the entire descriptor)
|
||||
uint32_t dataLength;
|
||||
uint8_t *data;
|
||||
} SampleDescription;
|
||||
|
||||
typedef struct {
|
||||
// byte 1 - bits 0:7
|
||||
bool durationIsEmpty : 1;
|
||||
bool defaultBaseIsMoof : 1;
|
||||
bool dummy1 : 6;
|
||||
// byte 2 - bits 0:7
|
||||
uint8_t dummy2 : 8;
|
||||
// byte 3 - bits 0:7
|
||||
bool baseDataOffsetPresent : 1;
|
||||
bool sampleDescriptionIndexPresent : 1;
|
||||
bool dummy3 : 1;
|
||||
bool defaultSampleDurationPresent : 1;
|
||||
bool defaultSampleSizePresent : 1;
|
||||
bool defaultSampleFlagsPresent : 1;
|
||||
bool dummy4 : 2;
|
||||
} TfFlags;
|
||||
|
||||
typedef struct {
|
||||
// byte 1 - bits 0:7
|
||||
uint8_t dummy1 : 8;
|
||||
// byte 2 - bits 0:7
|
||||
bool sampleDurationPresent : 1;
|
||||
bool sampleSizePresent : 1;
|
||||
bool sampleFlagsPresent : 1;
|
||||
bool sampleCompositionTimeOffsetsPresent : 1;
|
||||
bool dummy2 : 4;
|
||||
// byte 3 - bits 0:7
|
||||
bool dataOffsetPresent : 1;
|
||||
bool dummy3 : 1;
|
||||
bool firstSampleFlagsPresent : 1;
|
||||
bool dummy4 : 5;
|
||||
} TrFlags;
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-8.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseContainer.h"
|
||||
#include "Mpeg4Atoms.h"
|
||||
#include <memory>
|
||||
|
||||
class Mpeg4Container : public BaseContainer {
|
||||
public:
|
||||
~Mpeg4Container();
|
||||
/**
|
||||
* Start parsing the MP4 file. This method expects the source to read from 0th byte.
|
||||
* This method leaves pos at first mdat data byte, or mdat header for fMP4 files.
|
||||
*/
|
||||
bool parse() override;
|
||||
int32_t getLoadingOffset(uint32_t timeMs) override;
|
||||
bool seekTo(uint32_t timeMs) override;
|
||||
int32_t getCurrentTimeMs() override;
|
||||
uint8_t *readSample(uint32_t &len) override;
|
||||
uint8_t *getSetupData(uint32_t &len, AudioCodec matchCodec) override;
|
||||
void feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parse a single movie fragment. This method expects the source to read moof data, without the header.
|
||||
* After running, [pos] is left at next mdat header. A new fragment will be created if [pos] does not exist
|
||||
* in [fragments] table.
|
||||
*/
|
||||
bool parseMoof(uint32_t moofSize);
|
||||
bool goToData();
|
||||
// char mediaBrand[5];
|
||||
uint32_t totalDuration;
|
||||
bool totalDurationPresent = false;
|
||||
int8_t audioTrackId = -1;
|
||||
uint32_t timescale = 0;
|
||||
uint32_t sampleSizeMax = 0;
|
||||
uint8_t *sampleData = nullptr;
|
||||
uint32_t sampleDataLen = 0;
|
||||
bool isParsed = false;
|
||||
bool isFragmented = false;
|
||||
/** True if source reads **audio** mdat data bytes, false if source reads atom headers */
|
||||
bool isInData = false;
|
||||
|
||||
private: // data for the entire movie:
|
||||
/** All fragments in the MPEG file */
|
||||
Mpeg4Fragment *fragments;
|
||||
uint16_t fragmentsLen;
|
||||
/** Default sample descriptions for each track */
|
||||
SampleDefaults *sampleDefs;
|
||||
uint32_t sampleDefsLen;
|
||||
/** Track IDs of [sampleDef] items */
|
||||
uint32_t *sampleDefTracks;
|
||||
/** Sample Description Table */
|
||||
SampleDescription *sampleDesc;
|
||||
uint32_t sampleDescLen;
|
||||
|
||||
private: // data changing every fragment:
|
||||
/** Chunks in the current fragment */
|
||||
Mpeg4ChunkRange *chunks;
|
||||
uint32_t chunksLen;
|
||||
/** Absolute chunk offsets in the current fragment */
|
||||
Mpeg4ChunkOffset *chunkOffsets;
|
||||
uint32_t chunkOffsetsLen;
|
||||
/** All sample descriptors in the current fragment */
|
||||
Mpeg4SampleRange *samples;
|
||||
uint32_t samplesLen;
|
||||
/** All sample sizes in the current fragment */
|
||||
Mpeg4SampleSize *sampleSizes;
|
||||
uint32_t sampleSizesLen;
|
||||
|
||||
private: // current status and position within the file
|
||||
/** Currently loaded fragment (ptr) */
|
||||
Mpeg4Fragment *curFragment;
|
||||
/** The chunk currently being processed */
|
||||
Mpeg4Chunk curChunk;
|
||||
/** Size of the current sample (ptr) */
|
||||
Mpeg4SampleSize *curSampleSize;
|
||||
|
||||
private: // Mpeg4Utils.cpp
|
||||
void readAtomHeader(uint32_t &size, uint32_t &type);
|
||||
void freeAll();
|
||||
void freeFragment();
|
||||
SampleDefaults *getSampleDef(uint32_t trackId);
|
||||
void setCurrentFragment();
|
||||
void setCurrentSample();
|
||||
static bool isInFragment(Mpeg4Fragment *f, uint32_t offset);
|
||||
static AudioCodec getCodec(SampleDescription *desc);
|
||||
Mpeg4Fragment *createFragment();
|
||||
int64_t findSample(int64_t byTime, int32_t byPos, uint64_t startTime);
|
||||
|
||||
private: // Mpeg4Parser.cpp
|
||||
/** Populate [chunks] using the Sample-to-chunk Table */
|
||||
void readStsc();
|
||||
/** Populate [chunkOffsets] using the Chunk Offset Table */
|
||||
void readStco();
|
||||
/** Populate [samples] using the Time-to-sample Table */
|
||||
void readStts();
|
||||
/** Populate [sampleSizes] using the Sample Size Table */
|
||||
void readStsz();
|
||||
/** Populate [sampleDesc] using the Sample Description Table */
|
||||
void readStsd();
|
||||
|
||||
private: // Mpeg4ParserFrag.cpp
|
||||
/** Populate [fragments] using the Segment Index Table */
|
||||
void readSidx(uint32_t atomSize);
|
||||
/** Populate [sampleDefs] using Track Extends */
|
||||
void readTrex();
|
||||
/** Populate [sampleDefs] using Track Fragment Header */
|
||||
void readTfhd(uint32_t trafEnd, uint32_t moofOffset);
|
||||
/** Populate [chunks, chunkOffsets, samples, sampleSizes] using Track Fragment Run Table */
|
||||
void readTrun(uint32_t atomSize, uint32_t moofOffset);
|
||||
void allocSampleData();
|
||||
};
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class AtomType {
|
||||
/** File Type */
|
||||
ATOM_FTYP = 0x66747970,
|
||||
/** Movie */
|
||||
ATOM_MOOV = 0x6D6F6F76,
|
||||
/** Movie Header */
|
||||
ATOM_MVHD = 0x6D766864,
|
||||
/** Movie Extends */
|
||||
ATOM_MVEX = 0x6D766578,
|
||||
/** Movie Extends Header */
|
||||
ATOM_MEHD = 0x6D656864,
|
||||
/** Track Extends */
|
||||
ATOM_TREX = 0x74726578,
|
||||
/** Track */
|
||||
ATOM_TRAK = 0x7472616B,
|
||||
/** Track Header */
|
||||
ATOM_TKHD = 0x746B6864,
|
||||
/** Edit */
|
||||
ATOM_EDTS = 0x65647473,
|
||||
/** Edit List */
|
||||
ATOM_ELST = 0x656C7374,
|
||||
/** Media */
|
||||
ATOM_MDIA = 0x6D646961,
|
||||
/** Media Header */
|
||||
ATOM_MDHD = 0x6D646864,
|
||||
/** Handler Reference */
|
||||
ATOM_HDLR = 0x68646C72,
|
||||
/** Handler Type - Sound */
|
||||
ATOM_SOUN = 0x736F756E,
|
||||
/** Handler Type - Video */
|
||||
ATOM_VIDE = 0x76696465,
|
||||
/** Handler Type - Subtitle */
|
||||
ATOM_SUBT = 0x73756274,
|
||||
/** Media Information */
|
||||
ATOM_MINF = 0x6D696E66,
|
||||
/** Data Information */
|
||||
ATOM_DINF = 0x64696E66,
|
||||
/** Data Reference */
|
||||
ATOM_DREF = 0x64726566,
|
||||
/** Data Entry Url */
|
||||
ATOM_URL = 0x75726C20,
|
||||
/** Sample Table */
|
||||
ATOM_STBL = 0x7374626C,
|
||||
/** Sample Description */
|
||||
ATOM_STSD = 0x73747364,
|
||||
/** siDecompressionParam */
|
||||
ATOM_WAVE = 0x77617665,
|
||||
/** Format Atom */
|
||||
ATOM_FRMA = 0x66726D61,
|
||||
/** Audio Channel Layout Atom */
|
||||
ATOM_CHAN = 0x6368616E,
|
||||
/** Terminator Atom */
|
||||
ATOM_TERM = 0x00000000,
|
||||
/** MPEG-4 Elementary Stream Descriptor */
|
||||
ATOM_ESDS = 0x65736473,
|
||||
/** Time-to-sample Table */
|
||||
ATOM_STTS = 0x73747473,
|
||||
/** Sync Sample Table */
|
||||
ATOM_STSS = 0x73747373,
|
||||
/** Sample-to-chunk Table */
|
||||
ATOM_STSC = 0x73747363,
|
||||
/** Chunk Offset Table */
|
||||
ATOM_STCO = 0x7374636F,
|
||||
/** Sample Size Table */
|
||||
ATOM_STSZ = 0x7374737A,
|
||||
/** Sound Media Header */
|
||||
ATOM_SMHD = 0x736D6864,
|
||||
/** Segment Index Table */
|
||||
ATOM_SIDX = 0x73696478,
|
||||
/** Movie Fragment */
|
||||
ATOM_MOOF = 0x6D6F6F66,
|
||||
/** Movie Fragment Header */
|
||||
ATOM_MFHD = 0x6D666864,
|
||||
/** Track Fragment */
|
||||
ATOM_TRAF = 0x74726166,
|
||||
/** Track Fragment Header */
|
||||
ATOM_TFHD = 0x74666864,
|
||||
/** Track Fragment Run */
|
||||
ATOM_TRUN = 0x7472756E,
|
||||
/** Media Data */
|
||||
ATOM_MDAT = 0x6D646174,
|
||||
};
|
||||
|
||||
// These formats are the direct sub-children of the stsd atom.
|
||||
// https://mp4ra.org/#/codecs (+additions)
|
||||
enum class AudioSampleFormat {
|
||||
UNDEFINED = 0,
|
||||
A3DS = 0x61336473, // Auro-Cx 3D audio
|
||||
AC3 = 0x61632d33, // AC-3 audio
|
||||
AC4 = 0x61632d34, // AC-4 audio
|
||||
AGSM = 0x6167736d, // GSM
|
||||
ALAC = 0x616c6163, // Apple lossless audio codec
|
||||
ALAW = 0x616c6177, // a-Law
|
||||
CAVS = 0x63617673, // AVS2-P3 codec
|
||||
DRA1 = 0x64726131, // DRA Audio
|
||||
DTS_MINUS = 0x6474732d, // Dependent base layer for DTS layered audio
|
||||
DTS_PLUS = 0x6474732b, // Enhancement layer for DTS layered audio
|
||||
DTSC = 0x64747363, // Core Substream
|
||||
DTSE = 0x64747365, // Extension Substream containing only LBR
|
||||
DTSH = 0x64747368, // Core Substream + Extension Substream
|
||||
DTSL = 0x6474736c, // Extension Substream containing only XLL
|
||||
DTSX = 0x64747378, // DTS-UHD profile 2
|
||||
DTSY = 0x64747379, // DTS-UHD profile 3 or higher
|
||||
DVI = 0x64766920, // DVI (as used in RTP, 4:1 compression)
|
||||
EC3 = 0x65632d33, // Enhanced AC-3 audio
|
||||
ENCA = 0x656e6361, // Encrypted/Protected audio
|
||||
FL32 = 0x666c3332, // 32 bit float
|
||||
FL64 = 0x666c3634, // 64 bit float
|
||||
FLAC = 0x664c6143, // Free Lossless Audio Codec
|
||||
G719 = 0x67373139, // ITU-T Recommendation G.719 (2008)
|
||||
G726 = 0x67373236, // ITU-T Recommendation G.726 (1990)
|
||||
IMA4 = 0x696d6134, // IMA (International Multimedia Assocation, defunct, 4:1)
|
||||
IN24 = 0x696e3234, // 24 bit integer uncompressed
|
||||
IN32 = 0x696e3332, // 32 bit integer uncompressed
|
||||
LPCM = 0x6c70636d, // Uncompressed audio (various integer and float formats)
|
||||
M4AE = 0x6d346165, // MPEG-4 Audio Enhancement
|
||||
MHA1 = 0x6d686131, // MPEG-H Audio (single stream, unencapsulated)
|
||||
MHA2 = 0x6d686132, // MPEG-H Audio (multi-stream, unencapsulated)
|
||||
MHM1 = 0x6d686d31, // MPEG-H Audio (single stream, MHAS encapsulated)
|
||||
MHM2 = 0x6d686d32, // MPEG-H Audio (multi-stream, MHAS encapsulated)
|
||||
MLPA = 0x6d6c7061, // MLP Audio
|
||||
MP4A = 0x6d703461, // MPEG-4 Audio
|
||||
OPUS = 0x4f707573, // Opus audio coding
|
||||
QCLP = 0x51636c70, // Qualcomm PureVoice
|
||||
QDM2 = 0x51444d32, // Qdesign music 2
|
||||
QDMC = 0x51444d43, // Qdesign music 1
|
||||
RAW = 0x72617720, // Uncompressed audio
|
||||
SAMR = 0x73616d72, // Narrowband AMR voice
|
||||
SAWB = 0x73617762, // Wideband AMR voice
|
||||
SAWP = 0x73617770, // Extended AMR-WB (AMR-WB+)
|
||||
SEVC = 0x73657663, // EVRC Voice
|
||||
SEVS = 0x73657673, // Enhanced Voice Services (EVS)
|
||||
SQCP = 0x73716370, // 13K Voice
|
||||
SSMV = 0x73736d76, // SMV Voice
|
||||
TWOS = 0x74776f73, // Uncompressed 16-bit audio
|
||||
ULAW = 0x756c6177, // Samples have been compressed using uLaw 2:1.
|
||||
VDVA = 0x76647661, // DV audio (variable duration per video frame)
|
||||
};
|
||||
|
||||
// These are present in the DecoderConfigDescriptor tag in ESDS (for AudioSampleFormat::FORMAT_MP4A).
|
||||
// Source: https://mp4ra.org/#/codecs
|
||||
enum class MP4AObjectType {
|
||||
UNDEFINED = 0,
|
||||
_13K = 0xE1, // 13K Voice
|
||||
AAC_LC = 0x67, // ISO/IEC 13818-7 (AAC) Low Complexity Profile
|
||||
AAC_MAIN = 0x66, // ISO/IEC 13818-7 (AAC) Main Profile
|
||||
AAC_SSR = 0x68, // ISO/IEC 13818-7 (AAC) Scaleable Sampling Rate Profile
|
||||
AC3 = 0xA5, // AC-3
|
||||
AC3_ENH = 0xA6, // Enhanced AC-3
|
||||
AC4 = 0xAE, // AC-4
|
||||
AURO_CX_3D = 0xAF, // Auro-Cx 3D audio
|
||||
DRA = 0xA7, // DRA Audio
|
||||
DTS_CORE = 0xA9, // Core Substream
|
||||
DTS_CORE_EXT = 0xAA, // Core Substream + Extension Substream
|
||||
DTS_LBR = 0xAC, // Extension Substream containing only LBR
|
||||
DTS_UHD2 = 0xB2, // DTS-UHD profile 2
|
||||
DTS_UHD3 = 0xB3, // DTS-UHD profile 3 or higher
|
||||
DTS_XLL = 0xAB, // Extension Substream containing only XLL
|
||||
EVRC = 0xA0, // EVRC Voice
|
||||
G719 = 0xA8, // ITU G.719 Audio
|
||||
MP4A = 0x40, // ISO/IEC 14496-3 (MPEG-4 Audio)
|
||||
MPEG1 = 0x6B, // ISO/IEC 11172-3 (MPEG-1 Part 3)
|
||||
MPEG2 = 0x69, // ISO/IEC 13818-3 (MPEG-2 Part 3)
|
||||
OPUS = 0xAD, // Opus audio
|
||||
SMV = 0xA1, // SMV Voice
|
||||
VORBIS = 0xDD, // Vorbis
|
||||
};
|
||||
|
||||
// These are present in the DecoderSpecificInfo tag in ESDS (for MP4AObjectType::TYPE_MP4A).
|
||||
// Source: https://wiki.multimedia.cx/index.php/MPEG-4_Audio
|
||||
enum class MP4AProfile {
|
||||
UNDEFINED = 0,
|
||||
AAC_MAIN = 1, // AAC main
|
||||
AAC_LC = 2, // AAC LC
|
||||
AAC_SSR = 3, // AAC SSR
|
||||
AAC_LTP = 4, // AAC LTP
|
||||
SBR = 5, // SBR
|
||||
AAC_SCALABLE = 6, // AAC Scalable
|
||||
TWINVQ = 7, // TwinVQ
|
||||
CELP = 8, // CELP
|
||||
HVXC = 9, // HVXC
|
||||
TTSI = 12, // TTSI
|
||||
MAIN_SYNTHETIC = 13, // Main synthetic
|
||||
WAVETABLE_SYNTHESIS = 14, // Wavetable synthesis
|
||||
GENERAL_MIDI = 15, // General MIDI
|
||||
ALGORITHMIC_SYNTHESIS_AND_AUDIO_FX = 16, // Algorithmic Synthesis and Audio FX
|
||||
ER_AAC_LC = 17, // ER AAC LC
|
||||
ER_AAC_LTP = 19, // ER AAC LTP
|
||||
ER_AAC_SCALABLE = 20, // ER AAC Scalable
|
||||
ER_TWINVQ = 21, // ER TwinVQ
|
||||
ER_BSAC = 22, // ER BSAC
|
||||
ER_AAC_LD = 23, // ER AAC LD
|
||||
ER_CELP = 24, // ER CELP
|
||||
ER_HVXC = 25, // ER HVXC
|
||||
ER_HILN = 26, // ER HILN
|
||||
ER_PARAMETRIC = 27, // ER Parametric
|
||||
SSC = 28, // SSC
|
||||
LAYER_1 = 32, // Layer-1
|
||||
LAYER_2 = 33, // Layer-2
|
||||
LAYER_3 = 34, // Layer-3
|
||||
DST = 35, // DST
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseContainer.h"
|
||||
|
||||
enum class ElementId;
|
||||
|
||||
class WebmContainer : public BaseContainer {
|
||||
public:
|
||||
~WebmContainer();
|
||||
bool parse() override;
|
||||
int32_t getLoadingOffset(uint32_t timeMs) override;
|
||||
bool seekTo(uint32_t timeMs) override;
|
||||
int32_t getCurrentTimeMs() override;
|
||||
uint8_t *readSample(uint32_t &len) override;
|
||||
uint8_t *getSetupData(uint32_t &len, AudioCodec matchCodec) override;
|
||||
void feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) override;
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
uint32_t time;
|
||||
uint32_t offset;
|
||||
} CuePoint;
|
||||
|
||||
private:
|
||||
// used while parsing
|
||||
uint32_t esize;
|
||||
ElementId eid;
|
||||
// container parameters
|
||||
char *docType = nullptr;
|
||||
uint8_t audioTrackId = 255;
|
||||
float timescale = 0.0f;
|
||||
char *codecId = nullptr;
|
||||
uint8_t *codecPrivate = nullptr;
|
||||
uint32_t codecPrivateLen = 0;
|
||||
// container state
|
||||
CuePoint *cues = nullptr;
|
||||
uint16_t cuesLen = 0;
|
||||
uint32_t clusterEnd = 0;
|
||||
uint32_t clusterTime = 0;
|
||||
uint32_t currentTime = 0;
|
||||
bool isParsed = false;
|
||||
// buffer
|
||||
uint8_t *sampleData = nullptr;
|
||||
uint32_t sampleLen = 0;
|
||||
// lacing parameters
|
||||
uint32_t *laceSizes = nullptr;
|
||||
uint32_t *laceCurrent = nullptr;
|
||||
uint8_t laceLeft = 0;
|
||||
// set to read the current buffer instead of loading new frames
|
||||
uint16_t readOutFrameSize = 0;
|
||||
|
||||
private:
|
||||
uint32_t readVarNum32(bool raw = false);
|
||||
uint64_t readVarNum64();
|
||||
uint32_t readUint(uint8_t len);
|
||||
uint64_t readUlong(uint8_t len);
|
||||
float readFloat(uint8_t len);
|
||||
void readElem();
|
||||
void parseSegment(uint32_t start);
|
||||
void parseTrack(uint32_t end);
|
||||
void parseCuePoint(uint16_t idx, uint32_t end, uint32_t segmentStart);
|
||||
/**
|
||||
* Continue reading elements until a block is encountered.
|
||||
*
|
||||
* If [untilTime] is set, the method will keep reading until [currentTime]
|
||||
* is less than [untilTime]. Because of how WebM works, [pos] will be one frame later
|
||||
* than the requested time, although the container will report the correct position.
|
||||
*
|
||||
* @return size of the frame pointed by [pos]
|
||||
*/
|
||||
uint32_t readCluster(uint32_t untilTime = 0);
|
||||
/**
|
||||
* Parse a single block within a cluster. This method will populate lacing parameters if needed.
|
||||
* @param end offset of the next byte after this block
|
||||
* @return size of the frame pointed by [pos]
|
||||
*/
|
||||
uint32_t readBlock(uint32_t end);
|
||||
uint8_t *readFrame(uint32_t size, uint32_t &outLen);
|
||||
};
|
||||
@@ -0,0 +1,713 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class ElementId {
|
||||
/** [sub-elements] Set the EBML characteristics of the data to follow. Each EBML document has to start with this. */
|
||||
EBML = 0x1A45DFA3,
|
||||
/** [u-integer] The version of EBML parser used to create the file. */
|
||||
EBMLVersion = 0x4286,
|
||||
/** [u-integer] The minimum EBML version a parser has to support to read this file. */
|
||||
EBMLReadVersion = 0x42F7,
|
||||
/** [u-integer] The maximum length of the IDs you'll find in this file (4 or less in Matroska). */
|
||||
EBMLMaxIDLength = 0x42F2,
|
||||
/** [u-integer] The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not
|
||||
override the element size indicated at the beginning of an element. Elements that have an indicated size which is
|
||||
larger than what is allowed by EBMLMaxSizeLength shall be considered invalid. */
|
||||
EBMLMaxSizeLength = 0x42F3,
|
||||
/** [string] A string that describes the type of document that follows this EBML header ('matroska' in our case). */
|
||||
DocType = 0x4282,
|
||||
/** [u-integer] The version of DocType interpreter used to create the file. */
|
||||
DocTypeVersion = 0x4287,
|
||||
/** [u-integer] The minimum DocType version an interpreter has to support to read this file. */
|
||||
DocTypeReadVersion = 0x4285,
|
||||
/** [binary] The CRC is computed on all the data from the last CRC element (or start of the upper level element), up
|
||||
to the CRC element, including other previous CRC elements. All level 1 elements should include a CRC-32. */
|
||||
CRC32 = 0xBF,
|
||||
/** [binary] Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is
|
||||
discarded. Also used to reserve space in a sub-element for later use. */
|
||||
Void = 0xEC,
|
||||
/** [sub-elements] Contain signature of some (coming) elements in the stream. */
|
||||
SignatureSlot = 0x1B538667,
|
||||
/** [u-integer] Signature algorithm used (1=RSA, 2=elliptic). */
|
||||
SignatureAlgo = 0x7E8A,
|
||||
/** [u-integer] Hash algorithm used (1=SHA1-160, 2=MD5). */
|
||||
SignatureHash = 0x7E9A,
|
||||
/** [binary] The public key to use with the algorithm (in the case of a PKI-based signature). */
|
||||
SignaturePublicKey = 0x7EA5,
|
||||
/** [binary] The signature of the data (until a new. */
|
||||
Signature = 0x7EB5,
|
||||
/** [sub-elements] Contains elements that will be used to compute the signature. */
|
||||
SignatureElements = 0x7E5B,
|
||||
/** [sub-elements] A list consists of a number of consecutive elements that represent one case where data is used in
|
||||
signature. Ex: Cluster|Block|BlockAdditional means that the BlockAdditional of all Blocks in all Clusters is used
|
||||
for encryption. */
|
||||
SignatureElementList = 0x7E7B,
|
||||
/** [binary] An element ID whose data will be used to compute the signature. */
|
||||
SignedElement = 0x6532,
|
||||
|
||||
/* ebml_matroska.xml */
|
||||
/** [master] The Root Element that contains all other Top-Level Elements (Elements defined only at Level 1). A
|
||||
Matroska file is composed of 1 Segment. */
|
||||
Segment = 0x18538067,
|
||||
/** [master] Contains the Segment Position of other Top-Level Elements. */
|
||||
SeekHead = 0x114D9B74,
|
||||
/** [master] Contains a single seek entry to an EBML Element. */
|
||||
Seek = 0x4DBB,
|
||||
/** [binary] The binary ID corresponding to the Element name. */
|
||||
SeekID = 0x53AB,
|
||||
/** [uinteger] The Segment Position of the Element. */
|
||||
SeekPosition = 0x53AC,
|
||||
/** [master] Contains general information about the Segment. */
|
||||
Info = 0x1549A966,
|
||||
/** [binary] A randomly generated unique ID to identify the Segment amongst many others (128 bits). */
|
||||
SegmentUID = 0x73A4,
|
||||
/** [utf-8] A filename corresponding to this Segment. */
|
||||
SegmentFilename = 0x7384,
|
||||
/** [binary] A unique ID to identify the previous Segment of a Linked Segment (128 bits). */
|
||||
PrevUID = 0x3CB923,
|
||||
/** [utf-8] A filename corresponding to the file of the previous Linked Segment. */
|
||||
PrevFilename = 0x3C83AB,
|
||||
/** [binary] A unique ID to identify the next Segment of a Linked Segment (128 bits). */
|
||||
NextUID = 0x3EB923,
|
||||
/** [utf-8] A filename corresponding to the file of the next Linked Segment. */
|
||||
NextFilename = 0x3E83BB,
|
||||
/** [binary] A randomly generated unique ID that all Segments of a Linked Segment **MUST** share (128 bits). */
|
||||
SegmentFamily = 0x4444,
|
||||
/** [master] The mapping between this `Segment` and a segment value in the given Chapter Codec. */
|
||||
ChapterTranslate = 0x6924,
|
||||
/** [binary] The binary value used to represent this Segment in the chapter codec data. The format depends on the
|
||||
ChapProcessCodecID used; see (#chapprocesscodecid-element). */
|
||||
ChapterTranslateID = 0x69A5,
|
||||
/** [uinteger] This `ChapterTranslate` applies to this chapter codec of the given chapter edition(s); see
|
||||
(#chapprocesscodecid-element). */
|
||||
ChapterTranslateCodec = 0x69BF,
|
||||
/** [uinteger] Specify a chapter edition UID on which this `ChapterTranslate` applies. */
|
||||
ChapterTranslateEditionUID = 0x69FC,
|
||||
/** [uinteger] Timestamp scale in nanoseconds (1.000.000 means all timestamps in the Segment are expressed in
|
||||
milliseconds). */
|
||||
TimestampScale = 0x2AD7B1,
|
||||
/** [float] Duration of the Segment in nanoseconds based on TimestampScale. */
|
||||
Duration = 0x4489,
|
||||
/** [date] The date and time that the Segment was created by the muxing application or library. */
|
||||
DateUTC = 0x4461,
|
||||
/** [utf-8] General name of the Segment. */
|
||||
Title = 0x7BA9,
|
||||
/** [utf-8] Muxing application or library (example: "libmatroska-0.4.3"). */
|
||||
MuxingApp = 0x4D80,
|
||||
/** [utf-8] Writing application (example: "mkvmerge-0.3.3"). */
|
||||
WritingApp = 0x5741,
|
||||
/** [master] The Top-Level Element containing the (monolithic) Block structure. */
|
||||
Cluster = 0x1F43B675,
|
||||
/** [uinteger] Absolute timestamp of the cluster (based on TimestampScale). */
|
||||
Timestamp = 0xE7,
|
||||
/** [master] The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks
|
||||
on seeking or to decide what track to use. */
|
||||
SilentTracks = 0x5854,
|
||||
/** [uinteger] One of the track number that are not used from now on in the stream. It could change later if not
|
||||
specified as silent in a further Cluster. */
|
||||
SilentTrackNumber = 0x58D7,
|
||||
/** [uinteger] The Segment Position of the Cluster in the Segment (0 in live streams). It might help to
|
||||
resynchronise offset on damaged streams. */
|
||||
Position = 0xA7,
|
||||
/** [uinteger] Size of the previous Cluster, in octets. Can be useful for backward playing. */
|
||||
PrevSize = 0xAB,
|
||||
/** [binary] Similar to Block, see (#block-structure), but without all the extra information, mostly used to reduced
|
||||
overhead when no extra feature is needed; see (#simpleblock-structure) on SimpleBlock Structure. */
|
||||
SimpleBlock = 0xA3,
|
||||
/** [master] Basic container of information containing a single Block and information specific to that Block. */
|
||||
BlockGroup = 0xA0,
|
||||
/** [binary] Block containing the actual data to be rendered and a timestamp relative to the Cluster Timestamp; see
|
||||
(#block-structure) on Block Structure. */
|
||||
Block = 0xA1,
|
||||
/** [binary] A Block with no data. It **MUST** be stored in the stream at the place the real Block would be in
|
||||
display order. */
|
||||
BlockVirtual = 0xA2,
|
||||
/** [master] Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block
|
||||
structure could still see and use/skip these data. */
|
||||
BlockAdditions = 0x75A1,
|
||||
/** [master] Contain the BlockAdditional and some parameters. */
|
||||
BlockMore = 0xA6,
|
||||
/** [uinteger] An ID to identify the BlockAdditional level. If BlockAddIDType of the corresponding block is 0, this
|
||||
value is also the value of BlockAddIDType for the meaning of the content of BlockAdditional. */
|
||||
BlockAddID = 0xEE,
|
||||
/** [binary] Interpreted by the codec as it wishes (using the BlockAddID). */
|
||||
BlockAdditional = 0xA5,
|
||||
/** [uinteger] The duration of the Block (based on TimestampScale). The BlockDuration Element can be useful at the
|
||||
end of a Track to define the duration of the last frame (as there is no subsequent Block available), or when
|
||||
there is a break in a track like for subtitle tracks. */
|
||||
BlockDuration = 0x9B,
|
||||
/** [uinteger] This frame is referenced and has the specified cache priority. In cache only a frame of the same or
|
||||
higher priority can replace this frame. A value of 0 means the frame is not referenced. */
|
||||
ReferencePriority = 0xFA,
|
||||
/** [integer] A timestamp value, relative to the timestamp of the Block in this BlockGroup. This is used to
|
||||
reference other frames necessary to decode this frame. The relative value **SHOULD** correspond to a valid
|
||||
`Block` this `Block` depends on. Historically Matroska Writer didn't write the actual `Block(s)` this `Block`
|
||||
depends on, but *some* `Block` in the past. The value "0" **MAY** also be used to signify this `Block` cannot be
|
||||
decoded on its own, but without knownledge of which `Block` is necessary. In this case, other `ReferenceBlock`
|
||||
**MUST NOT** be found in the same `BlockGroup`. If the `BlockGroup` doesn't have any `ReferenceBlock` element,
|
||||
then the `Block` it contains can be decoded without using any other `Block` data. */
|
||||
ReferenceBlock = 0xFB,
|
||||
/** [integer] The Segment Position of the data that would otherwise be in position of the virtual block. */
|
||||
ReferenceVirtual = 0xFD,
|
||||
/** [binary] The new codec state to use. Data interpretation is private to the codec. This information **SHOULD**
|
||||
always be referenced by a seek entry. */
|
||||
CodecState = 0xA4,
|
||||
/** [integer] Duration in nanoseconds of the silent data added to the Block (padding at the end of the Block for
|
||||
positive value, at the beginning of the Block for negative value). The duration of DiscardPadding is not
|
||||
calculated in the duration of the TrackEntry and **SHOULD** be discarded during playback. */
|
||||
DiscardPadding = 0x75A2,
|
||||
/** [master] Contains slices description. */
|
||||
Slices = 0x8E,
|
||||
/** [master] Contains extra time information about the data contained in the Block. Being able to interpret this
|
||||
Element is not **REQUIRED** for playback. */
|
||||
TimeSlice = 0xE8,
|
||||
/** [uinteger] The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). Being
|
||||
able to interpret this Element is not **REQUIRED** for playback. */
|
||||
LaceNumber = 0xCC,
|
||||
/** [uinteger] The number of the frame to generate from this lace with this delay (allow you to generate many frames
|
||||
from the same Block/Frame). */
|
||||
FrameNumber = 0xCD,
|
||||
/** [uinteger] The ID of the BlockAdditional Element (0 is the main Block). */
|
||||
BlockAdditionID = 0xCB,
|
||||
/** [uinteger] The (scaled) delay to apply to the Element. */
|
||||
Delay = 0xCE,
|
||||
/** [uinteger] The (scaled) duration to apply to the Element. */
|
||||
SliceDuration = 0xCF,
|
||||
/** [master] Contains information about the last reference frame. See [@?DivXTrickTrack]. */
|
||||
ReferenceFrame = 0xC8,
|
||||
/** [uinteger] The relative offset, in bytes, from the previous BlockGroup element for this Smooth FF/RW video track
|
||||
to the containing BlockGroup element. See [@?DivXTrickTrack]. */
|
||||
ReferenceOffset = 0xC9,
|
||||
/** [uinteger] The timecode of the BlockGroup pointed to by ReferenceOffset. See [@?DivXTrickTrack]. */
|
||||
ReferenceTimestamp = 0xCA,
|
||||
/** [binary] Similar to SimpleBlock, see (#simpleblock-structure), but the data inside the Block are Transformed
|
||||
(encrypt and/or signed). */
|
||||
EncryptedBlock = 0xAF,
|
||||
/** [master] A Top-Level Element of information with many tracks described. */
|
||||
Tracks = 0x1654AE6B,
|
||||
/** [master] Describes a track with all Elements. */
|
||||
TrackEntry = 0xAE,
|
||||
/** [uinteger] The track number as used in the Block Header (using more than 127 tracks is not encouraged, though
|
||||
the design allows an unlimited number). */
|
||||
TrackNumber = 0xD7,
|
||||
/** [uinteger] A unique ID to identify the Track. */
|
||||
TrackUID = 0x73C5,
|
||||
/** [uinteger] The `TrackType` defines the type of each frame found in the Track. The value **SHOULD** be stored on
|
||||
1 octet. */
|
||||
TrackType = 0x83,
|
||||
/** [uinteger] Set to 1 if the track is usable. It is possible to turn a not usable track into a usable track using
|
||||
chapter codecs or control tracks. */
|
||||
FlagEnabled = 0xB9,
|
||||
/** [uinteger] Set if that track (audio, video or subs) **SHOULD** be eligible for automatic selection by the
|
||||
player; see (#default-track-selection) for more details. */
|
||||
FlagDefault = 0x88,
|
||||
/** [uinteger] Applies only to subtitles. Set if that track **SHOULD** be eligible for automatic selection by the
|
||||
player if it matches the user's language preference, even if the user's preferences would normally not enable
|
||||
subtitles with the selected audio track; this can be used for tracks containing only translations of
|
||||
foreign-language audio or onscreen text. See (#default-track-selection) for more details. */
|
||||
FlagForced = 0x55AA,
|
||||
/** [uinteger] Set to 1 if that track is suitable for users with hearing impairments, set to 0 if it is unsuitable
|
||||
for users with hearing impairments. */
|
||||
FlagHearingImpaired = 0x55AB,
|
||||
/** [uinteger] Set to 1 if that track is suitable for users with visual impairments, set to 0 if it is unsuitable
|
||||
for users with visual impairments. */
|
||||
FlagVisualImpaired = 0x55AC,
|
||||
/** [uinteger] Set to 1 if that track contains textual descriptions of video content, set to 0 if that track does
|
||||
not contain textual descriptions of video content. */
|
||||
FlagTextDescriptions = 0x55AD,
|
||||
/** [uinteger] Set to 1 if that track is in the content's original language, set to 0 if it is a translation. */
|
||||
FlagOriginal = 0x55AE,
|
||||
/** [uinteger] Set to 1 if that track contains commentary, set to 0 if it does not contain commentary. */
|
||||
FlagCommentary = 0x55AF,
|
||||
/** [uinteger] Set to 1 if the track **MAY** contain blocks using lacing. When set to 0 all blocks **MUST** have
|
||||
their lacing flags set to No lacing; see (#block-lacing) on Block Lacing. */
|
||||
FlagLacing = 0x9C,
|
||||
/** [uinteger] The minimum number of frames a player **SHOULD** be able to cache during playback. If set to 0, the
|
||||
reference pseudo-cache system is not used. */
|
||||
MinCache = 0x6DE7,
|
||||
/** [uinteger] The maximum cache size necessary to store referenced frames in and the current frame. 0 means no
|
||||
cache is needed. */
|
||||
MaxCache = 0x6DF8,
|
||||
/** [uinteger] Number of nanoseconds (not scaled via TimestampScale) per frame (frame in the Matroska sense -- one
|
||||
Element put into a (Simple)Block). */
|
||||
DefaultDuration = 0x23E383,
|
||||
/** [uinteger] The period in nanoseconds (not scaled by TimestampScale) between two successive fields at the output
|
||||
of the decoding process, see (#defaultdecodedfieldduration) for more information */
|
||||
DefaultDecodedFieldDuration = 0x234E7A,
|
||||
/** [float] DEPRECATED, DO NOT USE. The scale to apply on this track to work at normal speed in relation with other
|
||||
tracks (mostly used to adjust video speed when the audio length differs). */
|
||||
TrackTimestampScale = 0x23314F,
|
||||
/** [integer] A value to add to the Block's Timestamp. This can be used to adjust the playback offset of a track. */
|
||||
TrackOffset = 0x537F,
|
||||
/** [uinteger] The maximum value of BlockAddID ((#blockaddid-element)). A value 0 means there is no BlockAdditions
|
||||
((#blockadditions-element)) for this track. */
|
||||
MaxBlockAdditionID = 0x55EE,
|
||||
/** [master] Contains elements that extend the track format, by adding content either to each frame, with BlockAddID
|
||||
((#blockaddid-element)), or to the track as a whole with BlockAddIDExtraData. */
|
||||
BlockAdditionMapping = 0x41E4,
|
||||
/** [uinteger] If the track format extension needs content beside frames, the value refers to the BlockAddID
|
||||
((#blockaddid-element)), value being described. To keep MaxBlockAdditionID as low as possible, small values
|
||||
**SHOULD** be used. */
|
||||
BlockAddIDValue = 0x41F0,
|
||||
/** [string] A human-friendly name describing the type of BlockAdditional data, as defined by the associated Block
|
||||
Additional Mapping. */
|
||||
BlockAddIDName = 0x41A4,
|
||||
/** [uinteger] Stores the registered identifier of the Block Additional Mapping to define how the BlockAdditional
|
||||
data should be handled. */
|
||||
BlockAddIDType = 0x41E7,
|
||||
/** [binary] Extra binary data that the BlockAddIDType can use to interpret the BlockAdditional data. The
|
||||
interpretation of the binary data depends on the BlockAddIDType value and the corresponding Block Additional
|
||||
Mapping. */
|
||||
BlockAddIDExtraData = 0x41ED,
|
||||
/** [utf-8] A human-readable track name. */
|
||||
Name = 0x536E,
|
||||
/** [string] Specifies the language of the track in the Matroska languages form; see (#language-codes) on language
|
||||
codes. This Element **MUST** be ignored if the LanguageIETF Element is used in the same TrackEntry. */
|
||||
Language = 0x22B59C,
|
||||
/** [string] Specifies the language of the track according to [@!BCP47] and using the IANA Language Subtag Registry
|
||||
[@!IANALangRegistry]. If this Element is used, then any Language Elements used in the same TrackEntry **MUST** be
|
||||
ignored. */
|
||||
LanguageIETF = 0x22B59D,
|
||||
/** [string] An ID corresponding to the codec, see [@!MatroskaCodec] for more info. */
|
||||
CodecID = 0x86,
|
||||
/** [binary] Private data only known to the codec. */
|
||||
CodecPrivate = 0x63A2,
|
||||
/** [utf-8] A human-readable string specifying the codec. */
|
||||
CodecName = 0x258688,
|
||||
/** [uinteger] The UID of an attachment that is used by this codec. */
|
||||
AttachmentLink = 0x7446,
|
||||
/** [utf-8] A string describing the encoding setting used. */
|
||||
CodecSettings = 0x3A9697,
|
||||
/** [string] A URL to find information about the codec used. */
|
||||
CodecInfoURL = 0x3B4040,
|
||||
/** [string] A URL to download about the codec used. */
|
||||
CodecDownloadURL = 0x26B240,
|
||||
/** [uinteger] Set to 1 if the codec can decode potentially damaged data. */
|
||||
CodecDecodeAll = 0xAA,
|
||||
/** [uinteger] Specify that this track is an overlay track for the Track specified (in the u-integer). That means
|
||||
when this track has a gap, see (#silenttracks-element) on SilentTracks, the overlay track **SHOULD** be used
|
||||
instead. The order of multiple TrackOverlay matters, the first one is the one that **SHOULD** be used. If not
|
||||
found it **SHOULD** be the second, etc. */
|
||||
TrackOverlay = 0x6FAB,
|
||||
/** [uinteger] CodecDelay is The codec-built-in delay in nanoseconds. This value **MUST** be subtracted from each
|
||||
block timestamp in order to get the actual timestamp. The value **SHOULD** be small so the muxing of tracks with
|
||||
the same actual timestamp are in the same Cluster. */
|
||||
CodecDelay = 0x56AA,
|
||||
/** [uinteger] After a discontinuity, SeekPreRoll is the duration in nanoseconds of the data the decoder **MUST**
|
||||
decode before the decoded data is valid. */
|
||||
SeekPreRoll = 0x56BB,
|
||||
/** [master] The mapping between this `TrackEntry` and a track value in the given Chapter Codec. */
|
||||
TrackTranslate = 0x6624,
|
||||
/** [binary] The binary value used to represent this `TrackEntry` in the chapter codec data. The format depends on
|
||||
the `ChapProcessCodecID` used; see (#chapprocesscodecid-element). */
|
||||
TrackTranslateTrackID = 0x66A5,
|
||||
/** [uinteger] This `TrackTranslate` applies to this chapter codec of the given chapter edition(s); see
|
||||
(#chapprocesscodecid-element). */
|
||||
TrackTranslateCodec = 0x66BF,
|
||||
/** [uinteger] Specify a chapter edition UID on which this `TrackTranslate` applies. */
|
||||
TrackTranslateEditionUID = 0x66FC,
|
||||
/** [master] Video settings. */
|
||||
Video = 0xE0,
|
||||
/** [uinteger] Specify whether the video frames in this track are interlaced or not. */
|
||||
FlagInterlaced = 0x9A,
|
||||
/** [uinteger] Specify the field ordering of video frames in this track. */
|
||||
FieldOrder = 0x9D,
|
||||
/** [uinteger] Stereo-3D video mode. There are some more details in (#multi-planar-and-3d-videos). */
|
||||
StereoMode = 0x53B8,
|
||||
/** [uinteger] Alpha Video Mode. Presence of this Element indicates that the BlockAdditional Element could contain
|
||||
Alpha data. */
|
||||
AlphaMode = 0x53C0,
|
||||
/** [uinteger] DEPRECATED, DO NOT USE. Bogus StereoMode value used in old versions of libmatroska. */
|
||||
OldStereoMode = 0x53B9,
|
||||
/** [uinteger] Width of the encoded video frames in pixels. */
|
||||
PixelWidth = 0xB0,
|
||||
/** [uinteger] Height of the encoded video frames in pixels. */
|
||||
PixelHeight = 0xBA,
|
||||
/** [uinteger] The number of video pixels to remove at the bottom of the image. */
|
||||
PixelCropBottom = 0x54AA,
|
||||
/** [uinteger] The number of video pixels to remove at the top of the image. */
|
||||
PixelCropTop = 0x54BB,
|
||||
/** [uinteger] The number of video pixels to remove on the left of the image. */
|
||||
PixelCropLeft = 0x54CC,
|
||||
/** [uinteger] The number of video pixels to remove on the right of the image. */
|
||||
PixelCropRight = 0x54DD,
|
||||
/** [uinteger] Width of the video frames to display. Applies to the video frame after cropping (PixelCrop*
|
||||
Elements). */
|
||||
DisplayWidth = 0x54B0,
|
||||
/** [uinteger] Height of the video frames to display. Applies to the video frame after cropping (PixelCrop*
|
||||
Elements). */
|
||||
DisplayHeight = 0x54BA,
|
||||
/** [uinteger] How DisplayWidth & DisplayHeight are interpreted. */
|
||||
DisplayUnit = 0x54B2,
|
||||
/** [uinteger] Specify the possible modifications to the aspect ratio. */
|
||||
AspectRatioType = 0x54B3,
|
||||
/** [binary] Specify the uncompressed pixel format used for the Track's data as a FourCC. This value is similar in
|
||||
scope to the biCompression value of AVI's `BITMAPINFO` [@?AVIFormat]. See the YUV video formats [@?FourCC-YUV]
|
||||
and RGB video formats [@?FourCC-RGB] for common values. */
|
||||
UncompressedFourCC = 0x2EB524,
|
||||
/** [float] Gamma Value. */
|
||||
GammaValue = 0x2FB523,
|
||||
/** [float] Number of frames per second. This value is Informational only. It is intended for constant frame rate
|
||||
streams, and **SHOULD NOT** be used for a variable frame rate TrackEntry. */
|
||||
FrameRate = 0x2383E3,
|
||||
/** [master] Settings describing the colour format. */
|
||||
Colour = 0x55B0,
|
||||
/** [uinteger] The Matrix Coefficients of the video used to derive luma and chroma values from red, green, and blue
|
||||
color primaries. For clarity, the value and meanings for MatrixCoefficients are adopted from Table 4 of ISO/IEC
|
||||
23001-8:2016 or ITU-T H.273. */
|
||||
MatrixCoefficients = 0x55B1,
|
||||
/** [uinteger] Number of decoded bits per channel. A value of 0 indicates that the BitsPerChannel is unspecified. */
|
||||
BitsPerChannel = 0x55B2,
|
||||
/** [uinteger] The amount of pixels to remove in the Cr and Cb channels for every pixel not removed horizontally.
|
||||
Example: For video with 4:2:0 chroma subsampling, the ChromaSubsamplingHorz **SHOULD** be set to 1. */
|
||||
ChromaSubsamplingHorz = 0x55B3,
|
||||
/** [uinteger] The amount of pixels to remove in the Cr and Cb channels for every pixel not removed vertically.
|
||||
Example: For video with 4:2:0 chroma subsampling, the ChromaSubsamplingVert **SHOULD** be set to 1. */
|
||||
ChromaSubsamplingVert = 0x55B4,
|
||||
/** [uinteger] The amount of pixels to remove in the Cb channel for every pixel not removed horizontally. This is
|
||||
additive with ChromaSubsamplingHorz. Example: For video with 4:2:1 chroma subsampling, the ChromaSubsamplingHorz
|
||||
**SHOULD** be set to 1 and CbSubsamplingHorz **SHOULD** be set to 1. */
|
||||
CbSubsamplingHorz = 0x55B5,
|
||||
/** [uinteger] The amount of pixels to remove in the Cb channel for every pixel not removed vertically. This is
|
||||
additive with ChromaSubsamplingVert. */
|
||||
CbSubsamplingVert = 0x55B6,
|
||||
/** [uinteger] How chroma is subsampled horizontally. */
|
||||
ChromaSitingHorz = 0x55B7,
|
||||
/** [uinteger] How chroma is subsampled vertically. */
|
||||
ChromaSitingVert = 0x55B8,
|
||||
/** [uinteger] Clipping of the color ranges. */
|
||||
Range = 0x55B9,
|
||||
/** [uinteger] The transfer characteristics of the video. For clarity, the value and meanings for
|
||||
TransferCharacteristics are adopted from Table 3 of ISO/IEC 23091-4 or ITU-T H.273. */
|
||||
TransferCharacteristics = 0x55BA,
|
||||
/** [uinteger] The colour primaries of the video. For clarity, the value and meanings for Primaries are adopted from
|
||||
Table 2 of ISO/IEC 23091-4 or ITU-T H.273. */
|
||||
Primaries = 0x55BB,
|
||||
/** [uinteger] Maximum brightness of a single pixel (Maximum Content Light Level) in candelas per square meter
|
||||
(cd/m^2^). */
|
||||
MaxCLL = 0x55BC,
|
||||
/** [uinteger] Maximum brightness of a single full frame (Maximum Frame-Average Light Level) in candelas per square
|
||||
meter (cd/m^2^). */
|
||||
MaxFALL = 0x55BD,
|
||||
/** [master] SMPTE 2086 mastering data. */
|
||||
MasteringMetadata = 0x55D0,
|
||||
/** [float] Red X chromaticity coordinate, as defined by CIE 1931. */
|
||||
PrimaryRChromaticityX = 0x55D1,
|
||||
/** [float] Red Y chromaticity coordinate, as defined by CIE 1931. */
|
||||
PrimaryRChromaticityY = 0x55D2,
|
||||
/** [float] Green X chromaticity coordinate, as defined by CIE 1931. */
|
||||
PrimaryGChromaticityX = 0x55D3,
|
||||
/** [float] Green Y chromaticity coordinate, as defined by CIE 1931. */
|
||||
PrimaryGChromaticityY = 0x55D4,
|
||||
/** [float] Blue X chromaticity coordinate, as defined by CIE 1931. */
|
||||
PrimaryBChromaticityX = 0x55D5,
|
||||
/** [float] Blue Y chromaticity coordinate, as defined by CIE 1931. */
|
||||
PrimaryBChromaticityY = 0x55D6,
|
||||
/** [float] White X chromaticity coordinate, as defined by CIE 1931. */
|
||||
WhitePointChromaticityX = 0x55D7,
|
||||
/** [float] White Y chromaticity coordinate, as defined by CIE 1931. */
|
||||
WhitePointChromaticityY = 0x55D8,
|
||||
/** [float] Maximum luminance. Represented in candelas per square meter (cd/m^2^). */
|
||||
LuminanceMax = 0x55D9,
|
||||
/** [float] Minimum luminance. Represented in candelas per square meter (cd/m^2^). */
|
||||
LuminanceMin = 0x55DA,
|
||||
/** [master] Describes the video projection details. Used to render spherical, VR videos or flipping videos
|
||||
horizontally/vertically. */
|
||||
Projection = 0x7670,
|
||||
/** [uinteger] Describes the projection used for this video track. */
|
||||
ProjectionType = 0x7671,
|
||||
/** [binary] Private data that only applies to a specific projection. * If `ProjectionType` equals 0
|
||||
(Rectangular), then this element must not be present. * If `ProjectionType` equals 1 (Equirectangular),
|
||||
then this element must be present and contain the same binary data that would be stored inside an ISOBMFF
|
||||
Equirectangular Projection Box ('equi'). * If `ProjectionType` equals 2 (Cubemap), then this element must be
|
||||
present and contain the same binary data that would be stored inside an ISOBMFF Cubemap Projection Box
|
||||
('cbmp'). * If `ProjectionType` equals 3 (Mesh), then this element must be present and contain the same binary
|
||||
data that would be stored inside an ISOBMFF Mesh Projection Box ('mshp'). */
|
||||
ProjectionPrivate = 0x7672,
|
||||
/** [float] Specifies a yaw rotation to the projection. Value represents a clockwise rotation, in degrees, around
|
||||
the up vector. This rotation must be applied before any `ProjectionPosePitch` or `ProjectionPoseRoll` rotations.
|
||||
The value of this element **MUST** be in the -180 to 180 degree range, both included. Setting
|
||||
`ProjectionPoseYaw` to 180 or -180 degrees, with the `ProjectionPoseRoll` and `ProjectionPosePitch` set to 0
|
||||
degrees flips the image horizontally. */
|
||||
ProjectionPoseYaw = 0x7673,
|
||||
/** [float] Specifies a pitch rotation to the projection. Value represents a counter-clockwise rotation, in
|
||||
degrees, around the right vector. This rotation must be applied after the `ProjectionPoseYaw` rotation and before
|
||||
the `ProjectionPoseRoll` rotation. The value of this element **MUST** be in the -90 to 90 degree range, both
|
||||
included. */
|
||||
ProjectionPosePitch = 0x7674,
|
||||
/** [float] Specifies a roll rotation to the projection. Value represents a counter-clockwise rotation, in degrees,
|
||||
around the forward vector. This rotation must be applied after the `ProjectionPoseYaw` and `ProjectionPosePitch`
|
||||
rotations. The value of this element **MUST** be in the -180 to 180 degree range, both included. Setting
|
||||
`ProjectionPoseRoll` to 180 or -180 degrees, the `ProjectionPoseYaw` to 180 or -180 degrees with
|
||||
`ProjectionPosePitch` set to 0 degrees flips the image vertically. Setting `ProjectionPoseRoll` to 180 or -180
|
||||
degrees, with the `ProjectionPoseYaw` and `ProjectionPosePitch` set to 0 degrees flips the image horizontally and
|
||||
vertically. */
|
||||
ProjectionPoseRoll = 0x7675,
|
||||
/** [master] Audio settings. */
|
||||
Audio = 0xE1,
|
||||
/** [float] Sampling frequency in Hz. */
|
||||
SamplingFrequency = 0xB5,
|
||||
/** [float] Real output sampling frequency in Hz (used for SBR techniques). */
|
||||
OutputSamplingFrequency = 0x78B5,
|
||||
/** [uinteger] Numbers of channels in the track. */
|
||||
Channels = 0x9F,
|
||||
/** [binary] Table of horizontal angles for each successive channel. */
|
||||
ChannelPositions = 0x7D7B,
|
||||
/** [uinteger] Bits per sample, mostly used for PCM. */
|
||||
BitDepth = 0x6264,
|
||||
/** [master] Operation that needs to be applied on tracks to create this virtual track. For more details look at
|
||||
(#track-operation). */
|
||||
TrackOperation = 0xE2,
|
||||
/** [master] Contains the list of all video plane tracks that need to be combined to create this 3D track */
|
||||
TrackCombinePlanes = 0xE3,
|
||||
/** [master] Contains a video plane track that need to be combined to create this 3D track */
|
||||
TrackPlane = 0xE4,
|
||||
/** [uinteger] The trackUID number of the track representing the plane. */
|
||||
TrackPlaneUID = 0xE5,
|
||||
/** [uinteger] The kind of plane this track corresponds to. */
|
||||
TrackPlaneType = 0xE6,
|
||||
/** [master] Contains the list of all tracks whose Blocks need to be combined to create this virtual track */
|
||||
TrackJoinBlocks = 0xE9,
|
||||
/** [uinteger] The trackUID number of a track whose blocks are used to create this virtual track. */
|
||||
TrackJoinUID = 0xED,
|
||||
/** [uinteger] The TrackUID of the Smooth FF/RW video in the paired EBML structure corresponding to this video
|
||||
track. See [@?DivXTrickTrack]. */
|
||||
TrickTrackUID = 0xC0,
|
||||
/** [binary] The SegmentUID of the Segment containing the track identified by TrickTrackUID. See [@?DivXTrickTrack].
|
||||
*/
|
||||
TrickTrackSegmentUID = 0xC1,
|
||||
/** [uinteger] Set to 1 if this video track is a Smooth FF/RW track. If set to 1, MasterTrackUID and
|
||||
MasterTrackSegUID should must be present and BlockGroups for this track must contain ReferenceFrame structures.
|
||||
Otherwise, TrickTrackUID and TrickTrackSegUID must be present if this track has a corresponding Smooth FF/RW
|
||||
track. See [@?DivXTrickTrack]. */
|
||||
TrickTrackFlag = 0xC6,
|
||||
/** [uinteger] The TrackUID of the video track in the paired EBML structure that corresponds to this Smooth FF/RW
|
||||
track. See [@?DivXTrickTrack]. */
|
||||
TrickMasterTrackUID = 0xC7,
|
||||
/** [binary] The SegmentUID of the Segment containing the track identified by MasterTrackUID. See
|
||||
[@?DivXTrickTrack]. */
|
||||
TrickMasterTrackSegmentUID = 0xC4,
|
||||
/** [master] Settings for several content encoding mechanisms like compression or encryption. */
|
||||
ContentEncodings = 0x6D80,
|
||||
/** [master] Settings for one content encoding like compression or encryption. */
|
||||
ContentEncoding = 0x6240,
|
||||
/** [uinteger] Tells when this modification was used during encoding/muxing starting with 0 and counting upwards.
|
||||
The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to
|
||||
be unique over all ContentEncodingOrder Elements in the TrackEntry that contains this ContentEncodingOrder
|
||||
element. */
|
||||
ContentEncodingOrder = 0x5031,
|
||||
/** [uinteger] A bit field that describes which Elements have been modified in this way. Values (big-endian) can be
|
||||
OR'ed. */
|
||||
ContentEncodingScope = 0x5032,
|
||||
/** [uinteger] A value describing what kind of transformation is applied. */
|
||||
ContentEncodingType = 0x5033,
|
||||
/** [master] Settings describing the compression used. This Element **MUST** be present if the value of
|
||||
ContentEncodingType is 0 and absent otherwise. Each block **MUST** be decompressable even if no previous block is
|
||||
available in order not to prevent seeking. */
|
||||
ContentCompression = 0x5034,
|
||||
/** [uinteger] The compression algorithm used. */
|
||||
ContentCompAlgo = 0x4254,
|
||||
/** [binary] Settings that might be needed by the decompressor. For Header Stripping (`ContentCompAlgo`=3), the
|
||||
bytes that were removed from the beginning of each frames of the track. */
|
||||
ContentCompSettings = 0x4255,
|
||||
/** [master] Settings describing the encryption used. This Element **MUST** be present if the value of
|
||||
`ContentEncodingType` is 1 (encryption) and **MUST** be ignored otherwise. */
|
||||
ContentEncryption = 0x5035,
|
||||
/** [uinteger] The encryption algorithm used. The value "0" means that the contents have not been encrypted. */
|
||||
ContentEncAlgo = 0x47E1,
|
||||
/** [binary] For public key algorithms this is the ID of the public key the the data was encrypted with. */
|
||||
ContentEncKeyID = 0x47E2,
|
||||
/** [master] Settings describing the encryption algorithm used. If `ContentEncAlgo` != 5 this **MUST** be ignored.
|
||||
*/
|
||||
ContentEncAESSettings = 0x47E7,
|
||||
/** [uinteger] The AES cipher mode used in the encryption. */
|
||||
AESSettingsCipherMode = 0x47E8,
|
||||
/** [binary] A cryptographic signature of the contents. */
|
||||
ContentSignature = 0x47E3,
|
||||
/** [binary] This is the ID of the private key the data was signed with. */
|
||||
ContentSigKeyID = 0x47E4,
|
||||
/** [uinteger] The algorithm used for the signature. */
|
||||
ContentSigAlgo = 0x47E5,
|
||||
/** [uinteger] The hash algorithm used for the signature. */
|
||||
ContentSigHashAlgo = 0x47E6,
|
||||
/** [master] A Top-Level Element to speed seeking access. All entries are local to the Segment. */
|
||||
Cues = 0x1C53BB6B,
|
||||
/** [master] Contains all information relative to a seek point in the Segment. */
|
||||
CuePoint = 0xBB,
|
||||
/** [uinteger] Absolute timestamp according to the Segment time base. */
|
||||
CueTime = 0xB3,
|
||||
/** [master] Contain positions for different tracks corresponding to the timestamp. */
|
||||
CueTrackPositions = 0xB7,
|
||||
/** [uinteger] The track for which a position is given. */
|
||||
CueTrack = 0xF7,
|
||||
/** [uinteger] The Segment Position of the Cluster containing the associated Block. */
|
||||
CueClusterPosition = 0xF1,
|
||||
/** [uinteger] The relative position inside the Cluster of the referenced SimpleBlock or BlockGroup with 0 being the
|
||||
first possible position for an Element inside that Cluster. */
|
||||
CueRelativePosition = 0xF0,
|
||||
/** [uinteger] The duration of the block according to the Segment time base. If missing the track's DefaultDuration
|
||||
does not apply and no duration information is available in terms of the cues. */
|
||||
CueDuration = 0xB2,
|
||||
/** [uinteger] Number of the Block in the specified Cluster. */
|
||||
CueBlockNumber = 0x5378,
|
||||
/** [uinteger] The Segment Position of the Codec State corresponding to this Cue Element. 0 means that the data is
|
||||
taken from the initial Track Entry. */
|
||||
CueCodecState = 0xEA,
|
||||
/** [master] The Clusters containing the referenced Blocks. */
|
||||
CueReference = 0xDB,
|
||||
/** [uinteger] Timestamp of the referenced Block. */
|
||||
CueRefTime = 0x96,
|
||||
/** [uinteger] The Segment Position of the Cluster containing the referenced Block. */
|
||||
CueRefCluster = 0x97,
|
||||
/** [uinteger] Number of the referenced Block of Track X in the specified Cluster. */
|
||||
CueRefNumber = 0x535F,
|
||||
/** [uinteger] The Segment Position of the Codec State corresponding to this referenced Element. 0 means that the
|
||||
data is taken from the initial Track Entry. */
|
||||
CueRefCodecState = 0xEB,
|
||||
/** [master] Contain attached files. */
|
||||
Attachments = 0x1941A469,
|
||||
/** [master] An attached file. */
|
||||
AttachedFile = 0x61A7,
|
||||
/** [utf-8] A human-friendly name for the attached file. */
|
||||
FileDescription = 0x467E,
|
||||
/** [utf-8] Filename of the attached file. */
|
||||
FileName = 0x466E,
|
||||
/** [string] MIME type of the file. */
|
||||
FileMimeType = 0x4660,
|
||||
/** [binary] The data of the file. */
|
||||
FileData = 0x465C,
|
||||
/** [uinteger] Unique ID representing the file, as random as possible. */
|
||||
FileUID = 0x46AE,
|
||||
/** [binary] A binary value that a track/codec can refer to when the attachment is needed. */
|
||||
FileReferral = 0x4675,
|
||||
/** [uinteger] The timecode at which this optimized font attachment comes into context, based on the Segment
|
||||
TimecodeScale. This element is reserved for future use and if written must be the segment start time. See
|
||||
[@?DivXWorldFonts]. */
|
||||
FileUsedStartTime = 0x4661,
|
||||
/** [uinteger] The timecode at which this optimized font attachment goes out of context, based on the Segment
|
||||
TimecodeScale. This element is reserved for future use and if written must be the segment end time. See
|
||||
[@?DivXWorldFonts]. */
|
||||
FileUsedEndTime = 0x4662,
|
||||
/** [master] A system to define basic menus and partition data. For more detailed information, look at the Chapters
|
||||
explanation in (#chapters). */
|
||||
Chapters = 0x1043A770,
|
||||
/** [master] Contains all information about a Segment edition. */
|
||||
EditionEntry = 0x45B9,
|
||||
/** [uinteger] A unique ID to identify the edition. It's useful for tagging an edition. */
|
||||
EditionUID = 0x45BC,
|
||||
/** [uinteger] Set to 1 if an edition is hidden. Hidden editions **SHOULD NOT** be available to the user interface
|
||||
(but still to Control Tracks; see (#chapter-flags) on Chapter flags). */
|
||||
EditionFlagHidden = 0x45BD,
|
||||
/** [uinteger] Set to 1 if the edition **SHOULD** be used as the default one. */
|
||||
EditionFlagDefault = 0x45DB,
|
||||
/** [uinteger] Set to 1 if the chapters can be defined multiple times and the order to play them is enforced; see
|
||||
(#editionflagordered). */
|
||||
EditionFlagOrdered = 0x45DD,
|
||||
/** [master] Contains the atom information to use as the chapter atom (apply to all tracks). */
|
||||
ChapterAtom = 0xB6,
|
||||
/** [uinteger] A unique ID to identify the Chapter. */
|
||||
ChapterUID = 0x73C4,
|
||||
/** [utf-8] A unique string ID to identify the Chapter. Use for WebVTT cue identifier storage [@!WebVTT]. */
|
||||
ChapterStringUID = 0x5654,
|
||||
/** [uinteger] Timestamp of the start of Chapter (not scaled). */
|
||||
ChapterTimeStart = 0x91,
|
||||
/** [uinteger] Timestamp of the end of Chapter (timestamp excluded, not scaled). The value **MUST** be greater than
|
||||
or equal to the `ChapterTimeStart` of the same `ChapterAtom`. */
|
||||
ChapterTimeEnd = 0x92,
|
||||
/** [uinteger] Set to 1 if a chapter is hidden. Hidden chapters **SHOULD NOT** be available to the user interface
|
||||
(but still to Control Tracks; see (#chapterflaghidden) on Chapter flags). */
|
||||
ChapterFlagHidden = 0x98,
|
||||
/** [uinteger] Set to 1 if the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the
|
||||
movie **SHOULD** skip all the content between the TimeStart and TimeEnd of this chapter; see (#chapter-flags) on
|
||||
Chapter flags. */
|
||||
ChapterFlagEnabled = 0x4598,
|
||||
/** [binary] The SegmentUID of another Segment to play during this chapter. */
|
||||
ChapterSegmentUID = 0x6E67,
|
||||
/** [uinteger] The EditionUID to play from the Segment linked in ChapterSegmentUID. If ChapterSegmentEditionUID is
|
||||
undeclared, then no Edition of the linked Segment is used; see (#medium-linking) on medium-linking Segments. */
|
||||
ChapterSegmentEditionUID = 0x6EBC,
|
||||
/** [uinteger] Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50); see
|
||||
(#physical-types) for a complete list of values. */
|
||||
ChapterPhysicalEquiv = 0x63C3,
|
||||
/** [master] List of tracks on which the chapter applies. If this Element is not present, all tracks apply */
|
||||
ChapterTrack = 0x8F,
|
||||
/** [uinteger] UID of the Track to apply this chapter to. In the absence of a control track, choosing this chapter
|
||||
will select the listed Tracks and deselect unlisted tracks. Absence of this Element indicates that the Chapter
|
||||
**SHOULD** be applied to any currently used Tracks. */
|
||||
ChapterTrackUID = 0x89,
|
||||
/** [master] Contains all possible strings to use for the chapter display. */
|
||||
ChapterDisplay = 0x80,
|
||||
/** [utf-8] Contains the string to use as the chapter atom. */
|
||||
ChapString = 0x85,
|
||||
/** [string] A language corresponding to the string, in the bibliographic ISO-639-2 form [@!ISO639-2]. This Element
|
||||
**MUST** be ignored if a ChapLanguageIETF Element is used within the same ChapterDisplay Element. */
|
||||
ChapLanguage = 0x437C,
|
||||
/** [string] Specifies a language corresponding to the ChapString in the format defined in [@!BCP47] and using the
|
||||
IANA Language Subtag Registry [@!IANALangRegistry]. If a ChapLanguageIETF Element is used, then any ChapLanguage
|
||||
and ChapCountry Elements used in the same ChapterDisplay **MUST** be ignored. */
|
||||
ChapLanguageIETF = 0x437D,
|
||||
/** [string] A country corresponding to the string, using the same 2 octets country-codes as in Internet domains
|
||||
[@!IANADomains] based on [@!ISO3166-1] alpha-2 codes. This Element **MUST** be ignored if a ChapLanguageIETF
|
||||
Element is used within the same ChapterDisplay Element. */
|
||||
ChapCountry = 0x437E,
|
||||
/** [master] Contains all the commands associated to the Atom. */
|
||||
ChapProcess = 0x6944,
|
||||
/** [uinteger] Contains the type of the codec used for the processing. A value of 0 means native Matroska processing
|
||||
(to be defined), a value of 1 means the DVD command set is used; see (#menu-features) on DVD menus. More codec
|
||||
IDs can be added later. */
|
||||
ChapProcessCodecID = 0x6955,
|
||||
/** [binary] Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it
|
||||
is the "DVD level" equivalent; see (#menu-features) on DVD menus. */
|
||||
ChapProcessPrivate = 0x450D,
|
||||
/** [master] Contains all the commands associated to the Atom. */
|
||||
ChapProcessCommand = 0x6911,
|
||||
/** [uinteger] Defines when the process command **SHOULD** be handled */
|
||||
ChapProcessTime = 0x6922,
|
||||
/** [binary] Contains the command information. The data **SHOULD** be interpreted depending on the
|
||||
ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post
|
||||
commands; see (#menu-features) on DVD menus. */
|
||||
ChapProcessData = 0x6933,
|
||||
/** [master] Element containing metadata describing Tracks, Editions, Chapters, Attachments, or the Segment as a
|
||||
whole. A list of valid tags can be found in [@!MatroskaTags]. */
|
||||
Tags = 0x1254C367,
|
||||
/** [master] A single metadata descriptor. */
|
||||
Tag = 0x7373,
|
||||
/** [master] Specifies which other elements the metadata represented by the Tag applies to. If empty or not present,
|
||||
then the Tag describes everything in the Segment. */
|
||||
Targets = 0x63C0,
|
||||
/** [uinteger] A number to indicate the logical level of the target. */
|
||||
TargetTypeValue = 0x68CA,
|
||||
/** [string] An informational string that can be used to display the logical level of the target like "ALBUM",
|
||||
"TRACK", "MOVIE", "CHAPTER", etc ; see Section 6.4 of [@!MatroskaTags]. */
|
||||
TargetType = 0x63CA,
|
||||
/** [uinteger] A unique ID to identify the Track(s) the tags belong to. */
|
||||
TagTrackUID = 0x63C5,
|
||||
/** [uinteger] A unique ID to identify the EditionEntry(s) the tags belong to. */
|
||||
TagEditionUID = 0x63C9,
|
||||
/** [uinteger] A unique ID to identify the Chapter(s) the tags belong to. */
|
||||
TagChapterUID = 0x63C4,
|
||||
/** [uinteger] A unique ID to identify the Attachment(s) the tags belong to. */
|
||||
TagAttachmentUID = 0x63C6,
|
||||
/** [master] Contains general information about the target. */
|
||||
SimpleTag = 0x67C8,
|
||||
/** [utf-8] The name of the Tag that is going to be stored. */
|
||||
TagName = 0x45A3,
|
||||
/** [string] Specifies the language of the tag specified, in the Matroska languages form; see (#language-codes) on
|
||||
language codes. This Element **MUST** be ignored if the TagLanguageIETF Element is used within the same SimpleTag
|
||||
Element. */
|
||||
TagLanguage = 0x447A,
|
||||
/** [string] Specifies the language used in the TagString according to [@!BCP47] and using the IANA Language Subtag
|
||||
Registry [@!IANALangRegistry]. If this Element is used, then any TagLanguage Elements used in the same SimpleTag
|
||||
**MUST** be ignored. */
|
||||
TagLanguageIETF = 0x447B,
|
||||
/** [uinteger] A boolean value to indicate if this is the default/original language to use for the given tag. */
|
||||
TagDefault = 0x4484,
|
||||
/** [uinteger] A variant of the TagDefault element with a bogus Element ID; see (#tagdefault-element). */
|
||||
TagDefaultBogus = 0x44B4,
|
||||
/** [utf-8] The value of the Tag. */
|
||||
TagString = 0x4487,
|
||||
/** [binary] The values of the Tag, if it is binary. Note that this cannot be used in the same SimpleTag as
|
||||
TagString. */
|
||||
TagBinary = 0x4485,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef ES8311AUDIOSINK_H
|
||||
#define ES8311AUDIOSINK_H
|
||||
|
||||
#include "driver/i2s.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "BufferedAudioSink.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2c.h"
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
class ES8311AudioSink : public BufferedAudioSink
|
||||
{
|
||||
public:
|
||||
ES8311AudioSink();
|
||||
~ES8311AudioSink();
|
||||
void writeReg(uint8_t reg_add, uint8_t data);
|
||||
void volumeChanged(uint16_t volume);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
121
components/spotify/cspot/bell/include/audio/sinks/esp/es8311.h
Normal file
121
components/spotify/cspot/bell/include/audio/sinks/esp/es8311.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* ES8311.h -- ES8311 ALSA SoC Audio Codec
|
||||
*
|
||||
* Authors:
|
||||
*
|
||||
* Based on ES8374.h by David Yang
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _ES8311_H
|
||||
#define _ES8311_H
|
||||
#include "driver/i2c.h"
|
||||
#include "esxxx_common.h"
|
||||
|
||||
/*
|
||||
* ES8311_REGISTER NAME_REG_REGISTER ADDRESS
|
||||
*/
|
||||
#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/
|
||||
|
||||
/*
|
||||
* Clock Scheme Register definition
|
||||
*/
|
||||
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
|
||||
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
||||
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
||||
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
||||
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
|
||||
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
|
||||
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
|
||||
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
|
||||
#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */
|
||||
#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */
|
||||
#define ES8311_SYSTEM_REG0B 0x0B /* system */
|
||||
#define ES8311_SYSTEM_REG0C 0x0C /* system */
|
||||
#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */
|
||||
#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */
|
||||
#define ES8311_SYSTEM_REG10 0x10 /* system */
|
||||
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
||||
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
||||
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
||||
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
|
||||
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
||||
#define ES8311_ADC_REG16 0x16 /* ADC */
|
||||
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
||||
#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */
|
||||
#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */
|
||||
#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */
|
||||
#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */
|
||||
#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */
|
||||
#define ES8311_DAC_REG31 0x31 /* DAC, mute */
|
||||
#define ES8311_DAC_REG32 0x32 /* DAC, volume */
|
||||
#define ES8311_DAC_REG33 0x33 /* DAC, offset */
|
||||
#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */
|
||||
#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */
|
||||
#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */
|
||||
#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */
|
||||
#define ES8311_GP_REG45 0x45 /* GP CONTROL */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */
|
||||
#define ES8311_CHVER_REGFF 0xFF /* VERSION */
|
||||
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
|
||||
|
||||
#define ES8311_MAX_REGISTER 0xFF
|
||||
|
||||
|
||||
typedef struct {
|
||||
ESCodecMode esMode;
|
||||
i2c_port_t i2c_port_num;
|
||||
i2c_config_t i2c_cfg;
|
||||
DacOutput dacOutput;
|
||||
AdcInput adcInput;
|
||||
} Es8311Config;
|
||||
|
||||
|
||||
#define AUDIO_CODEC_ES8311_DEFAULT(){ \
|
||||
.esMode = ES_MODE_SLAVE, \
|
||||
.i2c_port_num = I2C_NUM_0, \
|
||||
.i2c_cfg = { \
|
||||
.mode = I2C_MODE_MASTER, \
|
||||
.sda_io_num = IIC_DATA, \
|
||||
.scl_io_num = IIC_CLK, \
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,\
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,\
|
||||
.master.clk_speed = 100000\
|
||||
}, \
|
||||
.adcInput = ADC_INPUT_LINPUT1_RINPUT1,\
|
||||
.dacOutput = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2,\
|
||||
};
|
||||
|
||||
int Es8311Init(Es8311Config *cfg);
|
||||
void Es8311Uninit();
|
||||
esp_err_t Es8311GetRef(bool flag);
|
||||
esp_err_t Es7243Init(void);
|
||||
|
||||
int Es7243ReadReg(uint8_t regAdd);
|
||||
|
||||
int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt);
|
||||
int Es8311I2sConfigClock(ESCodecI2sClock cfg);
|
||||
int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample);
|
||||
|
||||
int Es8311Start(ESCodecModule mode);
|
||||
int Es8311Stop(ESCodecModule mode);
|
||||
|
||||
int Es8311SetVoiceVolume(int volume);
|
||||
int Es8311GetVoiceVolume(int *volume);
|
||||
int Es8311SetVoiceMute(int enable);
|
||||
int Es8311GetVoiceMute(int *mute);
|
||||
int Es8311SetMicGain(MicGain gain);
|
||||
|
||||
int Es8311ConfigAdcInput(AdcInput input);
|
||||
int Es8311ConfigDacOutput(DacOutput output);
|
||||
|
||||
int ES8311WriteReg(uint8_t regAdd, uint8_t data);
|
||||
|
||||
void Es8311ReadAll();
|
||||
int Es8311ReadReg(uint8_t regAdd);
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
#ifndef __ESCODEC_COMMON_H__
|
||||
#define __ESCODEC_COMMON_H__
|
||||
|
||||
typedef enum BitsLength {
|
||||
BIT_LENGTH_MIN = -1,
|
||||
BIT_LENGTH_16BITS = 0x03,
|
||||
BIT_LENGTH_18BITS = 0x02,
|
||||
BIT_LENGTH_20BITS = 0x01,
|
||||
BIT_LENGTH_24BITS = 0x00,
|
||||
BIT_LENGTH_32BITS = 0x04,
|
||||
BIT_LENGTH_MAX,
|
||||
} BitsLength;
|
||||
|
||||
typedef enum {
|
||||
SAMPLE_RATE_MIN = -1,
|
||||
SAMPLE_RATE_16K,
|
||||
SAMPLE_RATE_32K,
|
||||
SAMPLE_RATE_44_1K,
|
||||
SAMPLE_RATE_MAX,
|
||||
} SampleRate;
|
||||
|
||||
typedef enum {
|
||||
MclkDiv_MIN = -1,
|
||||
MclkDiv_1 = 1,
|
||||
MclkDiv_2 = 2,
|
||||
MclkDiv_3 = 3,
|
||||
MclkDiv_4 = 4,
|
||||
MclkDiv_6 = 5,
|
||||
MclkDiv_8 = 6,
|
||||
MclkDiv_9 = 7,
|
||||
MclkDiv_11 = 8,
|
||||
MclkDiv_12 = 9,
|
||||
MclkDiv_16 = 10,
|
||||
MclkDiv_18 = 11,
|
||||
MclkDiv_22 = 12,
|
||||
MclkDiv_24 = 13,
|
||||
MclkDiv_33 = 14,
|
||||
MclkDiv_36 = 15,
|
||||
MclkDiv_44 = 16,
|
||||
MclkDiv_48 = 17,
|
||||
MclkDiv_66 = 18,
|
||||
MclkDiv_72 = 19,
|
||||
MclkDiv_5 = 20,
|
||||
MclkDiv_10 = 21,
|
||||
MclkDiv_15 = 22,
|
||||
MclkDiv_17 = 23,
|
||||
MclkDiv_20 = 24,
|
||||
MclkDiv_25 = 25,
|
||||
MclkDiv_30 = 26,
|
||||
MclkDiv_32 = 27,
|
||||
MclkDiv_34 = 28,
|
||||
MclkDiv_7 = 29,
|
||||
MclkDiv_13 = 30,
|
||||
MclkDiv_14 = 31,
|
||||
MclkDiv_MAX,
|
||||
} SclkDiv;
|
||||
|
||||
typedef enum {
|
||||
LclkDiv_MIN = -1,
|
||||
LclkDiv_128 = 0,
|
||||
LclkDiv_192 = 1,
|
||||
LclkDiv_256 = 2,
|
||||
LclkDiv_384 = 3,
|
||||
LclkDiv_512 = 4,
|
||||
LclkDiv_576 = 5,
|
||||
LclkDiv_768 = 6,
|
||||
LclkDiv_1024 = 7,
|
||||
LclkDiv_1152 = 8,
|
||||
LclkDiv_1408 = 9,
|
||||
LclkDiv_1536 = 10,
|
||||
LclkDiv_2112 = 11,
|
||||
LclkDiv_2304 = 12,
|
||||
|
||||
LclkDiv_125 = 16,
|
||||
LclkDiv_136 = 17,
|
||||
LclkDiv_250 = 18,
|
||||
LclkDiv_272 = 19,
|
||||
LclkDiv_375 = 20,
|
||||
LclkDiv_500 = 21,
|
||||
LclkDiv_544 = 22,
|
||||
LclkDiv_750 = 23,
|
||||
LclkDiv_1000 = 24,
|
||||
LclkDiv_1088 = 25,
|
||||
LclkDiv_1496 = 26,
|
||||
LclkDiv_1500 = 27,
|
||||
LclkDiv_MAX,
|
||||
} LclkDiv;
|
||||
|
||||
typedef enum {
|
||||
ADC_INPUT_MIN = -1,
|
||||
ADC_INPUT_LINPUT1_RINPUT1 = 0x00,
|
||||
ADC_INPUT_MIC1 = 0x05,
|
||||
ADC_INPUT_MIC2 = 0x06,
|
||||
ADC_INPUT_LINPUT2_RINPUT2 = 0x50,
|
||||
ADC_INPUT_DIFFERENCE = 0xf0,
|
||||
ADC_INPUT_MAX,
|
||||
} AdcInput;
|
||||
|
||||
typedef enum {
|
||||
DAC_OUTPUT_MIN = -1,
|
||||
DAC_OUTPUT_LOUT1 = 0x04,
|
||||
DAC_OUTPUT_LOUT2 = 0x08,
|
||||
DAC_OUTPUT_SPK = 0x09,
|
||||
DAC_OUTPUT_ROUT1 = 0x10,
|
||||
DAC_OUTPUT_ROUT2 = 0x20,
|
||||
DAC_OUTPUT_ALL = 0x3c,
|
||||
DAC_OUTPUT_MAX,
|
||||
} DacOutput;
|
||||
|
||||
typedef enum {
|
||||
D2SE_PGA_GAIN_MIN = -1,
|
||||
D2SE_PGA_GAIN_DIS = 0,
|
||||
D2SE_PGA_GAIN_EN = 1,
|
||||
D2SE_PGA_GAIN_MAX = 2,
|
||||
} D2SEPGA;
|
||||
|
||||
typedef enum {
|
||||
MIC_GAIN_MIN = -1,
|
||||
MIC_GAIN_0DB = 0,
|
||||
MIC_GAIN_3DB = 3,
|
||||
MIC_GAIN_6DB = 6,
|
||||
MIC_GAIN_9DB = 9,
|
||||
MIC_GAIN_12DB = 12,
|
||||
MIC_GAIN_15DB = 15,
|
||||
MIC_GAIN_18DB = 18,
|
||||
MIC_GAIN_21DB = 21,
|
||||
MIC_GAIN_24DB = 24,
|
||||
#if defined CONFIG_CODEC_CHIP_IS_ES8311
|
||||
MIC_GAIN_30DB = 30,
|
||||
MIC_GAIN_36DB = 36,
|
||||
MIC_GAIN_42DB = 42,
|
||||
#endif
|
||||
MIC_GAIN_MAX,
|
||||
} MicGain;
|
||||
|
||||
typedef enum {
|
||||
ES_MODULE_MIN = -1,
|
||||
ES_MODULE_ADC = 0x01,
|
||||
ES_MODULE_DAC = 0x02,
|
||||
ES_MODULE_ADC_DAC = 0x03,
|
||||
ES_MODULE_LINE = 0x04,
|
||||
ES_MODULE_MAX
|
||||
} ESCodecModule;
|
||||
|
||||
typedef enum {
|
||||
ES_MODE_MIN = -1,
|
||||
ES_MODE_SLAVE = 0x00,
|
||||
ES_MODE_MASTER = 0x01,
|
||||
ES_MODE_MAX,
|
||||
} ESCodecMode;
|
||||
|
||||
typedef enum {
|
||||
ES_ = -1,
|
||||
ES_I2S_NORMAL = 0,
|
||||
ES_I2S_LEFT = 1,
|
||||
ES_I2S_RIGHT = 2,
|
||||
ES_I2S_DSP = 3,
|
||||
ES_I2S_MAX
|
||||
} ESCodecI2SFmt;
|
||||
|
||||
typedef struct {
|
||||
SclkDiv sclkDiv;
|
||||
LclkDiv lclkDiv;
|
||||
} ESCodecI2sClock;
|
||||
|
||||
#endif //__ESCODEC_COMMON_H__
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "AudioSink.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <stdio.h>
|
||||
#include <Task.h>
|
||||
#include <BellTask.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
18
components/spotify/cspot/bell/include/platform/MDNSService.h
Normal file
18
components/spotify/cspot/bell/include/platform/MDNSService.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef BELLL_MDNS_SERVICE_H
|
||||
#define BELLL_MDNS_SERVICE_H
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class MDNSService {
|
||||
public:
|
||||
static void registerService(
|
||||
const std::string &serviceName,
|
||||
const std::string &serviceType,
|
||||
const std::string &serviceProto,
|
||||
const std::string &serviceHost,
|
||||
int servicePort,
|
||||
const std::map<std::string, std::string> txtData
|
||||
);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "freertos/semphr.h"
|
||||
#elif __APPLE__
|
||||
#include <dispatch/dispatch.h>
|
||||
#elif _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#include <semaphore.h>
|
||||
@@ -18,6 +20,8 @@ private:
|
||||
xSemaphoreHandle semaphoreHandle;
|
||||
#elif __APPLE__
|
||||
dispatch_semaphore_t semaphoreHandle;
|
||||
#elif _WIN32
|
||||
HANDLE semaphoreHandle;
|
||||
#else
|
||||
sem_t semaphoreHandle;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#define ssize_t SSIZE_T
|
||||
|
||||
#define strcasecmp stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#define bzero(p,n) memset(p,0,n)
|
||||
#define usleep(x) Sleep((x)/1000)
|
||||
|
||||
inline void close(int sock) { closesocket(sock); }
|
||||
inline size_t read(int sock, char* buf, size_t n) { return recv(sock, buf, n, 0); }
|
||||
inline int write(int sock, const char* buf, size_t n) { return send(sock, buf, n, 0); }
|
||||
39
components/spotify/cspot/bell/nanopb/.github/workflows/cifuzz.yml
vendored
Normal file
39
components/spotify/cspot/bell/nanopb/.github/workflows/cifuzz.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: CIFuzz
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'nanopb'
|
||||
dry-run: false
|
||||
sanitizer: undefined
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'nanopb'
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
sanitizer: undefined
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
63
components/spotify/cspot/bell/nanopb/.github/workflows/platformio.yaml
vendored
Normal file
63
components/spotify/cspot/bell/nanopb/.github/workflows/platformio.yaml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: platformio
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
platformio:
|
||||
name: Build and run PlatformIO example
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: ⤵️ Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: nanopb
|
||||
|
||||
- name: Installing dependencies for local act
|
||||
if: ${{ env.ACT }}
|
||||
run: |
|
||||
sudo apt update
|
||||
|
||||
- name: Installing common dependencies
|
||||
run: |
|
||||
sudo apt install -y python3-pip
|
||||
|
||||
- name: Install and setup PlatformIO
|
||||
run: |
|
||||
pip3 install -U platformio
|
||||
export PATH=~/.local/bin:$PATH
|
||||
|
||||
- name: Build PlatformIO package
|
||||
run: |
|
||||
cd nanopb
|
||||
pio package pack
|
||||
|
||||
- name: Example - Extract PlatformIO package to example dir
|
||||
run: |
|
||||
cp -R nanopb/examples/platformio example
|
||||
mkdir -p example/lib/nanopb
|
||||
tar -xzf nanopb/Nanopb-*.tar.gz -C example/lib/nanopb
|
||||
|
||||
- name: Example - Build
|
||||
run: |
|
||||
cd example
|
||||
pio run
|
||||
|
||||
- name: Example - Run test without options
|
||||
run: example/.pio/build/pio_without_options/program
|
||||
|
||||
- name: Example - Run test with options
|
||||
run: example/.pio/build/pio_with_options/program
|
||||
|
||||
- name: Build with default platformio.ini
|
||||
run: |
|
||||
mkdir -p test_default_pio_conf
|
||||
cd test_default_pio_conf
|
||||
pio project init
|
||||
ln -s ../nanopb lib/nanopb
|
||||
echo "[env:native]" >> platformio.ini
|
||||
echo "platform = native" >> platformio.ini
|
||||
echo "lib_deps = Nanopb" >> platformio.ini
|
||||
echo "int main(int argc, char *argv[]){}" > src/main.cpp
|
||||
pio run
|
||||
15
components/spotify/cspot/bell/nanopb/.github/workflows/spm.yml
vendored
Normal file
15
components/spotify/cspot/bell/nanopb/.github/workflows/spm.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: spm
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
swift-build-run:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: swift build
|
||||
- name: Run
|
||||
run: swift test
|
||||
@@ -98,3 +98,19 @@ leabut <leabut@users.noreply.github.com>
|
||||
Angel ILIEV <a.v.iliev13@gmail.com>
|
||||
Jakub Tymejczyk <jakub@tymejczyk.pl>
|
||||
Matthew Simmons <simmonmt@acm.org>
|
||||
Anthony Pesch <inolen@gmail.com>
|
||||
Avik De <avikde@gmail.com>
|
||||
ConradWood <github@conradwood.net>
|
||||
David Sabatie <david.sabatie@notrenet.com>
|
||||
Sebastian Stockhammer <sebastian.stockhammer@rosenberger.de>
|
||||
Gil Shapira <gil.shapira@intusurg.com>
|
||||
Ian Frosst <ianjfrosst@gmail.com>
|
||||
Ingo Kresse <ingo.kresse@kuka.com>
|
||||
Ivan Zrno <ivan.zrno2@gmail.com>
|
||||
Jonathan Seilkopf <j.seilkopf@isatech.de>
|
||||
Karl Ljungkvist <k.ljungkvist@gmail.com>
|
||||
Mathis Logemann <mathisloge@gmail.com>
|
||||
Oleg Dolgy <60554929+odolgy@users.noreply.github.com>
|
||||
Pavel Sokolov <pavel@sokolov.me>
|
||||
Slavey Karadzhov <slav@attachix.com>
|
||||
Tobias Nießen <tniessen@tnie.de>
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
nanopb-0.4.6 (2022-05-30)
|
||||
Fix passing of error message from substream callback (#703)
|
||||
Fix comments going to wrong member variables (#701)
|
||||
Fix regression in 0.4.3 where generator did not find all dependencies (#720)
|
||||
Fix FindNanopb.cmake not finding options file (#659)
|
||||
Fix double-definition errors with size_union (#692)
|
||||
Fix generator error with same inner message name (#746)
|
||||
Fix infinite recursion in generator/protoc script (#762)
|
||||
Fix unicode comment handling for Python 2 (#740)
|
||||
Fix compiler warnings with PB_BUFFER_ONLY (#717)
|
||||
Fix options dependency in nanopb.mk (#666)
|
||||
Fix handling of filenames with dot in them in FindNanopb.cmake (#756)
|
||||
Add fallback_type option (#772, #773)
|
||||
Use C11 static assert mechanism by default (#761, #766)
|
||||
Use 'static_assert' keyword for iar (#679)
|
||||
Explicitly check for pItem == NULL to satisfy Xcode analyzer (#667, #674)
|
||||
Support --proto-path as alias to -I (#749)
|
||||
Refactor name mangling to separate class, improve error messages (#735)
|
||||
Move PB_WT_PACKED definition to the header to fix compiler warnings (#671)
|
||||
FindNanopb.cmake: use --nanopb_opt for option passing by default (#752)
|
||||
FindNanopb.cmake: Add option NANOPB_GENERATE_CPP_STANDALONE (#741)
|
||||
FindNanopb.cmake: Add PROTOC_OPTIONS variable (#768, #771)
|
||||
CMakeLists: add build interface for using as a submodule (#669)
|
||||
CMakeLists: fix error with nanopb_BUILD_GENERATOR=OFF (#764)
|
||||
CMakeLists: make more uniform (#676)
|
||||
CMakeLists: Fix uninitialized PYTHON_INSTDIR (#652)
|
||||
Clean up CMake examples (#741)
|
||||
Rebuild nanopb_pb2.py and print version numbers on import failure (#733, #742)
|
||||
Use memcpy instead of iterating on buf_read/write (#751)
|
||||
Add generator support for PlatformIO (#718)
|
||||
Add clean target to generator/proto/Makefile (#681)
|
||||
Windows .bats: use standard python invocation instead of py.exe launcher (#657)
|
||||
Fix problems running tests with newer SCons version
|
||||
Improve handling of varint overflows
|
||||
Improve optimization for little-endian platforms
|
||||
|
||||
NOTE: During development, prereleases were published on PlatformIO registry
|
||||
as versions 0.4.6 - 0.4.6.3. The version 0.4.6.4 on PlatformIO corresponds
|
||||
to the real final 0.4.6 release.
|
||||
|
||||
nanopb-0.4.5 (2021-03-22)
|
||||
Fix invalid free() with oneof (#647, GHSA-7mv5-5mxh-qg88)
|
||||
Fix unordered field numbers inside oneof causing fields to be ignored (#617)
|
||||
@@ -149,6 +189,13 @@ nanopb-0.4.0 (2019-12-20)
|
||||
CMake: Split nanopb_out command (#454)
|
||||
CMake: install created shared library(dll) in windows to the binary folder (#447)
|
||||
|
||||
nanopb-0.3.9.9 (2022-04-23)
|
||||
Fix Xcode analyzer warnings (#667, #674)
|
||||
Fix clang sanitizer warnings
|
||||
|
||||
Note: there are no known functional differences between 0.3.9.8 and 0.3.9.9.
|
||||
The changes are merely to fix warnings introduced by new compiler versions.
|
||||
|
||||
nanopb-0.3.9.8 (2021-03-22)
|
||||
Fix invalid free() with oneof (#647, GHSA-7mv5-5mxh-qg88)
|
||||
Don't generate lines with trailing spaces (#622)
|
||||
|
||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
project(nanopb C)
|
||||
|
||||
set(nanopb_VERSION_STRING nanopb-0.4.6-dev)
|
||||
set(nanopb_VERSION_STRING nanopb-0.4.7-dev)
|
||||
set(nanopb_SOVERSION 0)
|
||||
|
||||
string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING})
|
||||
@@ -65,10 +65,10 @@ if(nanopb_BUILD_GENERATOR)
|
||||
DESTINATION ${PYTHON_INSTDIR}/proto/
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
install(FILES generator/proto/_utils.py
|
||||
DESTINATION ${PYTHON_INSTDIR}/proto/)
|
||||
install( FILES generator/proto/_utils.py
|
||||
DESTINATION ${PYTHON_INSTDIR}/proto/ )
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
install(
|
||||
@@ -123,7 +123,7 @@ if(nanopb_BUILD_RUNTIME)
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
target_include_directories(protobuf-nanopb-static INTERFACE
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@ nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS RELPATH proto
|
||||
proto/simple.proto proto/sub/unlucky.proto)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
#add_custom_target(generate_proto_sources DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS}
|
||||
PROPERTIES GENERATED TRUE)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -O0")
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@ include_directories(${NANOPB_INCLUDE_DIRS})
|
||||
|
||||
nanopb_generate_cpp(PROTO_SRCS PROTO_HDRS simple.proto)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
#add_custom_target(generate_proto_sources DEPENDS ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS}
|
||||
PROPERTIES GENERATED TRUE)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -O0")
|
||||
|
||||
|
||||
@@ -52,6 +52,11 @@
|
||||
* Normally it is automatically detected based on __BYTE_ORDER__ macro. */
|
||||
/* #define PB_LITTLE_ENDIAN_8BIT 1 */
|
||||
|
||||
/* Configure static assert mechanism. Instead of changing these, set your
|
||||
* compiler to C11 standard mode if possible. */
|
||||
/* #define PB_C99_STATIC_ASSERT 1 */
|
||||
/* #define PB_NO_STATIC_ASSERT 1 */
|
||||
|
||||
/******************************************************************
|
||||
* You usually don't need to change anything below this line. *
|
||||
* Feel free to look around and use the defined macros, though. *
|
||||
@@ -60,7 +65,7 @@
|
||||
|
||||
/* Version of the nanopb library. Just in case you want to check it in
|
||||
* your own program. */
|
||||
#define NANOPB_VERSION "nanopb-0.4.6-dev"
|
||||
#define NANOPB_VERSION "nanopb-0.4.7-dev"
|
||||
|
||||
/* Include all the system headers needed by nanopb. You will need the
|
||||
* definitions of the following:
|
||||
@@ -165,14 +170,17 @@ extern "C" {
|
||||
# if defined(__ICCARM__)
|
||||
/* IAR has static_assert keyword but no _Static_assert */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
|
||||
# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
/* C11 standard _Static_assert mechanism */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
|
||||
# else
|
||||
# elif defined(PB_C99_STATIC_ASSERT)
|
||||
/* Classic negative-size-array static assert mechanism */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
|
||||
# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
|
||||
# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
|
||||
# elif defined(__cplusplus)
|
||||
/* C++11 standard static_assert mechanism */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG);
|
||||
# else
|
||||
/* C11 standard _Static_assert mechanism */
|
||||
# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
@@ -180,6 +188,14 @@ extern "C" {
|
||||
# define PB_STATIC_ASSERT(COND,MSG)
|
||||
#endif
|
||||
|
||||
/* Test that PB_STATIC_ASSERT works
|
||||
* If you get errors here, you may need to do one of these:
|
||||
* - Enable C11 standard support in your compiler
|
||||
* - Define PB_C99_STATIC_ASSERT to enable C99 standard support
|
||||
* - Define PB_NO_STATIC_ASSERT to disable static asserts altogether
|
||||
*/
|
||||
PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING)
|
||||
|
||||
/* Number of required fields to keep track of. */
|
||||
#ifndef PB_MAX_REQUIRED_FIELDS
|
||||
#define PB_MAX_REQUIRED_FIELDS 64
|
||||
@@ -886,10 +902,13 @@ struct pb_extension_s {
|
||||
#define PB_INLINE_CONSTEXPR PB_CONSTEXPR
|
||||
#endif // __cplusplus >= 201703L
|
||||
|
||||
extern "C++"
|
||||
{
|
||||
namespace nanopb {
|
||||
// Each type will be partially specialized by the generator.
|
||||
template <typename GenMessageT> struct MessageDescriptor;
|
||||
} // namespace nanopb
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -67,14 +67,12 @@ typedef struct {
|
||||
|
||||
static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
const pb_byte_t *source = (const pb_byte_t*)stream->state;
|
||||
stream->state = (pb_byte_t*)stream->state + count;
|
||||
|
||||
if (buf != NULL)
|
||||
{
|
||||
for (i = 0; i < count; i++)
|
||||
buf[i] = source[i];
|
||||
memcpy(buf, source, count * sizeof(pb_byte_t));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -211,18 +209,20 @@ static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *d
|
||||
PB_RETURN_ERROR(stream, "varint overflow");
|
||||
}
|
||||
}
|
||||
else if (bitpos == 28)
|
||||
{
|
||||
if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78)
|
||||
{
|
||||
PB_RETURN_ERROR(stream, "varint overflow");
|
||||
}
|
||||
result |= (uint32_t)(byte & 0x0F) << bitpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= (uint32_t)(byte & 0x7F) << bitpos;
|
||||
}
|
||||
bitpos = (uint_fast8_t)(bitpos + 7);
|
||||
} while (byte & 0x80);
|
||||
|
||||
if (bitpos == 35 && (byte & 0x70) != 0)
|
||||
{
|
||||
/* The last byte was at bitpos=28, so only bottom 4 bits fit. */
|
||||
PB_RETURN_ERROR(stream, "varint overflow");
|
||||
}
|
||||
}
|
||||
|
||||
*dest = result;
|
||||
@@ -243,12 +243,12 @@ bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
|
||||
|
||||
do
|
||||
{
|
||||
if (bitpos >= 64)
|
||||
PB_RETURN_ERROR(stream, "varint overflow");
|
||||
|
||||
if (!pb_readbyte(stream, &byte))
|
||||
return false;
|
||||
|
||||
if (bitpos >= 63 && (byte & 0xFE) != 0)
|
||||
PB_RETURN_ERROR(stream, "varint overflow");
|
||||
|
||||
result |= (uint64_t)(byte & 0x7F) << bitpos;
|
||||
bitpos = (uint_fast8_t)(bitpos + 7);
|
||||
} while (byte & 0x80);
|
||||
@@ -761,7 +761,10 @@ static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type
|
||||
{
|
||||
prev_bytes_left = substream.bytes_left;
|
||||
if (!field->descriptor->field_callback(&substream, NULL, field))
|
||||
PB_RETURN_ERROR(stream, "callback failed");
|
||||
{
|
||||
PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed");
|
||||
return false;
|
||||
}
|
||||
} while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left);
|
||||
|
||||
if (!pb_close_string_substream(stream, &substream))
|
||||
|
||||
@@ -51,12 +51,10 @@ static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb
|
||||
|
||||
static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
pb_byte_t *dest = (pb_byte_t*)stream->state;
|
||||
stream->state = dest + count;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
dest[i] = buf[i];
|
||||
memcpy(dest, buf, count * sizeof(pb_byte_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -626,8 +624,9 @@ bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value)
|
||||
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value)
|
||||
{
|
||||
pb_uint64_t zigzagged;
|
||||
pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */
|
||||
if (value < 0)
|
||||
zigzagged = ~((pb_uint64_t)value << 1);
|
||||
zigzagged = ~(((pb_uint64_t)value & mask) << 1);
|
||||
else
|
||||
zigzagged = (pb_uint64_t)value << 1;
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ int main(int argc, char *argv[])
|
||||
filename = argv[2];
|
||||
}
|
||||
|
||||
elf_firmware_t firmware;
|
||||
elf_firmware_t firmware = {};
|
||||
elf_read_firmware(filename, &firmware);
|
||||
avr_init(g_avr);
|
||||
avr_load_firmware(g_avr, &firmware);
|
||||
|
||||
BIN
components/spotify/cspot/bell/src/.DS_Store
vendored
BIN
components/spotify/cspot/bell/src/.DS_Store
vendored
Binary file not shown.
@@ -1,88 +0,0 @@
|
||||
#include "BaseHTTPServer.h"
|
||||
#include <sstream>
|
||||
|
||||
unsigned char bell::BaseHTTPServer::h2int(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return ((unsigned char)c - '0');
|
||||
}
|
||||
if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
return ((unsigned char)c - 'a' + 10);
|
||||
}
|
||||
if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
return ((unsigned char)c - 'A' + 10);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
std::string bell::BaseHTTPServer::urlDecode(std::string str)
|
||||
{
|
||||
std::string encodedString = "";
|
||||
char c;
|
||||
char code0;
|
||||
char code1;
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
c = str[i];
|
||||
if (c == '+')
|
||||
{
|
||||
encodedString += ' ';
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
i++;
|
||||
code0 = str[i];
|
||||
i++;
|
||||
code1 = str[i];
|
||||
c = (h2int(code0) << 4) | h2int(code1);
|
||||
encodedString += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
encodedString += c;
|
||||
}
|
||||
}
|
||||
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
std::vector<std::string> bell::BaseHTTPServer::splitUrl(const std::string &url, char delimiter)
|
||||
{
|
||||
std::stringstream ssb(url);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(ssb, segment, delimiter))
|
||||
{
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
return seglist;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> bell::BaseHTTPServer::parseQueryString(const std::string &queryString)
|
||||
{
|
||||
std::map<std::string, std::string> query;
|
||||
auto prefixedString = "&" + queryString;
|
||||
while (prefixedString.find("&") != std::string::npos)
|
||||
{
|
||||
auto keyStart = prefixedString.find("&");
|
||||
auto keyEnd = prefixedString.find("=");
|
||||
// Find second occurence of "&" in prefixedString
|
||||
auto valueEnd = prefixedString.find("&", keyStart + 1);
|
||||
if (valueEnd == std::string::npos)
|
||||
{
|
||||
valueEnd = prefixedString.size();
|
||||
}
|
||||
|
||||
auto key = prefixedString.substr(keyStart + 1, keyEnd - 1);
|
||||
auto value = prefixedString.substr(keyEnd + 1, valueEnd - keyEnd - 1);
|
||||
query[key] = urlDecode(value);
|
||||
prefixedString = prefixedString.substr(valueEnd);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "BellLogger.h"
|
||||
|
||||
std::shared_ptr<bell::AbstractLogger> bell::bellGlobalLogger;
|
||||
bell::AbstractLogger* bell::bellGlobalLogger;
|
||||
|
||||
void bell::setDefaultLogger() {
|
||||
bell::bellGlobalLogger = std::make_shared<bell::BellLogger>();
|
||||
bell::bellGlobalLogger = new bell::BellLogger();
|
||||
}
|
||||
|
||||
void bell::enableSubmoduleLogging() {
|
||||
|
||||
@@ -18,8 +18,8 @@ void bell::BinaryReader::close() {
|
||||
}
|
||||
|
||||
void bell::BinaryReader::skip(size_t pos) {
|
||||
uint8_t b[pos];
|
||||
stream->read((uint8_t *)b, pos);
|
||||
std::vector<uint8_t> b(pos);
|
||||
stream->read(&b[0], pos);
|
||||
}
|
||||
|
||||
int32_t bell::BinaryReader::readInt() {
|
||||
|
||||
174
components/spotify/cspot/bell/src/BufferedStream.cpp
Normal file
174
components/spotify/cspot/bell/src/BufferedStream.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-7.
|
||||
|
||||
#include "BufferedStream.h"
|
||||
#include <cstring>
|
||||
|
||||
BufferedStream::BufferedStream(
|
||||
const std::string &taskName,
|
||||
uint32_t bufferSize,
|
||||
uint32_t readThreshold,
|
||||
uint32_t readSize,
|
||||
uint32_t readyThreshold,
|
||||
uint32_t notReadyThreshold,
|
||||
bool waitForReady)
|
||||
: bell::Task(taskName, 4096, 5, 0) {
|
||||
this->bufferSize = bufferSize;
|
||||
this->readAt = bufferSize - readThreshold;
|
||||
this->readSize = readSize;
|
||||
this->readyThreshold = readyThreshold;
|
||||
this->notReadyThreshold = notReadyThreshold;
|
||||
this->waitForReady = waitForReady;
|
||||
this->buf = static_cast<uint8_t *>(malloc(bufferSize));
|
||||
this->bufEnd = buf + bufferSize;
|
||||
reset();
|
||||
}
|
||||
|
||||
BufferedStream::~BufferedStream() {
|
||||
this->close();
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void BufferedStream::close() {
|
||||
this->terminate = true;
|
||||
this->readSem.give(); // force a read operation
|
||||
const std::lock_guard lock(runningMutex);
|
||||
if (this->source)
|
||||
this->source->close();
|
||||
this->source = nullptr;
|
||||
}
|
||||
|
||||
void BufferedStream::reset() {
|
||||
this->bufReadPtr = this->buf;
|
||||
this->bufWritePtr = this->buf;
|
||||
this->readTotal = 0;
|
||||
this->bufferTotal = 0;
|
||||
this->readAvailable = 0;
|
||||
this->terminate = false;
|
||||
}
|
||||
|
||||
bool BufferedStream::open(const std::shared_ptr<bell::ByteStream> &stream) {
|
||||
if (this->running)
|
||||
this->close();
|
||||
reset();
|
||||
this->source = stream;
|
||||
startTask();
|
||||
return source.get();
|
||||
}
|
||||
|
||||
bool BufferedStream::open(const StreamReader &newReader, uint32_t initialOffset) {
|
||||
if (this->running)
|
||||
this->close();
|
||||
reset();
|
||||
this->reader = newReader;
|
||||
this->bufferTotal = initialOffset;
|
||||
startTask();
|
||||
return source.get();
|
||||
}
|
||||
|
||||
bool BufferedStream::isReady() const {
|
||||
return readAvailable >= readyThreshold;
|
||||
}
|
||||
|
||||
bool BufferedStream::isNotReady() const {
|
||||
return readAvailable < notReadyThreshold;
|
||||
}
|
||||
|
||||
size_t BufferedStream::skip(size_t len) {
|
||||
return read(nullptr, len);
|
||||
}
|
||||
|
||||
size_t BufferedStream::position() {
|
||||
return readTotal;
|
||||
}
|
||||
|
||||
size_t BufferedStream::size() {
|
||||
return source->size();
|
||||
}
|
||||
|
||||
uint32_t BufferedStream::lengthBetween(uint8_t *me, uint8_t *other) {
|
||||
const std::lock_guard lock(readMutex);
|
||||
if (other <= me) {
|
||||
// buf .... other ...... me ........ bufEnd
|
||||
// buf .... me/other ........ bufEnd
|
||||
return bufEnd - me;
|
||||
} else {
|
||||
// buf ........ me ........ other .... bufEnd
|
||||
return other - me;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BufferedStream::read(uint8_t *dst, size_t len) {
|
||||
if (waitForReady && isNotReady()) {
|
||||
while ((source || reader) && !isReady()) {} // end waiting after termination
|
||||
}
|
||||
if (!running && !readAvailable) {
|
||||
reset();
|
||||
return 0;
|
||||
}
|
||||
uint32_t read = 0;
|
||||
uint32_t toReadTotal = std::min(readAvailable.load(), static_cast<uint32_t>(len));
|
||||
while (toReadTotal > 0) {
|
||||
uint32_t toRead = std::min(toReadTotal, lengthBetween(bufReadPtr, bufWritePtr));
|
||||
if (dst) {
|
||||
memcpy(dst, bufReadPtr, toRead);
|
||||
dst += toRead;
|
||||
}
|
||||
readAvailable -= toRead;
|
||||
bufReadPtr += toRead;
|
||||
if (bufReadPtr >= bufEnd)
|
||||
bufReadPtr = buf;
|
||||
toReadTotal -= toRead;
|
||||
read += toRead;
|
||||
readTotal += toRead;
|
||||
}
|
||||
this->readSem.give();
|
||||
return read;
|
||||
}
|
||||
|
||||
void BufferedStream::runTask() {
|
||||
const std::lock_guard lock(runningMutex);
|
||||
running = true;
|
||||
if (!source && reader) {
|
||||
// get the initial request on the task's thread
|
||||
source = reader(this->bufferTotal);
|
||||
}
|
||||
while (!terminate) {
|
||||
if (!source)
|
||||
break;
|
||||
if (isReady()) {
|
||||
// buffer ready, wait for any read operations
|
||||
this->readSem.wait();
|
||||
}
|
||||
if (terminate)
|
||||
break;
|
||||
if (readAvailable > readAt)
|
||||
continue;
|
||||
// here, the buffer needs re-filling
|
||||
uint32_t len;
|
||||
bool wasReady = isReady();
|
||||
do {
|
||||
uint32_t toRead = std::min(readSize, lengthBetween(bufWritePtr, bufReadPtr));
|
||||
if (!source) {
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
len = source->read(bufWritePtr, toRead);
|
||||
readAvailable += len;
|
||||
bufferTotal += len;
|
||||
bufWritePtr += len;
|
||||
if (bufWritePtr >= bufEnd) // TODO is == enough here?
|
||||
bufWritePtr = buf;
|
||||
} while (len && readSize < bufferSize - readAvailable); // loop until there's no more free space in the buffer
|
||||
if (!len && reader)
|
||||
source = reader(bufferTotal);
|
||||
else if (!len)
|
||||
terminate = true;
|
||||
// signal that buffer is ready for reading
|
||||
if (!wasReady && isReady()) {
|
||||
this->readySem.give();
|
||||
}
|
||||
}
|
||||
source = nullptr;
|
||||
reader = nullptr;
|
||||
running = false;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifdef BELL_USE_MBEDTLS
|
||||
#include "CryptoMbedTLS.h"
|
||||
#include "Crypto.h"
|
||||
|
||||
CryptoMbedTLS::CryptoMbedTLS()
|
||||
{
|
||||
@@ -224,4 +223,3 @@ std::vector<uint8_t> CryptoMbedTLS::generateVectorWithRandomData(size_t length)
|
||||
|
||||
return randomVector;
|
||||
}
|
||||
#endif
|
||||
@@ -1,184 +0,0 @@
|
||||
#include "CryptoOpenSSL.h"
|
||||
namespace
|
||||
{
|
||||
struct BIOFreeAll
|
||||
{
|
||||
void operator()(BIO *p) { BIO_free_all(p); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
CryptoOpenSSL::CryptoOpenSSL()
|
||||
{
|
||||
// OpenSSL init
|
||||
ENGINE_load_builtin_engines();
|
||||
ENGINE_register_all_complete();
|
||||
this->publicKey = std::vector<uint8_t>(DH_KEY_SIZE);
|
||||
this->privateKey = generateVectorWithRandomData(DH_KEY_SIZE);
|
||||
}
|
||||
|
||||
CryptoOpenSSL::~CryptoOpenSSL()
|
||||
{
|
||||
if (this->dhContext != nullptr)
|
||||
{
|
||||
DH_free(this->dhContext);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoOpenSSL::base64Decode(const std::string& data)
|
||||
{
|
||||
// base64 in openssl is an absolute mess lmao
|
||||
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
|
||||
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO *source = BIO_new_mem_buf(data.c_str(), -1); // read-only source
|
||||
BIO_push(b64.get(), source);
|
||||
const int maxlen = data.size() / 4 * 3 + 1;
|
||||
std::vector<uint8_t> decoded(maxlen);
|
||||
const int len = BIO_read(b64.get(), decoded.data(), maxlen);
|
||||
decoded.resize(len);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
std::string CryptoOpenSSL::base64Encode(const std::vector<uint8_t>& data)
|
||||
{
|
||||
// base64 in openssl is an absolute mess lmao x 2
|
||||
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
|
||||
|
||||
// No newline mode, put all the data into sink
|
||||
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||
BIO *sink = BIO_new(BIO_s_mem());
|
||||
BIO_push(b64.get(), sink);
|
||||
BIO_write(b64.get(), data.data(), data.size());
|
||||
BIO_flush(b64.get());
|
||||
const char *encoded;
|
||||
const long len = BIO_get_mem_data(sink, &encoded);
|
||||
return std::string(encoded, len);
|
||||
}
|
||||
|
||||
// Sha1
|
||||
void CryptoOpenSSL::sha1Init()
|
||||
{
|
||||
SHA1_Init(&sha1Context);
|
||||
}
|
||||
|
||||
void CryptoOpenSSL::sha1Update(const std::string& s)
|
||||
{
|
||||
sha1Update(std::vector<uint8_t>(s.begin(), s.end()));
|
||||
}
|
||||
void CryptoOpenSSL::sha1Update(const std::vector<uint8_t>& vec)
|
||||
{
|
||||
SHA1_Update(&sha1Context, vec.data(), vec.size());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoOpenSSL::sha1FinalBytes()
|
||||
{
|
||||
std::vector<uint8_t> digest(20); // 20 is 160 bits
|
||||
SHA1_Final(digest.data(), &sha1Context);
|
||||
return digest;
|
||||
}
|
||||
|
||||
std::string CryptoOpenSSL::sha1Final()
|
||||
{
|
||||
auto digest = sha1FinalBytes();
|
||||
return std::string(digest.begin(), digest.end());
|
||||
}
|
||||
|
||||
// HMAC SHA1
|
||||
std::vector<uint8_t> CryptoOpenSSL::sha1HMAC(const std::vector<uint8_t>& inputKey, const std::vector<uint8_t>& message)
|
||||
{
|
||||
std::vector<uint8_t> digest(20); // 20 is 160 bits
|
||||
auto hmacContext = HMAC_CTX_new();
|
||||
HMAC_Init_ex(hmacContext, inputKey.data(), inputKey.size(), EVP_sha1(), NULL);
|
||||
HMAC_Update(hmacContext, message.data(), message.size());
|
||||
|
||||
unsigned int resLen = 0;
|
||||
HMAC_Final(hmacContext, digest.data(), &resLen);
|
||||
|
||||
HMAC_CTX_free(hmacContext);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
// AES CTR
|
||||
void CryptoOpenSSL::aesCTRXcrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& iv, uint8_t* buffer, size_t nbytes)
|
||||
{
|
||||
// Prepare AES_KEY
|
||||
auto cryptoKey = AES_KEY();
|
||||
AES_set_encrypt_key(key.data(), 128, &cryptoKey);
|
||||
|
||||
// Needed for openssl internal cache
|
||||
unsigned char ecountBuf[16] = {0};
|
||||
unsigned int offsetInBlock = 0;
|
||||
|
||||
CRYPTO_ctr128_encrypt(
|
||||
buffer,
|
||||
buffer,
|
||||
nbytes,
|
||||
&cryptoKey,
|
||||
iv.data(),
|
||||
ecountBuf,
|
||||
&offsetInBlock,
|
||||
block128_f(AES_encrypt));
|
||||
}
|
||||
|
||||
void CryptoOpenSSL::aesECBdecrypt(const std::vector<uint8_t>& key, std::vector<uint8_t>& data)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
int len = 0;
|
||||
|
||||
EVP_DecryptInit_ex(ctx, EVP_aes_192_ecb(), NULL, key.data(), NULL);
|
||||
EVP_CIPHER_CTX_set_padding(ctx, 0); // disable padding
|
||||
EVP_DecryptUpdate(ctx, data.data(), &len, data.data(), data.size());
|
||||
EVP_DecryptFinal_ex(ctx, data.data() + len, &len);
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
|
||||
// PBKDF2
|
||||
std::vector<uint8_t> CryptoOpenSSL::pbkdf2HmacSha1(const std::vector<uint8_t>& password, const std::vector<uint8_t>& salt, int iterations, int digestSize)
|
||||
{
|
||||
std::vector<uint8_t> digest(digestSize);
|
||||
|
||||
// Generate PKDF2 digest
|
||||
PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.size(),
|
||||
(const unsigned char *)salt.data(), salt.size(), iterations,
|
||||
digestSize, digest.data());
|
||||
return digest;
|
||||
}
|
||||
|
||||
void CryptoOpenSSL::dhInit()
|
||||
{
|
||||
// Free old context
|
||||
if (this->dhContext != nullptr)
|
||||
{
|
||||
DH_free(this->dhContext);
|
||||
}
|
||||
this->dhContext = DH_new();
|
||||
|
||||
// Set prime and the generator
|
||||
DH_set0_pqg(this->dhContext, BN_bin2bn(DHPrime, DH_KEY_SIZE, NULL), NULL, BN_bin2bn(DHGenerator, 1, NULL));
|
||||
|
||||
// Generate public and private keys and copy them to vectors
|
||||
DH_generate_key(this->dhContext);
|
||||
BN_bn2bin(DH_get0_pub_key(dhContext), this->publicKey.data());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CryptoOpenSSL::dhCalculateShared(const std::vector<uint8_t>& remoteKey)
|
||||
{
|
||||
auto sharedKey = std::vector<uint8_t>(DH_KEY_SIZE);
|
||||
// Convert remote key to bignum and compute shared key
|
||||
auto pubKey = BN_bin2bn(&remoteKey[0], DH_KEY_SIZE, NULL);
|
||||
DH_compute_key(sharedKey.data(), pubKey, this->dhContext);
|
||||
BN_free(pubKey);
|
||||
return sharedKey;
|
||||
}
|
||||
|
||||
// Random stuff
|
||||
std::vector<uint8_t> CryptoOpenSSL::generateVectorWithRandomData(size_t length)
|
||||
{
|
||||
std::vector<uint8_t> randomVec(length);
|
||||
if(RAND_bytes(randomVec.data(), length) == 0)
|
||||
{
|
||||
}
|
||||
return randomVec;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "DecoderGlobals.h"
|
||||
|
||||
std::shared_ptr<bell::DecodersInstance> bell::decodersInstance;
|
||||
|
||||
void bell::createDecoders()
|
||||
{
|
||||
bell::decodersInstance = std::make_shared<bell::DecodersInstance>();
|
||||
}
|
||||
@@ -55,14 +55,14 @@ std::vector<std::string> bell::HTTPServer::splitUrl(const std::string &url,
|
||||
|
||||
void bell::HTTPServer::registerHandler(RequestType requestType,
|
||||
const std::string &routeUrl,
|
||||
httpHandler handler) {
|
||||
httpHandler handler,
|
||||
bool readBodyToStr) {
|
||||
if (routes.find(routeUrl) == routes.end()) {
|
||||
routes.insert({routeUrl, std::vector<HTTPRoute>()});
|
||||
}
|
||||
this->routes[routeUrl].push_back(HTTPRoute{
|
||||
.requestType = requestType,
|
||||
.handler = handler,
|
||||
});
|
||||
this->routes[routeUrl].push_back(HTTPRoute{.requestType = requestType,
|
||||
.handler = handler,
|
||||
.readBodyToStr = readBodyToStr});
|
||||
}
|
||||
|
||||
void bell::HTTPServer::listen() {
|
||||
@@ -126,8 +126,8 @@ void bell::HTTPServer::listen() {
|
||||
HTTPConnection conn = { .buffer = std::vector<uint8_t>(128),
|
||||
.httpMethod = "" };
|
||||
|
||||
this->connections.insert({ newFd, conn });
|
||||
}
|
||||
this->connections.insert({newFd, conn});
|
||||
}
|
||||
|
||||
/* Service other sockets and update set & max */
|
||||
maxfd = sockfd;
|
||||
@@ -140,8 +140,7 @@ void bell::HTTPServer::listen() {
|
||||
FD_CLR(fd, &activeFdSet);
|
||||
this->connections.erase(
|
||||
it++); // or "it = m.erase(it)" since C++11
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (fd != sockfd && FD_ISSET(fd, &readFdSet)) {
|
||||
/* Data arriving on an already-connected socket. */
|
||||
readFromClient(fd);
|
||||
@@ -156,6 +155,10 @@ void bell::HTTPServer::listen() {
|
||||
|
||||
void bell::HTTPServer::readFromClient(int clientFd) {
|
||||
HTTPConnection &conn = this->connections[clientFd];
|
||||
if (conn.headersRead) {
|
||||
return;
|
||||
}
|
||||
conn.fd = clientFd;
|
||||
|
||||
int nbytes = recv(clientFd, (char*) &conn.buffer[0], conn.buffer.size(), 0);
|
||||
if (nbytes < 0) {
|
||||
@@ -165,49 +168,55 @@ void bell::HTTPServer::readFromClient(int clientFd) {
|
||||
} else if (nbytes == 0) {
|
||||
this->closeConnection(clientFd);
|
||||
} else {
|
||||
conn.currentLine +=
|
||||
std::string(conn.buffer.data(), conn.buffer.data() + nbytes);
|
||||
READBODY:
|
||||
if (!conn.isReadingBody) {
|
||||
while (conn.currentLine.find("\r\n") != std::string::npos) {
|
||||
auto line =
|
||||
conn.currentLine.substr(0, conn.currentLine.find("\r\n"));
|
||||
conn.currentLine = conn.currentLine.substr(
|
||||
conn.currentLine.find("\r\n") + 2, conn.currentLine.size());
|
||||
if (line.find("GET ") != std::string::npos ||
|
||||
line.find("POST ") != std::string::npos ||
|
||||
line.find("OPTIONS ") != std::string::npos) {
|
||||
conn.httpMethod = line;
|
||||
}
|
||||
// append buffer to partialBuffer
|
||||
conn.partialBuffer.insert(conn.partialBuffer.end(), conn.buffer.begin(),
|
||||
conn.buffer.begin() + nbytes);
|
||||
auto stringifiedBuffer =
|
||||
std::string(conn.partialBuffer.data(),
|
||||
conn.partialBuffer.data() + conn.partialBuffer.size());
|
||||
|
||||
if (line.find("Content-Length: ") != std::string::npos) {
|
||||
conn.contentLength =
|
||||
std::stoi(line.substr(16, line.size() - 1));
|
||||
}
|
||||
// detect hostname for captive portal
|
||||
if (line.find("Host: connectivitycheck.gstatic.com") !=
|
||||
std::string::npos) {
|
||||
conn.isCaptivePortal = true;
|
||||
BELL_LOG(info, "http", "Captive portal request detected");
|
||||
}
|
||||
if (line.size() == 0) {
|
||||
if (conn.contentLength != 0) {
|
||||
conn.isReadingBody = true;
|
||||
goto READBODY;
|
||||
READBODY:
|
||||
auto readSize = 0;
|
||||
|
||||
while (stringifiedBuffer.find("\r\n") != std::string::npos) {
|
||||
auto line =
|
||||
stringifiedBuffer.substr(0, stringifiedBuffer.find("\r\n"));
|
||||
readSize += stringifiedBuffer.find("\r\n") + 2;
|
||||
stringifiedBuffer = stringifiedBuffer.substr(
|
||||
stringifiedBuffer.find("\r\n") + 2, stringifiedBuffer.size());
|
||||
if (line.find("GET ") != std::string::npos ||
|
||||
line.find("POST ") != std::string::npos ||
|
||||
line.find("OPTIONS ") != std::string::npos) {
|
||||
conn.httpMethod = line;
|
||||
}
|
||||
|
||||
if (line.find("Content-Length: ") != std::string::npos) {
|
||||
conn.contentLength =
|
||||
std::stoi(line.substr(16, line.size() - 1));
|
||||
}
|
||||
// detect hostname for captive portal
|
||||
if (line.find("Host: connectivitycheck.gstatic.com") !=
|
||||
std::string::npos) {
|
||||
conn.isCaptivePortal = true;
|
||||
BELL_LOG(info, "http", "Captive portal request detected");
|
||||
}
|
||||
if (line.size() == 0) {
|
||||
if (conn.contentLength != 0) {
|
||||
// conn.isReadingBody = true;
|
||||
// remove readSize bytes from partialBuffer
|
||||
conn.partialBuffer.erase(conn.partialBuffer.begin(),
|
||||
conn.partialBuffer.begin() +
|
||||
readSize);
|
||||
findAndHandleRoute(conn);
|
||||
// goto READBODY;
|
||||
} else {
|
||||
if (!conn.isCaptivePortal) {
|
||||
findAndHandleRoute(conn);
|
||||
} else {
|
||||
if (!conn.isCaptivePortal) {
|
||||
findAndHandleRoute(conn.httpMethod,
|
||||
conn.currentLine, clientFd);
|
||||
} else {
|
||||
this->redirectCaptivePortal(clientFd);
|
||||
}
|
||||
this->redirectCaptivePortal(clientFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (conn.currentLine.size() >= conn.contentLength) {
|
||||
findAndHandleRoute(conn.httpMethod, conn.currentLine, clientFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,8 +376,11 @@ bell::HTTPServer::parseQueryString(const std::string &queryString) {
|
||||
return query;
|
||||
}
|
||||
|
||||
void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
|
||||
int connectionFd) {
|
||||
void bell::HTTPServer::findAndHandleRoute(HTTPConnection &conn) {
|
||||
conn.headersRead = true;
|
||||
auto connectionFd = conn.fd;
|
||||
// auto body = conn.partialBuffer;
|
||||
auto url = conn.httpMethod;
|
||||
std::map<std::string, std::string> pathParams;
|
||||
std::map<std::string, std::string> queryParams;
|
||||
|
||||
@@ -451,18 +463,36 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
if (body.find('&') != std::string::npos) {
|
||||
queryParams = this->parseQueryString(body);
|
||||
auto reader = std::make_unique<RequestBodyReader>(
|
||||
conn.contentLength, conn.fd, conn.partialBuffer);
|
||||
|
||||
auto body = std::string();
|
||||
if (route.readBodyToStr) {
|
||||
body.resize(conn.contentLength);
|
||||
auto read = 0;
|
||||
while (read < conn.contentLength) {
|
||||
auto readBytes = reader->read(
|
||||
body.data() + read, conn.contentLength - read);
|
||||
read += readBytes;
|
||||
}
|
||||
body.resize(read);
|
||||
if (body.find('&') != std::string::npos) {
|
||||
queryParams = this->parseQueryString(body);
|
||||
}
|
||||
}
|
||||
|
||||
HTTPRequest req = {.urlParams = pathParams,
|
||||
.queryParams = queryParams,
|
||||
.body = body,
|
||||
.url = path,
|
||||
.handlerId = 0,
|
||||
.connection = connectionFd};
|
||||
std::unique_ptr<HTTPRequest> req =
|
||||
std::make_unique<HTTPRequest>();
|
||||
req->queryParams = queryParams;
|
||||
req->urlParams = pathParams;
|
||||
req->url = path;
|
||||
req->body = body;
|
||||
req->connection = connectionFd;
|
||||
req->handlerId = 0;
|
||||
req->responseReader = std::move(reader);
|
||||
req->contentLength = conn.contentLength;
|
||||
|
||||
route.handler(req);
|
||||
route.handler(std::move(req));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,19 @@
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "win32shim.h"
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
bell::HTTPStream::HTTPStream()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "platform/TLSSocket.h"
|
||||
#include "TLSSocket.h"
|
||||
|
||||
/**
|
||||
* Platform TLSSocket implementation for the mbedtls
|
||||
37
components/spotify/cspot/bell/src/audio/codec/AACDecoder.cpp
Normal file
37
components/spotify/cspot/bell/src/audio/codec/AACDecoder.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#include "AACDecoder.h"
|
||||
|
||||
AACDecoder::AACDecoder() {
|
||||
aac = AACInitDecoder();
|
||||
pcmData = (int16_t *)malloc(AAC_MAX_NSAMPS * AAC_MAX_NCHANS * sizeof(int16_t));
|
||||
}
|
||||
|
||||
AACDecoder::~AACDecoder() {
|
||||
AACFreeDecoder(aac);
|
||||
free(pcmData);
|
||||
}
|
||||
|
||||
bool AACDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||
frame.sampRateCore = (int)sampleRate;
|
||||
frame.nChans = channelCount;
|
||||
frame.bitsPerSample = bitDepth;
|
||||
return AACSetRawBlockParams(aac, 0, &frame) == 0;
|
||||
}
|
||||
|
||||
uint8_t *AACDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
|
||||
if (!inData)
|
||||
return nullptr;
|
||||
int status = AACDecode(
|
||||
aac,
|
||||
static_cast<unsigned char **>(&inData),
|
||||
reinterpret_cast<int *>(&inLen),
|
||||
static_cast<short *>(this->pcmData));
|
||||
AACGetLastFrameInfo(aac, &frame);
|
||||
if (status != ERR_AAC_NONE) {
|
||||
lastErrno = status;
|
||||
return nullptr;
|
||||
}
|
||||
outLen = frame.outputSamps * sizeof(int16_t);
|
||||
return (uint8_t *)pcmData;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#include "ALACDecoder.h"
|
||||
|
||||
ALACDecoder::ALACDecoder() {
|
||||
// aac = AACInitDecoder();
|
||||
// pcmData = (int16_t *)malloc(AAC_MAX_NSAMPS * AAC_MAX_NCHANS * sizeof(int16_t));
|
||||
}
|
||||
|
||||
ALACDecoder::~ALACDecoder() {
|
||||
// AACFreeDecoder(aac);
|
||||
// free(pcmData);
|
||||
}
|
||||
|
||||
bool ALACDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||
// frame.sampRateCore = (int)sampleRate;
|
||||
// frame.nChans = channelCount;
|
||||
// frame.bitsPerSample = bitDepth;
|
||||
// return AACSetRawBlockParams(aac, 0, &frame) == 0;
|
||||
}
|
||||
|
||||
uint8_t *ALACDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
|
||||
// if (!inData)
|
||||
// return nullptr;
|
||||
// int status = AACDecode(
|
||||
// aac,
|
||||
// static_cast<unsigned char **>(&inData),
|
||||
// reinterpret_cast<int *>(&inLen),
|
||||
// static_cast<short *>(this->pcmData));
|
||||
// AACGetLastFrameInfo(aac, &frame);
|
||||
// if (status != ERR_AAC_NONE) {
|
||||
// lastErrno = status;
|
||||
// return nullptr;
|
||||
// }
|
||||
// outLen = frame.outputSamps * sizeof(int16_t);
|
||||
// return (uint8_t *)pcmData;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#include "AudioCodecs.h"
|
||||
#include <map>
|
||||
|
||||
#ifdef BELL_CODEC_AAC
|
||||
#include "AACDecoder.h"
|
||||
static std::shared_ptr<AACDecoder> codecAac;
|
||||
#endif
|
||||
|
||||
#ifdef BELL_CODEC_MP3
|
||||
#include "MP3Decoder.h"
|
||||
static std::shared_ptr<MP3Decoder> codecMp3;
|
||||
#endif
|
||||
|
||||
#ifdef BELL_CODEC_VORBIS
|
||||
#include "VorbisDecoder.h"
|
||||
static std::shared_ptr<VorbisDecoder> codecVorbis;
|
||||
#endif
|
||||
|
||||
#ifdef BELL_CODEC_OPUS
|
||||
#include "OPUSDecoder.h"
|
||||
static std::shared_ptr<OPUSDecoder> codecOpus;
|
||||
#endif
|
||||
|
||||
std::map<AudioCodec, std::shared_ptr<BaseCodec>> customCodecs;
|
||||
|
||||
std::shared_ptr<BaseCodec> AudioCodecs::getCodec(AudioCodec type) {
|
||||
if (customCodecs.find(type) != customCodecs.end())
|
||||
return customCodecs[type];
|
||||
switch (type) {
|
||||
#ifdef BELL_CODEC_AAC
|
||||
case AudioCodec::AAC:
|
||||
if (codecAac)
|
||||
return codecAac;
|
||||
codecAac = std::make_shared<AACDecoder>();
|
||||
return codecAac;
|
||||
#endif
|
||||
#ifdef BELL_CODEC_MP3
|
||||
case AudioCodec::MP3:
|
||||
if (codecMp3)
|
||||
return codecMp3;
|
||||
codecMp3 = std::make_shared<MP3Decoder>();
|
||||
return codecMp3;
|
||||
#endif
|
||||
#ifdef BELL_CODEC_VORBIS
|
||||
case AudioCodec::VORBIS:
|
||||
if (codecVorbis)
|
||||
return codecVorbis;
|
||||
codecVorbis = std::make_shared<VorbisDecoder>();
|
||||
return codecVorbis;
|
||||
#endif
|
||||
#ifdef BELL_CODEC_OPUS
|
||||
case AudioCodec::OPUS:
|
||||
if (codecOpus)
|
||||
return codecOpus;
|
||||
codecOpus = std::make_shared<OPUSDecoder>();
|
||||
return codecOpus;
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<BaseCodec> AudioCodecs::getCodec(BaseContainer *container) {
|
||||
return getCodec(container->codec);
|
||||
}
|
||||
|
||||
void AudioCodecs::addCodec(AudioCodec type, const std::shared_ptr<BaseCodec> &codec) {
|
||||
customCodecs[type] = codec;
|
||||
}
|
||||
13
components/spotify/cspot/bell/src/audio/codec/BaseCodec.cpp
Normal file
13
components/spotify/cspot/bell/src/audio/codec/BaseCodec.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-12.
|
||||
|
||||
#include "BaseCodec.h"
|
||||
|
||||
bool BaseCodec::setup(BaseContainer *container) {
|
||||
return this->setup(container->sampleRate, container->channelCount, container->bitDepth);
|
||||
}
|
||||
|
||||
uint8_t *BaseCodec::decode(BaseContainer *container, uint32_t &outLen) {
|
||||
uint32_t len;
|
||||
auto *data = container->readSample(len);
|
||||
return decode(data, len, outLen);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "DecoderGlobals.h"
|
||||
|
||||
bell::DecodersInstance* bell::decodersInstance;
|
||||
|
||||
void bell::createDecoders()
|
||||
{
|
||||
bell::decodersInstance = new bell::DecodersInstance();
|
||||
}
|
||||
35
components/spotify/cspot/bell/src/audio/codec/MP3Decoder.cpp
Normal file
35
components/spotify/cspot/bell/src/audio/codec/MP3Decoder.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
|
||||
|
||||
#include "MP3Decoder.h"
|
||||
|
||||
MP3Decoder::MP3Decoder() {
|
||||
mp3 = MP3InitDecoder();
|
||||
pcmData = (int16_t *)malloc(MAX_NSAMP * MAX_NGRAN * MAX_NCHAN * sizeof(int16_t));
|
||||
}
|
||||
|
||||
MP3Decoder::~MP3Decoder() {
|
||||
MP3FreeDecoder(mp3);
|
||||
free(pcmData);
|
||||
}
|
||||
|
||||
bool MP3Decoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *MP3Decoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
|
||||
if (!inData)
|
||||
return nullptr;
|
||||
int status = MP3Decode(
|
||||
mp3,
|
||||
static_cast<unsigned char **>(&inData),
|
||||
reinterpret_cast<int *>(&inLen),
|
||||
static_cast<short *>(this->pcmData),
|
||||
/* useSize */ 0);
|
||||
MP3GetLastFrameInfo(mp3, &frame);
|
||||
if (status != ERR_MP3_NONE) {
|
||||
lastErrno = status;
|
||||
return nullptr;
|
||||
}
|
||||
outLen = frame.outputSamps * sizeof(int16_t);
|
||||
return (uint8_t *)pcmData;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
|
||||
|
||||
#include "OPUSDecoder.h"
|
||||
#include "opus.h"
|
||||
|
||||
#define MAX_FRAME_SIZE 6 * 960
|
||||
#define MAX_CHANNELS 2
|
||||
|
||||
// dummy structure, just to get access to channels
|
||||
struct OpusDecoder {
|
||||
int dummy1;
|
||||
int dummy2;
|
||||
int channels;
|
||||
};
|
||||
|
||||
OPUSDecoder::OPUSDecoder() {
|
||||
opus = nullptr;
|
||||
pcmData = (int16_t *)malloc(MAX_FRAME_SIZE * MAX_CHANNELS * sizeof(int16_t));
|
||||
}
|
||||
|
||||
OPUSDecoder::~OPUSDecoder() {
|
||||
if (opus)
|
||||
opus_decoder_destroy(opus);
|
||||
free(pcmData);
|
||||
}
|
||||
|
||||
bool OPUSDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||
if (opus)
|
||||
opus_decoder_destroy(opus);
|
||||
opus = opus_decoder_create((int32_t)sampleRate, channelCount, &lastErrno);
|
||||
return !lastErrno;
|
||||
}
|
||||
|
||||
uint8_t *OPUSDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
|
||||
if (!inData)
|
||||
return nullptr;
|
||||
outLen = opus_decode(
|
||||
opus,
|
||||
static_cast<unsigned char *>(inData),
|
||||
static_cast<int32_t>(inLen),
|
||||
pcmData,
|
||||
MAX_FRAME_SIZE,
|
||||
false);
|
||||
outLen *= opus->channels * sizeof(int16_t);
|
||||
return (uint8_t *)pcmData;
|
||||
}
|
||||
120
components/spotify/cspot/bell/src/audio/codec/VorbisDecoder.cpp
Normal file
120
components/spotify/cspot/bell/src/audio/codec/VorbisDecoder.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-14.
|
||||
|
||||
#include "VorbisDecoder.h"
|
||||
#include "AudioCodecs.h"
|
||||
|
||||
extern "C" {
|
||||
extern vorbis_dsp_state *vorbis_dsp_create(vorbis_info *vi);
|
||||
extern void vorbis_dsp_destroy(vorbis_dsp_state *v);
|
||||
extern int vorbis_dsp_restart(vorbis_dsp_state *v);
|
||||
extern int vorbis_dsp_headerin(vorbis_info *vi, vorbis_comment *vc, ogg_packet *op);
|
||||
extern int vorbis_dsp_synthesis(vorbis_dsp_state *vd, ogg_packet *op, int decodep);
|
||||
extern int vorbis_dsp_pcmout(vorbis_dsp_state *v, ogg_int16_t *pcm, int samples);
|
||||
extern int vorbis_dsp_read(vorbis_dsp_state *v, int samples);
|
||||
}
|
||||
|
||||
#define VORBIS_BUF_SAMPLES 1024
|
||||
#define VORBIS_BUF_CHANNELS 2
|
||||
|
||||
VorbisDecoder::VorbisDecoder() {
|
||||
vi = new vorbis_info;
|
||||
vorbis_info_init(vi);
|
||||
vc = new vorbis_comment;
|
||||
vorbis_comment_init(vc);
|
||||
|
||||
op.packet = new ogg_reference;
|
||||
op.packet->buffer = new ogg_buffer;
|
||||
op.packet->buffer->refcount = 0;
|
||||
op.packet->buffer->ptr.owner = nullptr;
|
||||
op.packet->buffer->ptr.next = nullptr;
|
||||
op.packet->begin = 0;
|
||||
op.packet->next = nullptr;
|
||||
op.granulepos = -1;
|
||||
op.packetno = 10;
|
||||
|
||||
pcmData = (int16_t *)malloc(VORBIS_BUF_SAMPLES * VORBIS_BUF_CHANNELS * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
VorbisDecoder::~VorbisDecoder() {
|
||||
vorbis_info_clear(vi);
|
||||
vorbis_comment_clear(vc);
|
||||
if (vd)
|
||||
vorbis_dsp_destroy(vd);
|
||||
vd = nullptr;
|
||||
free(pcmData);
|
||||
}
|
||||
|
||||
bool VorbisDecoder::setup(BaseContainer *container) {
|
||||
uint32_t setupLen;
|
||||
uint8_t *setup = container->getSetupData(setupLen, AudioCodec::VORBIS);
|
||||
if (!setup)
|
||||
return false;
|
||||
op.b_o_s = true; // mark this page as beginning of stream
|
||||
uint32_t bytesLeft = setupLen - 1; // minus header count length (8 bit)
|
||||
std::vector<uint32_t> headers(setup[0]);
|
||||
for (uint8_t i = 0; i < setup[0]; i++) {
|
||||
uint8_t *sizeByte = (uint8_t *)setup + 1 + i;
|
||||
headers[i] = 0;
|
||||
while (*sizeByte == 255) {
|
||||
headers[i] += *(sizeByte++);
|
||||
bytesLeft--;
|
||||
}
|
||||
headers[i] += *sizeByte;
|
||||
bytesLeft--;
|
||||
}
|
||||
// parse all headers from the setup data
|
||||
for (const auto &headerSize : headers) {
|
||||
setPacket(setup + setupLen - bytesLeft, headerSize);
|
||||
bytesLeft -= headerSize;
|
||||
lastErrno = vorbis_dsp_headerin(vi, vc, &op);
|
||||
if (lastErrno < 0) {
|
||||
bytesLeft = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// parse last header, not present in header table (seems to happen for MP4 containers)
|
||||
if (bytesLeft) {
|
||||
setPacket(setup + setupLen - bytesLeft, bytesLeft);
|
||||
lastErrno = vorbis_dsp_headerin(vi, vc, &op);
|
||||
}
|
||||
// disable BOS to allow reading audio data
|
||||
op.b_o_s = false;
|
||||
// set up the codec
|
||||
if (vd)
|
||||
vorbis_dsp_restart(vd);
|
||||
else
|
||||
vd = vorbis_dsp_create(vi);
|
||||
return !lastErrno;
|
||||
}
|
||||
|
||||
bool VorbisDecoder::setup(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
||||
// manual setup is not allowed
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *VorbisDecoder::decode(uint8_t *inData, uint32_t inLen, uint32_t &outLen) {
|
||||
if (!inData || !vi)
|
||||
return nullptr;
|
||||
setPacket(inData, inLen);
|
||||
// sources:
|
||||
// - vorbisfile.c:556
|
||||
// - vorbisfile.c:1557
|
||||
lastErrno = vorbis_dsp_synthesis(vd, &op, 1);
|
||||
if (lastErrno < 0)
|
||||
return nullptr;
|
||||
int samples = vorbis_dsp_pcmout(vd, pcmData, VORBIS_BUF_SAMPLES);
|
||||
outLen = samples;
|
||||
if (samples) {
|
||||
if (samples > 0) {
|
||||
vorbis_dsp_read(vd, samples);
|
||||
outLen = samples * 2 * vi->channels;
|
||||
}
|
||||
}
|
||||
return (uint8_t *)pcmData;
|
||||
}
|
||||
|
||||
void VorbisDecoder::setPacket(uint8_t *inData, uint32_t inLen) const {
|
||||
op.packet->buffer->data = static_cast<unsigned char *>(inData);
|
||||
op.packet->buffer->size = static_cast<long>(inLen);
|
||||
op.packet->length = static_cast<long>(inLen);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-15.
|
||||
|
||||
#include "AudioContainers.h"
|
||||
#include "Mpeg4Container.h"
|
||||
#ifdef _WIN32
|
||||
#include "win32shim.h"
|
||||
#endif
|
||||
|
||||
std::unique_ptr<BaseContainer> AudioContainers::create(const char *mimeType) {
|
||||
char *type = strchr((char *)mimeType, '/');
|
||||
if (!type || *(++type) == '\0')
|
||||
return nullptr;
|
||||
|
||||
if (strncasecmp(type, "mp4", 3) == 0)
|
||||
return std::make_unique<Mpeg4Container>();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-7.
|
||||
|
||||
#include "BaseContainer.h"
|
||||
|
||||
void BaseContainer::feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) {
|
||||
this->reader = std::make_unique<bell::BinaryReader>(stream);
|
||||
this->source = stream;
|
||||
this->pos = position;
|
||||
}
|
||||
|
||||
// TODO move source stream reading here, and set closed = true when stream ends
|
||||
|
||||
uint8_t BaseContainer::readUint8() {
|
||||
pos += 1;
|
||||
return reader->readByte();
|
||||
}
|
||||
|
||||
uint16_t BaseContainer::readUint16() {
|
||||
pos += 2;
|
||||
return reader->readShort();
|
||||
}
|
||||
|
||||
uint32_t BaseContainer::readUint24() {
|
||||
uint8_t b[3];
|
||||
readBytes(b, 3);
|
||||
return static_cast<int32_t>((b[2]) | (b[1] << 8) | (b[0] << 16));
|
||||
}
|
||||
|
||||
uint32_t BaseContainer::readUint32() {
|
||||
pos += 4;
|
||||
return reader->readUInt();
|
||||
}
|
||||
|
||||
uint64_t BaseContainer::readUint64() {
|
||||
pos += 8;
|
||||
return reader->readLong();
|
||||
}
|
||||
|
||||
uint32_t BaseContainer::readVarint32() {
|
||||
uint8_t b = readUint8();
|
||||
uint32_t result = b & 0x7f;
|
||||
while (b & 0b10000000) {
|
||||
b = readUint8();
|
||||
result <<= 7;
|
||||
result |= b & 0x7f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t BaseContainer::readBytes(uint8_t *dst, uint32_t num) {
|
||||
if (!num)
|
||||
return 0;
|
||||
uint32_t len, total = 0;
|
||||
do {
|
||||
if (dst) {
|
||||
len = source->read(dst, num);
|
||||
dst += len; // increment destination pointer
|
||||
} else {
|
||||
len = source->skip(num);
|
||||
}
|
||||
total += len; // increment total read count
|
||||
pos += len; // increment absolute source position
|
||||
num -= len; // decrement bytes left to read
|
||||
} while (len && num);
|
||||
if (!len) // source->read() returned 0, it's closed
|
||||
closed = true;
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32_t BaseContainer::skipBytes(uint32_t num) {
|
||||
return readBytes(nullptr, num);
|
||||
}
|
||||
|
||||
uint32_t BaseContainer::skipTo(uint32_t offset) {
|
||||
if (offset <= pos)
|
||||
return 0;
|
||||
return readBytes(nullptr, offset - pos);
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-8.
|
||||
|
||||
#include "Mpeg4Container.h"
|
||||
#include "AudioCodecs.h"
|
||||
#include "Mpeg4Atoms.h"
|
||||
#include "Mpeg4Types.h"
|
||||
|
||||
Mpeg4Container::~Mpeg4Container() {
|
||||
freeAll();
|
||||
this->source->close();
|
||||
}
|
||||
|
||||
void Mpeg4Container::feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) {
|
||||
BaseContainer::feed(stream, position);
|
||||
if (isParsed) {
|
||||
// this is needed to support seeking backwards, as goToData() always moves forward only
|
||||
setCurrentFragment();
|
||||
isInData = false;
|
||||
goToData();
|
||||
}
|
||||
}
|
||||
|
||||
bool Mpeg4Container::parse() {
|
||||
freeAll();
|
||||
if (pos != 0) {
|
||||
isParsed = false;
|
||||
return false;
|
||||
}
|
||||
uint32_t size;
|
||||
AtomType type;
|
||||
uint32_t moovEnd = 0, mdiaEnd = 0;
|
||||
bool parsed = false, error = false, hasMoov = false, hasHdlr = false;
|
||||
while (!parsed && !error && !closed) {
|
||||
readAtomHeader(size, (uint32_t &)type);
|
||||
switch (type) {
|
||||
/*case AtomType::ATOM_FTYP:
|
||||
readBytes(mediaBrand, 4);
|
||||
mediaBrand[4] = '\0';
|
||||
skipBytes(size - 4);
|
||||
break;*/
|
||||
|
||||
case AtomType::ATOM_MVEX:
|
||||
case AtomType::ATOM_TRAK:
|
||||
case AtomType::ATOM_MINF:
|
||||
case AtomType::ATOM_STBL:
|
||||
// this causes the next iteration to read the next direct child atom
|
||||
continue;
|
||||
case AtomType::ATOM_MOOV:
|
||||
moovEnd = pos + size;
|
||||
hasMoov = true;
|
||||
continue;
|
||||
case AtomType::ATOM_MDIA:
|
||||
mdiaEnd = pos + size;
|
||||
continue;
|
||||
|
||||
case AtomType::ATOM_TREX:
|
||||
readTrex();
|
||||
break;
|
||||
case AtomType::ATOM_TKHD:
|
||||
skipBytes(12);
|
||||
if (audioTrackId == -1) {
|
||||
audioTrackId = (int8_t)readUint32();
|
||||
skipBytes(size - 16);
|
||||
} else {
|
||||
// new track header, but audio track already found
|
||||
skipTo(moovEnd);
|
||||
}
|
||||
break;
|
||||
case AtomType::ATOM_MDHD:
|
||||
skipBytes(12);
|
||||
timescale = readUint32();
|
||||
totalDuration = readUint32();
|
||||
totalDurationPresent = true;
|
||||
durationMs = totalDuration * 1000LL / timescale;
|
||||
if (!sampleRate)
|
||||
sampleRate = timescale;
|
||||
hasHdlr = false;
|
||||
skipBytes(size - 20);
|
||||
break;
|
||||
case AtomType::ATOM_HDLR:
|
||||
if (hasHdlr) {
|
||||
skipBytes(size);
|
||||
continue;
|
||||
}
|
||||
hasHdlr = true;
|
||||
skipBytes(8);
|
||||
if (readUint32() != (uint32_t)AtomType::ATOM_SOUN) {
|
||||
skipTo(mdiaEnd); // skip the rest of mdia atom
|
||||
audioTrackId = -1; // unset the track ID, so the next tkhd can set it
|
||||
} else {
|
||||
skipBytes(size - 12);
|
||||
}
|
||||
break;
|
||||
case AtomType::ATOM_STSD:
|
||||
readStsd();
|
||||
break;
|
||||
case AtomType::ATOM_STTS:
|
||||
readStts();
|
||||
break;
|
||||
case AtomType::ATOM_STSC:
|
||||
readStsc();
|
||||
break;
|
||||
case AtomType::ATOM_STCO:
|
||||
readStco();
|
||||
break;
|
||||
case AtomType::ATOM_STSZ:
|
||||
readStsz();
|
||||
break;
|
||||
case AtomType::ATOM_SIDX:
|
||||
readSidx(size);
|
||||
break;
|
||||
case AtomType::ATOM_MOOF:
|
||||
case AtomType::ATOM_MDAT:
|
||||
// the track can be accessed randomly if all the tables are set before parsing the first fragment
|
||||
isSeekable = fragmentsLen || (chunksLen && chunkOffsetsLen && samplesLen && sampleSizesLen);
|
||||
if (type == AtomType::ATOM_MOOF) {
|
||||
// this will seek to the start of mdat header
|
||||
error = !parseMoof(size);
|
||||
} else {
|
||||
// pos already points to sample data
|
||||
isInData = true;
|
||||
}
|
||||
parsed = true;
|
||||
break;
|
||||
default:
|
||||
// ignore unknown atoms
|
||||
skipBytes(size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sampleDescLen) {
|
||||
codec = getCodec(sampleDesc);
|
||||
}
|
||||
if (!hasMoov || audioTrackId == -1 || codec == AudioCodec::UNKNOWN) {
|
||||
// this is not a progressive MP4, can't be played
|
||||
// or has no audio tracks
|
||||
// or has an unknown audio codec
|
||||
freeAll();
|
||||
isParsed = false;
|
||||
return false;
|
||||
}
|
||||
if (isInData) {
|
||||
// [pos] points to mdat, create a dummy fragment for it
|
||||
createFragment()->duration = totalDuration;
|
||||
}
|
||||
isParsed = !error && !closed;
|
||||
setCurrentFragment();
|
||||
setCurrentSample();
|
||||
return isParsed;
|
||||
}
|
||||
|
||||
bool Mpeg4Container::parseMoof(uint32_t moofSize) {
|
||||
freeFragment();
|
||||
uint32_t size;
|
||||
AtomType type;
|
||||
uint32_t moofOffset = pos - 8;
|
||||
uint32_t moofEnd = pos + moofSize;
|
||||
uint32_t trafEnd = 0;
|
||||
|
||||
bool hasFragment = false;
|
||||
Mpeg4Fragment *fragment;
|
||||
for (fragment = fragments; fragment < fragments + fragmentsLen; fragment++) {
|
||||
if (isInFragment(fragment, pos)) {
|
||||
hasFragment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (pos < moofEnd) {
|
||||
readAtomHeader(size, (uint32_t &)type);
|
||||
switch (type) {
|
||||
case AtomType::ATOM_TRAF:
|
||||
trafEnd = pos + size;
|
||||
continue;
|
||||
case AtomType::ATOM_TFHD:
|
||||
readTfhd(trafEnd, moofOffset);
|
||||
break;
|
||||
case AtomType::ATOM_TRUN:
|
||||
readTrun(size, moofOffset);
|
||||
break;
|
||||
default:
|
||||
skipBytes(size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// this moof is not in the fragments table
|
||||
if (!hasFragment) {
|
||||
fragment = createFragment();
|
||||
}
|
||||
if (!totalDurationPresent) {
|
||||
// total duration was not found or a new fragment was created
|
||||
uint32_t duration = 0;
|
||||
for (Mpeg4SampleRange *sr = samples; sr < samples + samplesLen; sr++) {
|
||||
duration += sr->count * sr->duration;
|
||||
}
|
||||
fragment->duration = duration;
|
||||
totalDuration += duration;
|
||||
durationMs = totalDuration * 1000LL / timescale;
|
||||
}
|
||||
isFragmented = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Mpeg4Container::getLoadingOffset(uint32_t timeMs) {
|
||||
if (!isParsed)
|
||||
return SAMPLE_NOT_LOADED;
|
||||
if (!isSeekable)
|
||||
return SAMPLE_NOT_SEEKABLE;
|
||||
// timeScaled - specified time in the media time coordinate system
|
||||
uint64_t timeScaled = (uint64_t)timeMs * timescale / 1000LL;
|
||||
uint64_t timeAbs = 0;
|
||||
|
||||
Mpeg4Fragment *fragment = fragments;
|
||||
for (; fragment < fragments + fragmentsLen; fragment++) {
|
||||
timeAbs += fragment->duration; // timeAbs holds the fragment end time
|
||||
if (timeScaled < timeAbs) {
|
||||
timeAbs -= fragment->duration; // set timeAbs to fragment start time
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fragment)
|
||||
return SAMPLE_NOT_FOUND;
|
||||
if (fragment != curFragment)
|
||||
return (int32_t)fragment->start;
|
||||
// get the position in bytes
|
||||
return (int32_t)findSample((int64_t)timeScaled, -1, timeAbs);
|
||||
}
|
||||
|
||||
bool Mpeg4Container::goToData() {
|
||||
if (!isParsed || !curFragment)
|
||||
return false;
|
||||
if (isInData)
|
||||
return true;
|
||||
uint32_t size;
|
||||
AtomType type;
|
||||
|
||||
if (pos == curFragment->start || pos >= curFragment->end) {
|
||||
// fragment ended, or a new one just loaded
|
||||
while (pos >= curFragment->end && curFragment < fragments + fragmentsLen - 1) {
|
||||
// skip to the next fragment header
|
||||
curFragment++;
|
||||
} // else, no more **loaded** fragments
|
||||
if (pos < curFragment->start && !skipTo(curFragment->start))
|
||||
return false;
|
||||
// [pos] is either a fragment header, EOF or unknown data
|
||||
readAtomHeader(size, (uint32_t &)type);
|
||||
if (type == AtomType::ATOM_MOOF) {
|
||||
// fragment header found, try to parse it
|
||||
parseMoof(size);
|
||||
// update [curFragment]
|
||||
setCurrentFragment();
|
||||
// read mdat header
|
||||
readAtomHeader(size, (uint32_t &)type);
|
||||
}
|
||||
if (type != AtomType::ATOM_MDAT)
|
||||
return false;
|
||||
} else if (pos >= curChunk.end) {
|
||||
// chunk ended, but still in [curFragment]
|
||||
if (!curChunk.nextStart) // no more chunks but fragment not ended ??
|
||||
return false;
|
||||
if (pos != curChunk.nextStart && !skipTo(curChunk.nextStart))
|
||||
return false;
|
||||
} /* else {
|
||||
readAtomHeader(size, (uint32_t &)type);
|
||||
return false;
|
||||
}*/
|
||||
// update [isInData], [curChunk] and [curSampleSize]
|
||||
setCurrentSample();
|
||||
if (pos < curChunk.start) {
|
||||
// chunk not started yet, probably a multi-track movie
|
||||
if (!skipTo(curChunk.start))
|
||||
return false;
|
||||
// update [isInData], [curChunk] and [curSampleSize]
|
||||
setCurrentSample();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpeg4Container::seekTo(uint32_t timeMs) {
|
||||
if (!isParsed || !isSeekable)
|
||||
return false;
|
||||
// try to go to nearest mdat data
|
||||
if (!goToData())
|
||||
return false;
|
||||
uint32_t offset = getLoadingOffset(timeMs);
|
||||
// check if the required [offset] is in the currently loaded fragment
|
||||
// - if it is, [offset] points to the required sample
|
||||
// - if it isn't, [offset] points to a moof header
|
||||
if (!isInFragment(curFragment, offset)) {
|
||||
// try to seek to moof header, fail if not possible
|
||||
if (offset != pos && !skipTo(offset))
|
||||
return false;
|
||||
// [pos] points to a moof header
|
||||
isInData = false;
|
||||
// parse the just loaded atom header (pos >= curFragment->end)
|
||||
if (!goToData())
|
||||
return false;
|
||||
// get the actual sample's offset
|
||||
offset = getLoadingOffset(timeMs);
|
||||
// ...or give up if still not loaded
|
||||
if (!isInFragment(curFragment, offset))
|
||||
return false;
|
||||
}
|
||||
if (!isInData) // something is really not ok
|
||||
return false;
|
||||
// [pos] points to mdat data
|
||||
// [offset] points to the required sample
|
||||
if (!skipTo(offset))
|
||||
return false;
|
||||
// update the current chunk range and sample sizes
|
||||
setCurrentSample();
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Mpeg4Container::getCurrentTimeMs() {
|
||||
if (!curFragment || !isParsed)
|
||||
return 0;
|
||||
int64_t time = 0;
|
||||
// get time offset of the current fragment
|
||||
Mpeg4Fragment *f = fragments;
|
||||
while (f != curFragment) {
|
||||
time += (f++)->duration;
|
||||
}
|
||||
time = findSample(-1, (int32_t)pos, time);
|
||||
if (time < 0)
|
||||
return (int32_t)time;
|
||||
return (int32_t)(time * 1000LL / timescale);
|
||||
}
|
||||
|
||||
uint8_t *Mpeg4Container::readSample(uint32_t &len) {
|
||||
if (!curFragment || !isParsed)
|
||||
return nullptr;
|
||||
if (!sampleData) {
|
||||
allocSampleData();
|
||||
}
|
||||
// go to mdat
|
||||
if (!isInData && !goToData())
|
||||
return nullptr;
|
||||
len = *curSampleSize;
|
||||
len = readBytes(sampleData, len);
|
||||
skipBytes(*curSampleSize - len); // skip the rest if something went wrong
|
||||
if (sampleSizesLen > 1)
|
||||
curSampleSize++;
|
||||
if (pos >= curChunk.end) {
|
||||
// chunk ended, make goToData() read the next one
|
||||
isInData = false;
|
||||
}
|
||||
return sampleData;
|
||||
}
|
||||
|
||||
uint8_t *Mpeg4Container::getSetupData(uint32_t &len, AudioCodec matchCodec) {
|
||||
for (SampleDescription *desc = sampleDesc; desc < sampleDesc + sampleDescLen; desc++) {
|
||||
if (matchCodec != getCodec(desc))
|
||||
continue;
|
||||
len = desc->dataLength;
|
||||
return desc->data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-10.
|
||||
|
||||
#include "BellUtils.h"
|
||||
#include "Mpeg4Container.h"
|
||||
#include "Mpeg4Types.h"
|
||||
|
||||
using namespace bell;
|
||||
|
||||
/** Populate [chunks] using the Sample-to-chunk Table */
|
||||
void Mpeg4Container::readStsc() {
|
||||
skipBytes(4); // skip version and flags
|
||||
chunksLen = readUint32();
|
||||
chunks = (Mpeg4ChunkRange *)malloc(chunksLen * sizeof(Mpeg4ChunkRange));
|
||||
for (uint32_t i = 0; i < chunksLen; i++) {
|
||||
chunks[i].count = readUint32();
|
||||
chunks[i].samples = readUint32();
|
||||
chunks[i].sampleDescriptionId = readUint32();
|
||||
if (i > 0) {
|
||||
chunks[i - 1].count = chunks[i].count - chunks[i - 1].count;
|
||||
}
|
||||
}
|
||||
if (chunkOffsetsLen) {
|
||||
chunks[chunksLen - 1].count = chunkOffsetsLen - chunks[chunksLen - 1].count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Populate [chunkOffsets] using the Chunk Offset Table */
|
||||
void Mpeg4Container::readStco() {
|
||||
skipBytes(4); // skip version and flags
|
||||
chunkOffsetsLen = readUint32();
|
||||
chunkOffsets = (Mpeg4ChunkOffset *)malloc(chunkOffsetsLen * sizeof(Mpeg4ChunkOffset));
|
||||
for (uint32_t i = 0; i < chunkOffsetsLen; i++) {
|
||||
chunkOffsets[i] = readUint32();
|
||||
}
|
||||
if (chunksLen) {
|
||||
chunks[chunksLen - 1].count = chunkOffsetsLen - chunks[chunksLen - 1].count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Populate [samples] using the Time-to-sample Table */
|
||||
void Mpeg4Container::readStts() {
|
||||
skipBytes(4); // skip version and flags
|
||||
samplesLen = readUint32();
|
||||
samples = (Mpeg4SampleRange *)malloc(samplesLen * sizeof(Mpeg4SampleRange));
|
||||
for (uint32_t i = 0; i < samplesLen; i++) {
|
||||
samples[i].count = readUint32();
|
||||
samples[i].duration = readUint32();
|
||||
}
|
||||
}
|
||||
|
||||
/** Populate [sampleSizes] using the Sample Size Table */
|
||||
void Mpeg4Container::readStsz() {
|
||||
skipBytes(4); // skip version and flags
|
||||
uint32_t sampleSize = readUint32();
|
||||
sampleSizesLen = readUint32();
|
||||
if (sampleSize) {
|
||||
sampleSizesLen = 1;
|
||||
}
|
||||
sampleSizes = (Mpeg4SampleSize *)malloc(sampleSizesLen * sizeof(Mpeg4SampleSize));
|
||||
if (sampleSize) {
|
||||
sampleSizes[0] = sampleSize;
|
||||
if (sampleSize > sampleSizeMax)
|
||||
sampleSizeMax = sampleSize;
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < sampleSizesLen; i++) {
|
||||
sampleSize = readUint32();
|
||||
if (sampleSize > sampleSizeMax)
|
||||
sampleSizeMax = sampleSize;
|
||||
sampleSizes[i] = sampleSize;
|
||||
}
|
||||
// reallocate sampleData if the max size changes
|
||||
allocSampleData();
|
||||
}
|
||||
|
||||
/** Populate [sampleDesc] using the Sample Description Table */
|
||||
void Mpeg4Container::readStsd() {
|
||||
// Helpful resources:
|
||||
// - STSD atom structure - ISO/IEC 14496-1 (page 277) - seems to cover QT desc ver.0
|
||||
// - ESDS atom structure - ISO/IEC 14496-1 (page 28)
|
||||
freeAndNull((void *&)sampleDesc);
|
||||
skipBytes(4); // skip version and flags
|
||||
sampleDescLen = readUint32();
|
||||
sampleDesc = (SampleDescription *)malloc(sampleDescLen * sizeof(SampleDescription));
|
||||
for (SampleDescription *desc = sampleDesc; desc < sampleDesc + sampleDescLen; desc++) {
|
||||
uint32_t entryEnd = readUint32() - 4 + pos;
|
||||
uint32_t esdsEnd = entryEnd;
|
||||
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-BBCHHGBH
|
||||
// General Structure of a Sample Description
|
||||
desc->format = (AudioSampleFormat)readUint32();
|
||||
desc->mp4aObjectType = MP4AObjectType::UNDEFINED;
|
||||
desc->mp4aProfile = MP4AProfile::UNDEFINED;
|
||||
skipBytes(6); // reserved
|
||||
desc->dataReferenceIndex = readUint16();
|
||||
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-75770
|
||||
// Sound Sample Description (Version 0)
|
||||
uint16_t version = readUint16();
|
||||
skipBytes(6); // skip Revision level(2), Vendor(4)
|
||||
channelCount = readUint16();
|
||||
bitDepth = readUint16();
|
||||
skipBytes(4); // skip Compression ID(2), Packet size(2)
|
||||
sampleRate = readUint16();
|
||||
skipBytes(2); // decimal part of sample rate
|
||||
if (version >= 1) {
|
||||
// Sound Sample Description (Version 1)
|
||||
skipBytes(16); // skip Samples per packet(4), Bytes per packet(4), Bytes per frame(4), Bytes per sample(4)
|
||||
}
|
||||
// read the child atom
|
||||
uint32_t atomSize;
|
||||
AtomType atomType;
|
||||
readAtomHeader(atomSize, (uint32_t &)atomType);
|
||||
if (atomType == AtomType::ATOM_WAVE) {
|
||||
do {
|
||||
readAtomHeader(atomSize, (uint32_t &)atomType);
|
||||
if (atomType == AtomType::ATOM_ESDS) {
|
||||
esdsEnd = pos + atomSize;
|
||||
break;
|
||||
}
|
||||
skipBytes(atomSize);
|
||||
} while (pos < entryEnd);
|
||||
if (pos >= entryEnd) // something went wrong
|
||||
continue;
|
||||
}
|
||||
if (atomType != AtomType::ATOM_ESDS) {
|
||||
desc->dataType = (uint32_t)atomType;
|
||||
desc->dataLength = atomSize;
|
||||
desc->data = (uint8_t *)malloc(desc->dataLength);
|
||||
readBytes(desc->data, desc->dataLength);
|
||||
continue;
|
||||
}
|
||||
// read ESDS
|
||||
skipBytes(4); // skip esds flags
|
||||
while (pos < esdsEnd) {
|
||||
uint8_t tag = readUint8();
|
||||
uint32_t size = readVarint32();
|
||||
uint8_t flags;
|
||||
switch (tag) {
|
||||
case 0x03: // ES_Descriptor
|
||||
skipBytes(2);
|
||||
flags = readUint8();
|
||||
if (flags & 0b10000000)
|
||||
skipBytes(2);
|
||||
if (flags & 0b01000000)
|
||||
skipBytes(readUint8());
|
||||
if (flags & 0b00100000)
|
||||
skipBytes(2);
|
||||
break;
|
||||
case 0x04: // DecoderConfigDescriptor
|
||||
desc->mp4aObjectType = (MP4AObjectType)readUint8();
|
||||
skipBytes(12);
|
||||
break;
|
||||
case 0x05: // DecoderSpecificInfo
|
||||
if (desc->mp4aObjectType == MP4AObjectType::MP4A) {
|
||||
desc->mp4aProfile = (MP4AProfile)(readUint8() >> 3);
|
||||
skipBytes(size - 1);
|
||||
} else {
|
||||
desc->dataType = 0;
|
||||
desc->dataLength = size;
|
||||
desc->data = (uint8_t *)malloc(desc->dataLength);
|
||||
readBytes(desc->data, desc->dataLength);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
skipBytes(size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// skip leftover atoms for version 1 QuickTime descriptors
|
||||
skipTo(entryEnd);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-10.
|
||||
|
||||
#include "Mpeg4Container.h"
|
||||
|
||||
/** Populate [fragments] using the Segment Index Table */
|
||||
void Mpeg4Container::readSidx(uint32_t atomSize) {
|
||||
// https://b.goeswhere.com/ISO_IEC_14496-12_2015.pdf (page 121)
|
||||
uint32_t offset = pos + atomSize;
|
||||
uint8_t version = readUint8();
|
||||
skipBytes(3); // skip flags
|
||||
if (audioTrackId != readUint32()) { // check reference_ID
|
||||
skipBytes(atomSize - 8); // skip the rest
|
||||
return;
|
||||
}
|
||||
skipBytes(4); // skip uint(32) timescale
|
||||
skipBytes(version ? 12 : 4); // skip zeroes, depending on version
|
||||
offset += readUint32(); // read first_offset
|
||||
skipBytes(2); // skip uint(16) reserved
|
||||
|
||||
fragmentsLen = readUint16();
|
||||
fragments = (Mpeg4Fragment *)malloc(fragmentsLen * sizeof(Mpeg4Fragment));
|
||||
for (uint32_t i = 0; i < fragmentsLen; i++) {
|
||||
auto size = readUint32();
|
||||
if (size & (1 << 31)) {
|
||||
skipBytes(8);
|
||||
continue; // ignore references to sidx, for now
|
||||
}
|
||||
fragments[i].start = offset;
|
||||
fragments[i].end = offset + size;
|
||||
fragments[i].duration = readUint32();
|
||||
offset += size;
|
||||
skipBytes(4); // skip SAP info
|
||||
}
|
||||
isFragmented = true;
|
||||
}
|
||||
|
||||
/** Populate [sampleDefs] using Track Extends */
|
||||
void Mpeg4Container::readTrex() {
|
||||
// https://b.goeswhere.com/ISO_IEC_14496-12_2015.pdf (page 69)
|
||||
skipBytes(4); // skip version and flags
|
||||
sampleDefsLen++;
|
||||
sampleDefs = (SampleDefaults *)realloc(sampleDefs, sampleDefsLen * sizeof(SampleDefaults));
|
||||
sampleDefTracks = (uint32_t *)realloc(sampleDefTracks, sampleDefsLen * sizeof(uint32_t));
|
||||
uint32_t i = sampleDefsLen - 1;
|
||||
sampleDefTracks[i] = readUint32();
|
||||
sampleDefs[i].offset = 0;
|
||||
sampleDefs[i].sampleDescriptionId = readUint32();
|
||||
sampleDefs[i].duration = readUint32();
|
||||
sampleDefs[i].size = readUint32();
|
||||
sampleDefs[i].flags = readUint32();
|
||||
}
|
||||
|
||||
/** Populate [sampleDefs] using Track Fragment Header */
|
||||
void Mpeg4Container::readTfhd(uint32_t trafEnd, uint32_t moofOffset) {
|
||||
skipBytes(1); // skip version
|
||||
TfFlags tfhdFlags = {};
|
||||
readBytes((uint8_t *)&tfhdFlags, 3);
|
||||
if (audioTrackId != readUint32()) {
|
||||
skipTo(trafEnd); // skip the rest of traf
|
||||
return;
|
||||
}
|
||||
auto *def = getSampleDef(audioTrackId);
|
||||
if (!def) {
|
||||
skipTo(trafEnd); // error?
|
||||
return;
|
||||
}
|
||||
def->offset = 0;
|
||||
if (tfhdFlags.baseDataOffsetPresent)
|
||||
def->offset = readUint64();
|
||||
if (tfhdFlags.sampleDescriptionIndexPresent)
|
||||
def->sampleDescriptionId = readUint32();
|
||||
if (tfhdFlags.defaultSampleDurationPresent)
|
||||
def->duration = readUint32();
|
||||
if (tfhdFlags.defaultSampleSizePresent)
|
||||
def->size = readUint32();
|
||||
if (tfhdFlags.defaultSampleFlagsPresent)
|
||||
def->flags = readUint32();
|
||||
if (tfhdFlags.defaultBaseIsMoof)
|
||||
def->offset += moofOffset;
|
||||
}
|
||||
|
||||
/** Populate [chunks, chunkOffsets, samples, sampleSizes] using Track Fragment Run Table */
|
||||
void Mpeg4Container::readTrun(uint32_t atomSize, uint32_t moofOffset) {
|
||||
skipBytes(1); // skip version
|
||||
TrFlags trunFlags = {};
|
||||
readBytes((uint8_t *)&trunFlags, 3);
|
||||
// audioTrackId is guaranteed to match this trun's track ID
|
||||
auto *def = getSampleDef(audioTrackId);
|
||||
if (!def) {
|
||||
skipBytes(atomSize - 4); // error?
|
||||
return;
|
||||
}
|
||||
uint32_t i, j = 0;
|
||||
uint32_t sampleCnt = readUint32();
|
||||
uint32_t offset = def->offset ? def->offset : moofOffset; // base offset is baseDataOffset or moofOffset
|
||||
// SampleFlags flags = def->flags;
|
||||
if (trunFlags.dataOffsetPresent)
|
||||
offset += readUint32();
|
||||
// if (trunFlags.firstSampleFlagsPresent)
|
||||
// flags = readUint32();
|
||||
|
||||
// treat every trun as a single new chunk
|
||||
i = chunksLen++;
|
||||
chunks = (Mpeg4ChunkRange *)realloc(chunks, chunksLen * sizeof(Mpeg4ChunkRange));
|
||||
chunks[i].count = 1;
|
||||
chunks[i].samples = sampleCnt;
|
||||
chunks[i].sampleDescriptionId = def->sampleDescriptionId;
|
||||
i = chunkOffsetsLen++;
|
||||
chunkOffsets = (Mpeg4ChunkOffset *)realloc(chunkOffsets, chunkOffsetsLen * sizeof(Mpeg4ChunkOffset));
|
||||
chunkOffsets[i] = offset;
|
||||
|
||||
// add all samples' sizes from this trun
|
||||
i = sampleSizesLen;
|
||||
sampleSizesLen += sampleCnt;
|
||||
sampleSizes = (Mpeg4SampleSize *)realloc(sampleSizes, sampleSizesLen * sizeof(Mpeg4SampleSize));
|
||||
// count duration changes for Mpeg4SampleRanges
|
||||
auto *durations = (uint32_t *)malloc(sampleCnt * sizeof(uint32_t));
|
||||
uint32_t prevDuration = 0, durationChanges = 0;
|
||||
|
||||
// TODO optimize memory usage for when all samples are of equal sizes
|
||||
for (; i < sampleSizesLen; i++) {
|
||||
durations[j] = trunFlags.sampleDurationPresent ? readUint32() : def->duration;
|
||||
sampleSizes[i] = trunFlags.sampleSizePresent ? readUint32() : def->size;
|
||||
if (sampleSizes[i] > sampleSizeMax)
|
||||
sampleSizeMax = sampleSizes[i];
|
||||
if (trunFlags.sampleFlagsPresent)
|
||||
skipBytes(4); // skip flags, for now
|
||||
if (trunFlags.sampleCompositionTimeOffsetsPresent)
|
||||
skipBytes(4); // skip sample_composition_time_offset
|
||||
// count duration changes
|
||||
if (durations[j] != prevDuration) {
|
||||
prevDuration = durations[j];
|
||||
durationChanges++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
// add each duration change as a sample range
|
||||
i = samplesLen;
|
||||
samplesLen += durationChanges;
|
||||
samples = (Mpeg4SampleRange *)realloc(samples, samplesLen * sizeof(Mpeg4SampleRange));
|
||||
prevDuration = 0;
|
||||
uint32_t durationCnt = 0; // how many consecutive samples have this duration
|
||||
for (j = 0; j < sampleCnt; j++) {
|
||||
if (durations[j] != prevDuration) {
|
||||
if (prevDuration) {
|
||||
samples[i].count = durationCnt;
|
||||
samples[i].duration = prevDuration;
|
||||
}
|
||||
prevDuration = durations[j];
|
||||
durationCnt = 1;
|
||||
} else {
|
||||
durationCnt++;
|
||||
}
|
||||
}
|
||||
samples[samplesLen - 1].count = durationCnt;
|
||||
samples[samplesLen - 1].duration = prevDuration;
|
||||
// free temp array
|
||||
free(durations);
|
||||
// reallocate sampleData if the max size changes
|
||||
allocSampleData();
|
||||
}
|
||||
215
components/spotify/cspot/bell/src/audio/container/Mpeg4Utils.cpp
Normal file
215
components/spotify/cspot/bell/src/audio/container/Mpeg4Utils.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-10.
|
||||
|
||||
#include "AudioCodecs.h"
|
||||
#include "BellUtils.h"
|
||||
#include "Mpeg4Container.h"
|
||||
#include "Mpeg4Types.h"
|
||||
|
||||
using namespace bell;
|
||||
|
||||
void Mpeg4Container::readAtomHeader(uint32_t &size, uint32_t &type) {
|
||||
size = readUint32() - 8;
|
||||
type = readUint32();
|
||||
}
|
||||
|
||||
void Mpeg4Container::allocSampleData() {
|
||||
if (sampleSizeMax != sampleDataLen) {
|
||||
freeAndNull((void *&)sampleData);
|
||||
sampleData = (uint8_t *)realloc(sampleData, sampleSizeMax);
|
||||
sampleDataLen = sampleSizeMax;
|
||||
}
|
||||
}
|
||||
|
||||
void Mpeg4Container::freeAll() {
|
||||
freeAndNull((void *&)fragments);
|
||||
fragmentsLen = 0;
|
||||
freeAndNull((void *&)sampleDefs);
|
||||
freeAndNull((void *&)sampleDefTracks);
|
||||
sampleDefsLen = 0;
|
||||
for (SampleDescription *desc = sampleDesc; desc < sampleDesc + sampleDefsLen; desc++) {
|
||||
free(desc->data);
|
||||
}
|
||||
freeAndNull((void *&)sampleDesc);
|
||||
sampleDescLen = 0;
|
||||
freeAndNull((void *&)sampleData);
|
||||
freeFragment();
|
||||
}
|
||||
|
||||
void Mpeg4Container::freeFragment() {
|
||||
freeAndNull((void *&)chunks);
|
||||
chunksLen = 0;
|
||||
freeAndNull((void *&)chunkOffsets);
|
||||
chunkOffsetsLen = 0;
|
||||
freeAndNull((void *&)samples);
|
||||
samplesLen = 0;
|
||||
freeAndNull((void *&)sampleSizes);
|
||||
sampleSizesLen = 0;
|
||||
}
|
||||
|
||||
SampleDefaults *Mpeg4Container::getSampleDef(uint32_t trackId) {
|
||||
for (uint32_t i = 0; i < sampleDefsLen; i++) {
|
||||
if (sampleDefTracks[i] == trackId)
|
||||
return sampleDefs + i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Mpeg4Container::isInFragment(Mpeg4Fragment *f, uint32_t offset) {
|
||||
return offset >= f->start && offset < f->end;
|
||||
}
|
||||
|
||||
void Mpeg4Container::setCurrentFragment() {
|
||||
if (!isParsed)
|
||||
return;
|
||||
for (Mpeg4Fragment *f = fragments; f < fragments + fragmentsLen; f++) {
|
||||
if (isInFragment(f, pos)) {
|
||||
curFragment = f;
|
||||
return;
|
||||
}
|
||||
}
|
||||
curFragment = nullptr;
|
||||
}
|
||||
|
||||
void Mpeg4Container::setCurrentSample() {
|
||||
if (!isParsed)
|
||||
return;
|
||||
Mpeg4ChunkRange *chunk = chunks;
|
||||
Mpeg4ChunkOffset *chunkOffset = chunkOffsets;
|
||||
uint32_t chunkCnt = chunk->count;
|
||||
uint32_t chunkSampleCnt = chunk->samples;
|
||||
curChunk.start = 0;
|
||||
curChunk.end = 0;
|
||||
uint32_t offset = *chunkOffset;
|
||||
Mpeg4SampleSize *ss = sampleSizes;
|
||||
while (ss < sampleSizes + sampleSizesLen) {
|
||||
// for (Mpeg4SampleSize *ss = sampleSizes; ss < sampleSizes + sampleSizesLen; ss++) {
|
||||
offset += *ss;
|
||||
if (!curChunk.start && pos < offset) { // sample found
|
||||
curChunk.start = chunkOffset ? *chunkOffset : 0; // set chunk beginning
|
||||
curSampleSize = ss; // store reference to current sample
|
||||
}
|
||||
chunkSampleCnt--; // decrease remaining samples in chunk
|
||||
if (!chunkSampleCnt) { // no more samples
|
||||
chunkOffset++; // get next chunk offset
|
||||
if (chunkOffset >= chunkOffsets + chunkOffsetsLen)
|
||||
chunkOffset = nullptr;
|
||||
if (curChunk.start) { // chunk ended and beginning already found
|
||||
curChunk.end = offset; // set chunk end
|
||||
curChunk.nextStart = chunkOffset ? *chunkOffset : 0; // set next chunk offset
|
||||
break;
|
||||
}
|
||||
if (chunkOffset)
|
||||
offset = *chunkOffset;
|
||||
chunkCnt--; // decrease remaining chunks in range
|
||||
if (!chunkCnt) { // no more chunks
|
||||
chunk++; // get next chunk range
|
||||
if (chunk >= chunks + chunksLen) // something is not ok
|
||||
return; // -> fail
|
||||
chunkCnt = chunk->count; // update new chunk count from range
|
||||
}
|
||||
chunkSampleCnt = chunk->samples; // update new sample count from range
|
||||
}
|
||||
if (sampleSizesLen > 1)
|
||||
ss++;
|
||||
}
|
||||
isInData = pos >= curChunk.start && pos < curChunk.end;
|
||||
}
|
||||
|
||||
Mpeg4Fragment *Mpeg4Container::createFragment() {
|
||||
uint32_t i = fragmentsLen++;
|
||||
fragments = (Mpeg4Fragment *)realloc(fragments, fragmentsLen * sizeof(Mpeg4Fragment));
|
||||
fragments[i].start = pos - 8;
|
||||
uint32_t fragmentEnd = chunkOffsets[chunkOffsetsLen - 1];
|
||||
uint32_t lastRangeSamples = chunks[chunksLen - 1].samples;
|
||||
if (sampleSizesLen == 1)
|
||||
fragmentEnd += *sampleSizes * lastRangeSamples;
|
||||
else {
|
||||
for (uint32_t j = sampleSizesLen - lastRangeSamples; j < sampleSizesLen; j++) {
|
||||
fragmentEnd += sampleSizes[j];
|
||||
}
|
||||
}
|
||||
fragments[i].end = fragmentEnd;
|
||||
fragments[i].duration = 0;
|
||||
totalDurationPresent = false;
|
||||
return fragments + i;
|
||||
}
|
||||
|
||||
AudioCodec Mpeg4Container::getCodec(SampleDescription *desc) {
|
||||
switch (desc->format) {
|
||||
case AudioSampleFormat::OPUS:
|
||||
return AudioCodec::OPUS;
|
||||
case AudioSampleFormat::FLAC:
|
||||
return AudioCodec::FLAC;
|
||||
case AudioSampleFormat::MP4A:
|
||||
switch (desc->mp4aObjectType) {
|
||||
case MP4AObjectType::AAC_LC:
|
||||
return AudioCodec::AAC;
|
||||
case MP4AObjectType::OPUS:
|
||||
return AudioCodec::OPUS;
|
||||
case MP4AObjectType::VORBIS:
|
||||
return AudioCodec::VORBIS;
|
||||
case MP4AObjectType::MPEG1:
|
||||
return AudioCodec::MP3;
|
||||
case MP4AObjectType::MP4A:
|
||||
switch (desc->mp4aProfile) {
|
||||
case MP4AProfile::AAC_LC:
|
||||
return AudioCodec::AAC;
|
||||
case MP4AProfile::LAYER_3:
|
||||
return AudioCodec::MP3;
|
||||
default:
|
||||
return AudioCodec::UNKNOWN;
|
||||
}
|
||||
default:
|
||||
return AudioCodec::UNKNOWN;
|
||||
}
|
||||
default:
|
||||
return AudioCodec::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Mpeg4Container::findSample(int64_t byTime, int32_t byPos, uint64_t startTime) {
|
||||
Mpeg4ChunkRange *chunk = chunks;
|
||||
Mpeg4SampleRange *sample = samples;
|
||||
Mpeg4ChunkOffset *chunkOffset = chunkOffsets;
|
||||
Mpeg4SampleSize *sampleSize = sampleSizes;
|
||||
uint32_t chunkCnt = chunk->count;
|
||||
uint32_t chunkSampleCnt = chunk->samples;
|
||||
uint32_t sampleRangeCnt = sample->count;
|
||||
|
||||
uint64_t timeAbs = startTime;
|
||||
uint32_t offsetAbs = *chunkOffset;
|
||||
while (sampleSize < sampleSizes + sampleSizesLen) {
|
||||
if (byTime >= 0 && byTime <= timeAbs) {
|
||||
return offsetAbs;
|
||||
}
|
||||
if (byPos >= 0 && byPos <= offsetAbs) {
|
||||
return (int64_t)timeAbs;
|
||||
}
|
||||
timeAbs += sample->duration;
|
||||
sampleRangeCnt--;
|
||||
if (!sampleRangeCnt) {
|
||||
sample++;
|
||||
if (sample > samples + samplesLen)
|
||||
return SAMPLE_NOT_FOUND;
|
||||
sampleRangeCnt = sample->count;
|
||||
}
|
||||
chunkSampleCnt--;
|
||||
if (!chunkSampleCnt) {
|
||||
chunkCnt--;
|
||||
chunkOffset++;
|
||||
if (chunkOffset > chunkOffsets + chunkOffsetsLen)
|
||||
return SAMPLE_NOT_FOUND;
|
||||
offsetAbs = *chunkOffset;
|
||||
if (!chunkCnt) {
|
||||
chunk++;
|
||||
if (chunk > chunks + chunksLen)
|
||||
return SAMPLE_NOT_FOUND;
|
||||
chunkCnt = chunk->count;
|
||||
}
|
||||
chunkSampleCnt = chunk->samples;
|
||||
} else {
|
||||
offsetAbs += sampleSizesLen > 1 ? *(sampleSize++) : *sampleSize;
|
||||
}
|
||||
}
|
||||
return SAMPLE_NOT_FOUND;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
|
||||
|
||||
#include "WebmContainer.h"
|
||||
#include "AudioCodecs.h"
|
||||
#include "BellUtils.h"
|
||||
#include "WebmElements.h"
|
||||
|
||||
using namespace bell;
|
||||
|
||||
#define BLOCK_LACING_MASK 0b110
|
||||
|
||||
WebmContainer::~WebmContainer() {
|
||||
freeAndNull((void *&)docType);
|
||||
freeAndNull((void *&)codecId);
|
||||
freeAndNull((void *&)codecPrivate);
|
||||
freeAndNull((void *&)cues);
|
||||
freeAndNull((void *&)sampleData);
|
||||
freeAndNull((void *&)laceSizes);
|
||||
}
|
||||
|
||||
void WebmContainer::feed(const std::shared_ptr<bell::ByteStream> &stream, uint32_t position) {
|
||||
BaseContainer::feed(stream, position);
|
||||
freeAndNull((void *&)laceSizes);
|
||||
laceLeft = 0;
|
||||
readOutFrameSize = 0;
|
||||
}
|
||||
|
||||
bool WebmContainer::parse() {
|
||||
bool segmentFound = false;
|
||||
do {
|
||||
readElem();
|
||||
switch (eid) {
|
||||
case ElementId::EBML:
|
||||
continue;
|
||||
case ElementId::DocType:
|
||||
docType = static_cast<char *>(malloc(esize + 1));
|
||||
docType[esize] = '\0';
|
||||
readBytes((uint8_t *)docType, esize);
|
||||
break;
|
||||
case ElementId::Segment:
|
||||
segmentFound = true;
|
||||
parseSegment(pos); // webm can only have a single segment
|
||||
break;
|
||||
default:
|
||||
skipBytes(esize);
|
||||
}
|
||||
} while (!segmentFound);
|
||||
if (strncmp(codecId, "A_OPUS", 6) == 0)
|
||||
codec = AudioCodec::OPUS;
|
||||
else if (strncmp(codecId, "A_VORBIS", 8) == 0)
|
||||
codec = AudioCodec::VORBIS;
|
||||
else
|
||||
codec = AudioCodec::UNKNOWN;
|
||||
isSeekable = cuesLen;
|
||||
return isParsed && codec != AudioCodec::UNKNOWN;
|
||||
}
|
||||
|
||||
int32_t WebmContainer::getLoadingOffset(uint32_t timeMs) {
|
||||
if (!isSeekable || !cuesLen)
|
||||
return SAMPLE_NOT_SEEKABLE;
|
||||
auto offset = (int32_t)cues[0].offset;
|
||||
auto reqTime = (uint32_t)((float)timeMs * 1000000.0f / timescale);
|
||||
for (CuePoint *cue = cues + 1; cue < cues + cuesLen; cue++) {
|
||||
if (reqTime <= cue->time)
|
||||
return offset;
|
||||
offset = (int32_t)cue->offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool WebmContainer::seekTo(uint32_t timeMs) {
|
||||
auto reqTime = (uint32_t)((float)timeMs * 1000000.0f / timescale);
|
||||
if (reqTime <= currentTime)
|
||||
return false;
|
||||
// seeking ¯\_(ツ)_/¯
|
||||
readOutFrameSize = readCluster(reqTime);
|
||||
return !closed;
|
||||
}
|
||||
|
||||
int32_t WebmContainer::getCurrentTimeMs() {
|
||||
return (int32_t)((float)currentTime * timescale / 1000000.0f);
|
||||
}
|
||||
|
||||
uint8_t *WebmContainer::readSample(uint32_t &len) {
|
||||
if (readOutFrameSize)
|
||||
return readFrame(readOutFrameSize, len);
|
||||
if (laceLeft && laceLeft--)
|
||||
return readFrame(*(laceCurrent++), len);
|
||||
return readFrame(readCluster(), len);
|
||||
}
|
||||
|
||||
uint32_t WebmContainer::readCluster(uint32_t untilTime) {
|
||||
uint32_t end;
|
||||
do {
|
||||
readElem();
|
||||
end = pos + esize;
|
||||
switch (eid) {
|
||||
case ElementId::Cluster:
|
||||
continue;
|
||||
case ElementId::Timestamp:
|
||||
clusterTime = readUint(esize);
|
||||
break;
|
||||
case ElementId::BlockGroup:
|
||||
continue;
|
||||
case ElementId::Block:
|
||||
case ElementId::SimpleBlock:
|
||||
if (readVarNum32() != audioTrackId) {
|
||||
skipTo(end);
|
||||
continue;
|
||||
}
|
||||
currentTime = clusterTime + readUint16();
|
||||
if (!untilTime || currentTime >= untilTime)
|
||||
return readBlock(end);
|
||||
skipTo(end); // skip all unneeded frames
|
||||
break;
|
||||
default:
|
||||
skipBytes(esize);
|
||||
}
|
||||
} while (!closed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t WebmContainer::readBlock(uint32_t end) {
|
||||
uint8_t lacing = readUint8() & BLOCK_LACING_MASK;
|
||||
// https://www.matroska.org/technical/basics.html#simpleblock-structure
|
||||
if (!lacing) // no lacing (0b000)
|
||||
return end - pos;
|
||||
// use lacing
|
||||
laceLeft = readUint8() + 1;
|
||||
freeAndNull((void *&)laceSizes);
|
||||
laceSizes = static_cast<uint32_t *>(malloc(laceLeft * sizeof(uint32_t)));
|
||||
auto *size = laceSizes;
|
||||
|
||||
for (uint8_t i = 0; i < laceLeft; i++) {
|
||||
if (lacing == 0b010) { // Xiph lacing (0b010)
|
||||
uint8_t sizeByte = readUint8();
|
||||
*size = sizeByte;
|
||||
while (sizeByte == 255) {
|
||||
sizeByte = readUint8();
|
||||
*size += sizeByte;
|
||||
}
|
||||
} else if (lacing == 0b110) { // EBML lacing (0b110)
|
||||
*size = readVarNum32();
|
||||
} else { // fixed-size lacing (0b100)
|
||||
*size = (end - pos) / laceLeft;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
laceCurrent = laceSizes + 1;
|
||||
laceLeft--;
|
||||
return laceSizes[0];
|
||||
}
|
||||
|
||||
uint8_t *WebmContainer::readFrame(uint32_t size, uint32_t &outLen) {
|
||||
if (!size)
|
||||
return nullptr;
|
||||
if (size > sampleLen) {
|
||||
free(sampleData);
|
||||
sampleData = static_cast<uint8_t *>(malloc(size));
|
||||
sampleLen = size;
|
||||
}
|
||||
outLen = readBytes(sampleData, size);
|
||||
readOutFrameSize = 0;
|
||||
return sampleData;
|
||||
}
|
||||
|
||||
uint8_t *WebmContainer::getSetupData(uint32_t &len, AudioCodec matchCodec) {
|
||||
if (codec != matchCodec)
|
||||
return nullptr;
|
||||
len = codecPrivateLen;
|
||||
return codecPrivate;
|
||||
}
|
||||
126
components/spotify/cspot/bell/src/audio/container/WebmParser.cpp
Normal file
126
components/spotify/cspot/bell/src/audio/container/WebmParser.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
|
||||
|
||||
#include "WebmContainer.h"
|
||||
#include "WebmElements.h"
|
||||
|
||||
void WebmContainer::parseSegment(uint32_t start) {
|
||||
uint16_t cueIdx = 0;
|
||||
do {
|
||||
readElem();
|
||||
switch (eid) {
|
||||
case ElementId::Info:
|
||||
case ElementId::Tracks:
|
||||
continue;
|
||||
case ElementId::TimestampScale:
|
||||
timescale = (float)readUint(esize);
|
||||
break;
|
||||
case ElementId::Duration:
|
||||
durationMs = (uint32_t)(readFloat(esize) * timescale / 1000000.0f);
|
||||
break;
|
||||
case ElementId::TrackEntry:
|
||||
if (audioTrackId == 255)
|
||||
parseTrack(pos + esize);
|
||||
else // skip other tracks if audio is already found
|
||||
skipBytes(esize);
|
||||
break;
|
||||
case ElementId::Cues:
|
||||
// try to guess the amount of CuePoints from the total size:
|
||||
// - CuePoint = id(1) + size(1) + CueTime + CueTrackPositions
|
||||
// - CueTime = id(1) + size(1) + value(2) # avg. 16 bit
|
||||
// - CueTrackPositions = id(1) + size(1) + CueTrack + CueClusterPosition
|
||||
// - CueTrack = id(1) + size(1) + value(1)
|
||||
// - CueClusterPosition = id(1) + size(1) + value(3) # avg. 24 bit
|
||||
// total: approx. 16 bytes
|
||||
cuesLen += esize / 16;
|
||||
cues = static_cast<CuePoint *>(realloc(cues, cuesLen * sizeof(CuePoint)));
|
||||
continue; // read the next child
|
||||
case ElementId::CuePoint:
|
||||
if (cueIdx >= cuesLen) {
|
||||
cuesLen++;
|
||||
cues = static_cast<CuePoint *>(realloc(cues, cuesLen * sizeof(CuePoint)));
|
||||
}
|
||||
parseCuePoint(cueIdx++, pos + esize, start);
|
||||
break;
|
||||
case ElementId::Cluster:
|
||||
isParsed = audioTrackId != 255;
|
||||
clusterEnd = pos + esize;
|
||||
return;
|
||||
default:
|
||||
skipBytes(esize);
|
||||
}
|
||||
} while (!isParsed);
|
||||
}
|
||||
|
||||
void WebmContainer::parseTrack(uint32_t end) {
|
||||
uint8_t trackId = 255;
|
||||
uint32_t trackRate = 0;
|
||||
uint8_t trackChannels = 0;
|
||||
uint8_t trackBits = 0;
|
||||
char *trackCodecId = nullptr;
|
||||
uint8_t *trackCodecPrivate = nullptr;
|
||||
uint32_t trackCodecPrivateLen = 0;
|
||||
do {
|
||||
readElem();
|
||||
switch (eid) {
|
||||
case ElementId::TrackNumber:
|
||||
trackId = readUint(esize);
|
||||
break;
|
||||
case ElementId::TrackType:
|
||||
if (readUint8() != 0x02) { // allow only audio tracks
|
||||
skipTo(end);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case ElementId::CodecID:
|
||||
trackCodecId = static_cast<char *>(malloc(esize + 1));
|
||||
trackCodecId[esize] = '\0';
|
||||
readBytes((uint8_t *)trackCodecId, esize);
|
||||
break;
|
||||
case ElementId::CodecPrivate:
|
||||
trackCodecPrivate = static_cast<uint8_t *>(malloc(esize));
|
||||
trackCodecPrivateLen = esize;
|
||||
readBytes(trackCodecPrivate, esize);
|
||||
break;
|
||||
case ElementId::Audio:
|
||||
continue;
|
||||
case ElementId::SamplingFrequency:
|
||||
trackRate = (uint32_t)readFloat(esize);
|
||||
break;
|
||||
case ElementId::Channels:
|
||||
trackChannels = readUint(esize);
|
||||
break;
|
||||
case ElementId::BitDepth:
|
||||
trackBits = readUint(esize);
|
||||
break;
|
||||
default:
|
||||
skipBytes(esize);
|
||||
}
|
||||
} while (pos < end);
|
||||
// not-audio tracks do not even get to this point
|
||||
audioTrackId = trackId;
|
||||
sampleRate = trackRate;
|
||||
channelCount = trackChannels;
|
||||
bitDepth = trackBits;
|
||||
codecId = trackCodecId;
|
||||
codecPrivate = trackCodecPrivate;
|
||||
codecPrivateLen = trackCodecPrivateLen;
|
||||
}
|
||||
|
||||
void WebmContainer::parseCuePoint(uint16_t idx, uint32_t end, uint32_t segmentStart) {
|
||||
CuePoint *cue = cues + idx;
|
||||
do {
|
||||
readElem();
|
||||
switch (eid) {
|
||||
case ElementId::CueTime:
|
||||
cue->time = readUint(esize);
|
||||
break;
|
||||
case ElementId::CueTrackPositions:
|
||||
continue;
|
||||
case ElementId::CueClusterPosition:
|
||||
cue->offset = segmentStart + readUint(esize);
|
||||
break;
|
||||
default:
|
||||
skipBytes(esize);
|
||||
}
|
||||
} while (pos < end);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) Kuba Szczodrzyński 2022-1-16.
|
||||
|
||||
#include "WebmContainer.h"
|
||||
|
||||
uint32_t WebmContainer::readVarNum32(bool raw) {
|
||||
uint32_t result = readUint8();
|
||||
if (!result) {
|
||||
closed = true;
|
||||
return 0;
|
||||
}
|
||||
uint8_t len = 0;
|
||||
for (; !(result >> (7 - len)); len++) {}
|
||||
if (!raw)
|
||||
result &= ~(1 << (7 - len));
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
result <<= 8;
|
||||
result |= readUint8();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t WebmContainer::readVarNum64() {
|
||||
uint64_t result = readUint8();
|
||||
if (!result) {
|
||||
closed = true;
|
||||
return 0;
|
||||
}
|
||||
uint8_t len = 0;
|
||||
for (; !(result >> (7 - len)); len++) {}
|
||||
result &= ~(1 << (7 - len));
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
result <<= 8;
|
||||
result |= readUint8();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t WebmContainer::readUint(uint8_t len) {
|
||||
if (len >= 4) {
|
||||
skipBytes(len - 4);
|
||||
return readUint32();
|
||||
}
|
||||
if (len == 3)
|
||||
return readUint24();
|
||||
if (len == 2)
|
||||
return readUint16();
|
||||
return readUint8();
|
||||
}
|
||||
|
||||
uint64_t WebmContainer::readUlong(uint8_t len) {
|
||||
if (len == 8)
|
||||
return readUint64();
|
||||
return readUint(len);
|
||||
}
|
||||
|
||||
float WebmContainer::readFloat(uint8_t len) {
|
||||
double result = 0;
|
||||
auto *b = (uint8_t *)&result;
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
b[len - i - 1] = readUint8();
|
||||
return (float)result;
|
||||
}
|
||||
|
||||
void WebmContainer::readElem() {
|
||||
eid = (ElementId)readVarNum32(true);
|
||||
esize = readVarNum32();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user