From 7e5f27af128aa882d02e7bf30f56dac47a0ceb8d Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 17 Nov 2022 14:06:00 -0800 Subject: [PATCH] update cspot --- components/spotify/CMakeLists.txt | 1 - components/spotify/Shim.cpp | 13 +- components/spotify/Shim.h | 3 +- components/spotify/cspot/CMakeLists.txt | 30 +- components/spotify/cspot/bell/.DS_Store | Bin 6148 -> 0 bytes components/spotify/cspot/bell/.gitignore | 126 +++ components/spotify/cspot/bell/.gitmodules | 3 + components/spotify/cspot/bell/CMakeLists.txt | 251 ++++-- .../spotify/cspot/bell/cJSON/CMakeLists.txt | 33 +- components/spotify/cspot/bell/cJSON/cJSON.c | 23 +- components/spotify/cspot/bell/cJSON/cJSON.h | 7 + .../cspot/bell/cJSON/tests/misc_tests.c | 95 ++- .../cspot/bell/include/BaseHTTPServer.h | 58 +- .../spotify/cspot/bell/include/BellLogger.h | 15 +- .../cspot/bell/include/{Task.h => BellTask.h} | 18 +- .../spotify/cspot/bell/include/BellUtils.h | 3 + .../cspot/bell/include/BufferedStream.h | 127 +++ .../spotify/cspot/bell/include/Crypto.h | 78 +- .../cspot/bell/include/CryptoMbedTLS.h | 78 -- .../cspot/bell/include/CryptoOpenSSL.h | 84 -- .../spotify/cspot/bell/include/HTTPClient.h | 2 +- .../spotify/cspot/bell/include/HTTPServer.h | 17 +- .../spotify/cspot/bell/include/HTTPStream.h | 2 +- .../spotify/cspot/bell/include/TCPSocket.h | 29 +- .../bell/include/{platform => }/TLSSocket.h | 28 +- .../spotify/cspot/bell/include/TimeDefs.h | 8 + .../bell/include/audio/codec/AACDecoder.h | 19 + .../bell/include/audio/codec/ALACDecoder.h | 18 + .../bell/include/audio/codec/AudioCodecs.h | 23 + .../bell/include/audio/codec/BaseCodec.h | 39 + .../{ => audio/codec}/DecoderGlobals.h | 2 +- .../bell/include/audio/codec/MP3Decoder.h | 19 + .../bell/include/audio/codec/OPUSDecoder.h | 19 + .../bell/include/audio/codec/VorbisDecoder.h | 25 + .../include/audio/container/AudioContainers.h | 10 + .../include/audio/container/BaseContainer.h | 104 +++ .../bell/include/audio/container/Mpeg4Atoms.h | 108 +++ .../include/audio/container/Mpeg4Container.h | 114 +++ .../bell/include/audio/container/Mpeg4Types.h | 206 +++++ .../include/audio/container/WebmContainer.h | 81 ++ .../include/audio/container/WebmElements.h | 713 ++++++++++++++++ .../{ => audio}/sinks/esp/AC101AudioSink.h | 0 .../{ => audio}/sinks/esp/BufferedAudioSink.h | 0 .../include/audio/sinks/esp/ES8311AudioSink.h | 27 + .../{ => audio}/sinks/esp/ES8388AudioSink.h | 0 .../{ => audio}/sinks/esp/ES9018AudioSink.h | 0 .../{ => audio}/sinks/esp/InternalAudioSink.h | 0 .../{ => audio}/sinks/esp/PCM5102AudioSink.h | 0 .../{ => audio}/sinks/esp/SPDIFAudioSink.h | 0 .../{ => audio}/sinks/esp/TAS5711AudioSink.h | 0 .../include/{ => audio}/sinks/esp/ac101.h | 0 .../bell/include/{ => audio}/sinks/esp/adac.h | 0 .../bell/include/audio/sinks/esp/es8311.h | 121 +++ .../include/audio/sinks/esp/esxxx_common.h | 166 ++++ .../{ => audio}/sinks/unix/ALSAAudioSink.h | 2 +- .../sinks/unix/NamedPipeAudioSink.h | 0 .../{ => audio}/sinks/unix/PortAudioSink.h | 0 .../cspot/bell/include/platform/MDNSService.h | 18 + .../bell/include/platform/WrappedSemaphore.h | 4 + .../bell/include/platform/win32/win32shim.h | 15 + .../bell/nanopb/.github/workflows/cifuzz.yml | 39 + .../nanopb/.github/workflows/platformio.yaml | 63 ++ .../bell/nanopb/.github/workflows/spm.yml | 15 + .../spotify/cspot/bell/nanopb/AUTHORS.txt | 16 + .../spotify/cspot/bell/nanopb/CHANGELOG.txt | 47 ++ .../spotify/cspot/bell/nanopb/CMakeLists.txt | 10 +- .../examples/cmake_relpath/CMakeLists.txt | 3 - .../examples/cmake_simple/CMakeLists.txt | 3 - components/spotify/cspot/bell/nanopb/pb.h | 29 +- .../spotify/cspot/bell/nanopb/pb_decode.c | 29 +- .../spotify/cspot/bell/nanopb/pb_encode.c | 7 +- .../tests/site_scons/platforms/avr/run_test.c | 2 +- components/spotify/cspot/bell/src/.DS_Store | Bin 6148 -> 0 bytes .../cspot/bell/src/BaseHTTPServer.cpp0 | 88 -- .../spotify/cspot/bell/src/BellLogger.cpp | 4 +- .../spotify/cspot/bell/src/BinaryReader.cpp | 4 +- .../spotify/cspot/bell/src/BufferedStream.cpp | 174 ++++ .../src/{CryptoMBedTLS.cpp => Crypto.cpp} | 4 +- .../spotify/cspot/bell/src/CryptoOpenSSL.cpp | 184 ---- .../spotify/cspot/bell/src/DecoderGlobals.cpp | 8 - .../spotify/cspot/bell/src/HTTPServer.cpp | 146 ++-- .../spotify/cspot/bell/src/HTTPStream.cpp | 8 +- .../bell/src/{platform/esp => }/TLSSocket.cpp | 2 +- .../cspot/bell/src/audio/codec/AACDecoder.cpp | 37 + .../bell/src/audio/codec/ALACDecoder.cpp | 37 + .../bell/src/audio/codec/AudioCodecs.cpp | 71 ++ .../cspot/bell/src/audio/codec/BaseCodec.cpp | 13 + .../bell/src/audio/codec/DecoderGlobals.cpp | 8 + .../cspot/bell/src/audio/codec/MP3Decoder.cpp | 35 + .../bell/src/audio/codec/OPUSDecoder.cpp | 46 + .../bell/src/audio/codec/VorbisDecoder.cpp | 120 +++ .../src/audio/container/AudioContainers.cpp | 18 + .../src/audio/container/BaseContainer.cpp | 78 ++ .../src/audio/container/Mpeg4Container.cpp | 359 ++++++++ .../bell/src/audio/container/Mpeg4Parser.cpp | 171 ++++ .../src/audio/container/Mpeg4ParserFrag.cpp | 162 ++++ .../bell/src/audio/container/Mpeg4Utils.cpp | 215 +++++ .../src/audio/container/WebmContainer.cpp | 172 ++++ .../bell/src/audio/container/WebmParser.cpp | 126 +++ .../bell/src/audio/container/WebmUtils.cpp | 67 ++ .../{ => audio}/sinks/esp/AC101AudioSink.cpp | 0 .../sinks/esp/BufferedAudioSink.cpp | 0 .../src/audio/sinks/esp/ES8311AudioSink.cpp | 105 +++ .../{ => audio}/sinks/esp/ES8388AudioSink.cpp | 5 - .../{ => audio}/sinks/esp/ES9018AudioSink.cpp | 4 - .../sinks/esp/InternalAudioSink.cpp | 2 + .../sinks/esp/PCM5102AudioSink.cpp | 0 .../{ => audio}/sinks/esp/SPDIFAudioSink.cpp | 0 .../sinks/esp/TAS5711AudioSink.cpp | 4 - .../bell/src/{ => audio}/sinks/esp/ac101.c | 0 .../cspot/bell/src/audio/sinks/esp/es8311.c | 798 ++++++++++++++++++ .../{ => audio}/sinks/unix/ALSAAudioSink.cpp | 0 .../sinks/unix/NamedPipeAudioSink.cpp | 0 .../{ => audio}/sinks/unix/PortAudioSink.cpp | 0 .../spotify/cspot/bell/src/platform/.DS_Store | Bin 6148 -> 0 bytes .../bell/src/platform/apple/MDNSService.cpp | 38 + .../bell/src/platform/esp/MDNSService.cpp | 35 + .../bell/src/platform/linux/MDNSService.cpp | 16 + .../bell/src/platform/linux/TLSSocket.cpp | 63 -- .../src/platform/win32/WrappedSemaphore.cpp | 27 + components/spotify/cspot/include/AudioChunk.h | 2 - .../spotify/cspot/include/AudioChunkManager.h | 2 +- .../cspot/include/ChunkedAudioStream.h | 2 + .../spotify/cspot/include/MercuryManager.h | 2 +- .../spotify/cspot/include/PlainConnection.h | 11 +- components/spotify/cspot/include/Player.h | 3 +- .../spotify/cspot/include/ShannonConnection.h | 10 +- components/spotify/cspot/include/Utils.h | 11 +- .../cspot/include/ZeroconfAuthenticator.h | 9 +- components/spotify/cspot/src/ApResolve.cpp | 6 + .../spotify/cspot/src/AudioChunkManager.cpp | 2 +- .../spotify/cspot/src/MercuryManager.cpp | 3 +- .../spotify/cspot/src/PlainConnection.cpp | 33 +- components/spotify/cspot/src/Shannon.cpp | 12 +- .../spotify/cspot/src/SpircController.cpp | 2 +- components/spotify/cspot/src/SpotifyTrack.cpp | 2 + .../cspot/src/ZeroconfAuthenticator.cpp | 68 +- 137 files changed, 6046 insertions(+), 836 deletions(-) delete mode 100644 components/spotify/cspot/bell/.DS_Store create mode 100644 components/spotify/cspot/bell/.gitignore rename components/spotify/cspot/bell/include/{Task.h => BellTask.h} (89%) create mode 100644 components/spotify/cspot/bell/include/BufferedStream.h delete mode 100644 components/spotify/cspot/bell/include/CryptoMbedTLS.h delete mode 100644 components/spotify/cspot/bell/include/CryptoOpenSSL.h rename components/spotify/cspot/bell/include/{platform => }/TLSSocket.h (81%) create mode 100644 components/spotify/cspot/bell/include/TimeDefs.h create mode 100644 components/spotify/cspot/bell/include/audio/codec/AACDecoder.h create mode 100644 components/spotify/cspot/bell/include/audio/codec/ALACDecoder.h create mode 100644 components/spotify/cspot/bell/include/audio/codec/AudioCodecs.h create mode 100644 components/spotify/cspot/bell/include/audio/codec/BaseCodec.h rename components/spotify/cspot/bell/include/{ => audio/codec}/DecoderGlobals.h (93%) create mode 100644 components/spotify/cspot/bell/include/audio/codec/MP3Decoder.h create mode 100644 components/spotify/cspot/bell/include/audio/codec/OPUSDecoder.h create mode 100644 components/spotify/cspot/bell/include/audio/codec/VorbisDecoder.h create mode 100644 components/spotify/cspot/bell/include/audio/container/AudioContainers.h create mode 100644 components/spotify/cspot/bell/include/audio/container/BaseContainer.h create mode 100644 components/spotify/cspot/bell/include/audio/container/Mpeg4Atoms.h create mode 100644 components/spotify/cspot/bell/include/audio/container/Mpeg4Container.h create mode 100644 components/spotify/cspot/bell/include/audio/container/Mpeg4Types.h create mode 100644 components/spotify/cspot/bell/include/audio/container/WebmContainer.h create mode 100644 components/spotify/cspot/bell/include/audio/container/WebmElements.h rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/AC101AudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/BufferedAudioSink.h (100%) create mode 100644 components/spotify/cspot/bell/include/audio/sinks/esp/ES8311AudioSink.h rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/ES8388AudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/ES9018AudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/InternalAudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/PCM5102AudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/SPDIFAudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/TAS5711AudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/ac101.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/esp/adac.h (100%) create mode 100644 components/spotify/cspot/bell/include/audio/sinks/esp/es8311.h create mode 100644 components/spotify/cspot/bell/include/audio/sinks/esp/esxxx_common.h rename components/spotify/cspot/bell/include/{ => audio}/sinks/unix/ALSAAudioSink.h (99%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/unix/NamedPipeAudioSink.h (100%) rename components/spotify/cspot/bell/include/{ => audio}/sinks/unix/PortAudioSink.h (100%) create mode 100644 components/spotify/cspot/bell/include/platform/MDNSService.h create mode 100644 components/spotify/cspot/bell/include/platform/win32/win32shim.h create mode 100644 components/spotify/cspot/bell/nanopb/.github/workflows/cifuzz.yml create mode 100644 components/spotify/cspot/bell/nanopb/.github/workflows/platformio.yaml create mode 100644 components/spotify/cspot/bell/nanopb/.github/workflows/spm.yml delete mode 100644 components/spotify/cspot/bell/src/.DS_Store delete mode 100644 components/spotify/cspot/bell/src/BaseHTTPServer.cpp0 create mode 100644 components/spotify/cspot/bell/src/BufferedStream.cpp rename components/spotify/cspot/bell/src/{CryptoMBedTLS.cpp => Crypto.cpp} (99%) delete mode 100644 components/spotify/cspot/bell/src/CryptoOpenSSL.cpp delete mode 100644 components/spotify/cspot/bell/src/DecoderGlobals.cpp rename components/spotify/cspot/bell/src/{platform/esp => }/TLSSocket.cpp (98%) create mode 100644 components/spotify/cspot/bell/src/audio/codec/AACDecoder.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/ALACDecoder.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/AudioCodecs.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/BaseCodec.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/DecoderGlobals.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/MP3Decoder.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/OPUSDecoder.cpp create mode 100644 components/spotify/cspot/bell/src/audio/codec/VorbisDecoder.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/AudioContainers.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/BaseContainer.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/Mpeg4Container.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/Mpeg4Parser.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/Mpeg4ParserFrag.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/Mpeg4Utils.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/WebmContainer.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/WebmParser.cpp create mode 100644 components/spotify/cspot/bell/src/audio/container/WebmUtils.cpp rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/AC101AudioSink.cpp (100%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/BufferedAudioSink.cpp (100%) create mode 100644 components/spotify/cspot/bell/src/audio/sinks/esp/ES8311AudioSink.cpp rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/ES8388AudioSink.cpp (96%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/ES9018AudioSink.cpp (86%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/InternalAudioSink.cpp (95%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/PCM5102AudioSink.cpp (100%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/SPDIFAudioSink.cpp (100%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/TAS5711AudioSink.cpp (95%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/esp/ac101.c (100%) create mode 100644 components/spotify/cspot/bell/src/audio/sinks/esp/es8311.c rename components/spotify/cspot/bell/src/{ => audio}/sinks/unix/ALSAAudioSink.cpp (100%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/unix/NamedPipeAudioSink.cpp (100%) rename components/spotify/cspot/bell/src/{ => audio}/sinks/unix/PortAudioSink.cpp (100%) delete mode 100644 components/spotify/cspot/bell/src/platform/.DS_Store create mode 100644 components/spotify/cspot/bell/src/platform/apple/MDNSService.cpp create mode 100644 components/spotify/cspot/bell/src/platform/esp/MDNSService.cpp create mode 100644 components/spotify/cspot/bell/src/platform/linux/MDNSService.cpp delete mode 100644 components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp create mode 100644 components/spotify/cspot/bell/src/platform/win32/WrappedSemaphore.cpp diff --git a/components/spotify/CMakeLists.txt b/components/spotify/CMakeLists.txt index d36fd7f5..88437688 100644 --- a/components/spotify/CMakeLists.txt +++ b/components/spotify/CMakeLists.txt @@ -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) diff --git a/components/spotify/Shim.cpp b/components/spotify/Shim.cpp index 99236618..8fd08061 100644 --- a/components/spotify/Shim.cpp +++ b/components/spotify/Shim.cpp @@ -116,6 +116,7 @@ static void cspotTask(void *pvParameters) { spircController = std::make_shared(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(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 request = std::make_unique(); 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), diff --git a/components/spotify/Shim.h b/components/spotify/Shim.h index 4f758b0b..c11104db 100644 --- a/components/spotify/Shim.h +++ b/components/spotify/Shim.h @@ -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 &); }; diff --git a/components/spotify/cspot/CMakeLists.txt b/components/spotify/cspot/CMakeLists.txt index a5fd7a3c..51722246 100644 --- a/components/spotify/cspot/CMakeLists.txt +++ b/components/spotify/cspot/CMakeLists.txt @@ -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}) diff --git a/components/spotify/cspot/bell/.DS_Store b/components/spotify/cspot/bell/.DS_Store deleted file mode 100644 index 1572ed4a22193245a23d7844086b0b786e69b261..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKTZ_{`6h5=`qStxQHzKiC}aH+%sImKtXr~UJX|SM z8!w)uavY{I+={gh%YbFz-(!H+?kY4%Ghcc zNlxSSC?3UmQQ@pOGyBx{@GMP=veWq?c53zAy@u0p_MKz@qn!C=P)>_}kiX+!ucS=F zWjP36C-H35x_T(HGDxyGR|!cJBjo*?B#Y#%FQ-{ls9axHI8CQHYTcO6yAOK@ZMS>B zw`jZbUboYB?;Si?ESk=>TX!BEjZV`^Cch}kNnmSLa@pV#zM^0z{mIX>RAwjeaP%HN zNRCf5AxZfLAs~85>JTZh$hu<}Ep213^PA6d=AHwu3%nL=R-qF6R!bx}< zt+fnT2G$wi8#<)L=l}5c?|&6!jb*?x@P9HO>H~k!$K1@>x;0BaYhCCEC=16`8dVC6 iIgV9valuestring; } -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; } diff --git a/components/spotify/cspot/bell/cJSON/cJSON.h b/components/spotify/cspot/bell/cJSON/cJSON.h index 92907a2c..95a9cf69 100644 --- a/components/spotify/cspot/bell/cJSON/cJSON.h +++ b/components/spotify/cspot/bell/cJSON/cJSON.h @@ -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) diff --git a/components/spotify/cspot/bell/cJSON/tests/misc_tests.c b/components/spotify/cspot/bell/cJSON/tests/misc_tests.c index 3bf0a1cc..19b7c853 100644 --- a/components/spotify/cspot/bell/cJSON/tests/misc_tests.c +++ b/components/spotify/cspot/bell/cJSON/tests/misc_tests.c @@ -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(); } diff --git a/components/spotify/cspot/bell/include/BaseHTTPServer.h b/components/spotify/cspot/bell/include/BaseHTTPServer.h index 8881ef80..ec534173 100644 --- a/components/spotify/cspot/bell/include/BaseHTTPServer.h +++ b/components/spotify/cspot/bell/include/BaseHTTPServer.h @@ -6,7 +6,14 @@ #include #include #include +#include #include +#include +#ifdef _WIN32 +#include +#else +#include +#endif namespace bell { @@ -20,6 +27,40 @@ class ResponseReader { virtual void close() = 0; }; +class RequestBodyReader : public ResponseReader { + public: + std::vector partialBuffer; + int fd = 0; + size_t contentLength = 0; + size_t sizeRead = 0; + + RequestBodyReader(size_t contentLength, int fd, std::vector &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 urlParams; std::map queryParams; + std::unique_ptr responseReader = std::unique_ptr(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; }; -typedef std::function httpHandler; +typedef std::function)> httpHandler; + struct HTTPRoute { RequestType requestType; httpHandler handler; + bool readBodyToStr; }; struct HTTPConnection { + int fd = 0; std::vector buffer; - std::string currentLine = ""; + std::vector partialBuffer = std::vector(); 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 diff --git a/components/spotify/cspot/bell/include/BellLogger.h b/components/spotify/cspot/bell/include/BellLogger.h index 78bb3c0d..a71784b3 100644 --- a/components/spotify/cspot/bell/include/BellLogger.h +++ b/components/spotify/cspot/bell/include/BellLogger.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -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 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}; }; diff --git a/components/spotify/cspot/bell/include/Task.h b/components/spotify/cspot/bell/include/BellTask.h similarity index 89% rename from components/spotify/cspot/bell/include/Task.h rename to components/spotify/cspot/bell/include/BellTask.h index 425b2072..22324ca2 100644 --- a/components/spotify/cspot/bell/include/Task.h +++ b/components/spotify/cspot/bell/include/BellTask.h @@ -7,9 +7,12 @@ #include #include #include +#elif _WIN32 +#include +#else +#include #endif -#include #include 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; } }; diff --git a/components/spotify/cspot/bell/include/BellUtils.h b/components/spotify/cspot/bell/include/BellUtils.h index 175af44e..d116f19f 100644 --- a/components/spotify/cspot/bell/include/BellUtils.h +++ b/components/spotify/cspot/bell/include/BellUtils.h @@ -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 diff --git a/components/spotify/cspot/bell/include/BufferedStream.h b/components/spotify/cspot/bell/include/BufferedStream.h new file mode 100644 index 00000000..3c8232a7 --- /dev/null +++ b/components/spotify/cspot/bell/include/BufferedStream.h @@ -0,0 +1,127 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-7. + +#pragma once + +#include "ByteStream.h" +#include "BellTask.h" +#include "WrappedSemaphore.h" +#include +#include +#include +#include + +/** + * 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 StreamPtr; + typedef std::function 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 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); +}; diff --git a/components/spotify/cspot/bell/include/Crypto.h b/components/spotify/cspot/bell/include/Crypto.h index b6188bb9..96c98dcd 100644 --- a/components/spotify/cspot/bell/include/Crypto.h +++ b/components/spotify/cspot/bell/include/Crypto.h @@ -1,14 +1,78 @@ #ifndef BELL_CRYPTO_H #define BELL_CRYPTO_H +#define Crypto CryptoMbedTLS + #include #include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#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 base64Decode(const std::string& data); + std::string base64Encode(const std::vector& data); + + // Sha1 + void sha1Init(); + void sha1Update(const std::string& s); + void sha1Update(const std::vector& vec); + std::string sha1Final(); + std::vector sha1FinalBytes(); + + // HMAC SHA1 + std::vector sha1HMAC(const std::vector& inputKey, const std::vector& message); + + // AES CTR + void aesCTRXcrypt(const std::vector& key, std::vector& iv, uint8_t* data, size_t nbytes); + + // AES ECB + void aesECBdecrypt(const std::vector& key, std::vector& data); + + // Diffie Hellman + std::vector publicKey; + std::vector privateKey; + void dhInit(); + std::vector dhCalculateShared(const std::vector& remoteKey); + + // PBKDF2 + std::vector pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize); + + // Random stuff + std::vector generateVectorWithRandomData(size_t length); +}; -#ifdef BELL_USE_MBEDTLS -#include "CryptoMbedTLS.h" -#define Crypto CryptoMbedTLS -#else -#include "CryptoOpenSSL.h" -#define Crypto CryptoOpenSSL -#endif #endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/CryptoMbedTLS.h b/components/spotify/cspot/bell/include/CryptoMbedTLS.h deleted file mode 100644 index 27a4ff42..00000000 --- a/components/spotify/cspot/bell/include/CryptoMbedTLS.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef BELL_CRYPTOMBEDTLS_H -#define BELL_CRYPTOMBEDTLS_H - -#ifdef BELL_USE_MBEDTLS -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -#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 base64Decode(const std::string& data); - std::string base64Encode(const std::vector& data); - - // Sha1 - void sha1Init(); - void sha1Update(const std::string& s); - void sha1Update(const std::vector& vec); - std::string sha1Final(); - std::vector sha1FinalBytes(); - - // HMAC SHA1 - std::vector sha1HMAC(const std::vector& inputKey, const std::vector& message); - - // AES CTR - void aesCTRXcrypt(const std::vector& key, std::vector& iv, uint8_t* data, size_t nbytes); - - // AES ECB - void aesECBdecrypt(const std::vector& key, std::vector& data); - - // Diffie Hellman - std::vector publicKey; - std::vector privateKey; - void dhInit(); - std::vector dhCalculateShared(const std::vector& remoteKey); - - // PBKDF2 - std::vector pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize); - - // Random stuff - std::vector generateVectorWithRandomData(size_t length); -}; - -#endif -#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/CryptoOpenSSL.h b/components/spotify/cspot/bell/include/CryptoOpenSSL.h deleted file mode 100644 index 56310fd3..00000000 --- a/components/spotify/cspot/bell/include/CryptoOpenSSL.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef BELL_CRYPTOOPENSSL_H -#define BELL_CRYPTOOPENSSL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 base64Decode(const std::string& data); - std::string base64Encode(const std::vector& data); - - // Sha1 - void sha1Init(); - void sha1Update(const std::string& s); - void sha1Update(const std::vector& 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 sha1FinalBytes(); - - // HMAC SHA1 - std::vector sha1HMAC(const std::vector& inputKey, const std::vector& message); - - // AES CTR - void aesCTRXcrypt(const std::vector& key, std::vector& iv, uint8_t* buffer, size_t nbytes); - - // AES ECB - void aesECBdecrypt(const std::vector& key, std::vector& data); - - // Diffie Hellman - std::vector publicKey; - std::vector privateKey; - void dhInit(); - std::vector dhCalculateShared(const std::vector& remoteKey); - - // PBKDF2 - std::vector pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize); - - // Random stuff - std::vector generateVectorWithRandomData(size_t length); -}; - -#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/HTTPClient.h b/components/spotify/cspot/bell/include/HTTPClient.h index 5c7709d4..fdab12e4 100644 --- a/components/spotify/cspot/bell/include/HTTPClient.h +++ b/components/spotify/cspot/bell/include/HTTPClient.h @@ -4,7 +4,7 @@ #include "BellSocket.h" #include "ByteStream.h" #include "TCPSocket.h" -#include "platform/TLSSocket.h" +#include "TLSSocket.h" #include #include #include diff --git a/components/spotify/cspot/bell/include/HTTPServer.h b/components/spotify/cspot/bell/include/HTTPServer.h index 3394df75..33a5cc43 100644 --- a/components/spotify/cspot/bell/include/HTTPServer.h +++ b/components/spotify/cspot/bell/include/HTTPServer.h @@ -11,16 +11,21 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else +#include +#include +#include #include +#endif #include #include -#include #include -#include #include -#include #include -#include #include #include #include "BaseHTTPServer.h" @@ -46,7 +51,7 @@ namespace bell std::map connections; void writeResponse(const HTTPResponse &); void writeResponseEvents(int connFd); - void findAndHandleRoute(std::string &, std::string &, int connectionFd); + void findAndHandleRoute(HTTPConnection& connection); std::vector 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); diff --git a/components/spotify/cspot/bell/include/HTTPStream.h b/components/spotify/cspot/bell/include/HTTPStream.h index cb252f92..b9c8ff4e 100644 --- a/components/spotify/cspot/bell/include/HTTPStream.h +++ b/components/spotify/cspot/bell/include/HTTPStream.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include /* * HTTPStream diff --git a/components/spotify/cspot/bell/include/TCPSocket.h b/components/spotify/cspot/bell/include/TCPSocket.h index 9f26a693..5d993936 100644 --- a/components/spotify/cspot/bell/include/TCPSocket.h +++ b/components/spotify/cspot/bell/include/TCPSocket.h @@ -9,15 +9,21 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else #include #include #include #include +#include +#include +#endif #include #include -#include #include -#include 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) { diff --git a/components/spotify/cspot/bell/include/platform/TLSSocket.h b/components/spotify/cspot/bell/include/TLSSocket.h similarity index 81% rename from components/spotify/cspot/bell/include/platform/TLSSocket.h rename to components/spotify/cspot/bell/include/TLSSocket.h index 020fcd2d..3d91cf95 100644 --- a/components/spotify/cspot/bell/include/platform/TLSSocket.h +++ b/components/spotify/cspot/bell/include/TLSSocket.h @@ -8,50 +8,36 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#else #include #include #include +#include +#include +#endif #include #include #include -#include #include -#include #include -#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 -#include -#include -#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: diff --git a/components/spotify/cspot/bell/include/TimeDefs.h b/components/spotify/cspot/bell/include/TimeDefs.h new file mode 100644 index 00000000..21d602ab --- /dev/null +++ b/components/spotify/cspot/bell/include/TimeDefs.h @@ -0,0 +1,8 @@ +// +// Created by Filip Grzywok on 28/02/2022. +// + +#ifndef EUPHONIUMCLI_TIMEDEFS_H +#define EUPHONIUMCLI_TIMEDEFS_H + +#endif // EUPHONIUMCLI_TIMEDEFS_H diff --git a/components/spotify/cspot/bell/include/audio/codec/AACDecoder.h b/components/spotify/cspot/bell/include/audio/codec/AACDecoder.h new file mode 100644 index 00000000..98e835c9 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/AACDecoder.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; +}; diff --git a/components/spotify/cspot/bell/include/audio/codec/ALACDecoder.h b/components/spotify/cspot/bell/include/audio/codec/ALACDecoder.h new file mode 100644 index 00000000..4d3dd13c --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/ALACDecoder.h @@ -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; +}; diff --git a/components/spotify/cspot/bell/include/audio/codec/AudioCodecs.h b/components/spotify/cspot/bell/include/audio/codec/AudioCodecs.h new file mode 100644 index 00000000..c50e2929 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/AudioCodecs.h @@ -0,0 +1,23 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-12. + +#pragma once + +#include "BaseCodec.h" +#include "BaseContainer.h" +#include + +enum class AudioCodec { + UNKNOWN = 0, + AAC = 1, + MP3 = 2, + VORBIS = 3, + OPUS = 4, + FLAC = 5, +}; + +class AudioCodecs { + public: + static std::shared_ptr getCodec(AudioCodec type); + static std::shared_ptr getCodec(BaseContainer *container); + static void addCodec(AudioCodec type, const std::shared_ptr &codec); +}; diff --git a/components/spotify/cspot/bell/include/audio/codec/BaseCodec.h b/components/spotify/cspot/bell/include/audio/codec/BaseCodec.h new file mode 100644 index 00000000..0f878fc4 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/BaseCodec.h @@ -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; +}; diff --git a/components/spotify/cspot/bell/include/DecoderGlobals.h b/components/spotify/cspot/bell/include/audio/codec/DecoderGlobals.h similarity index 93% rename from components/spotify/cspot/bell/include/DecoderGlobals.h rename to components/spotify/cspot/bell/include/audio/codec/DecoderGlobals.h index 47bcf0bb..5a03a3a5 100644 --- a/components/spotify/cspot/bell/include/DecoderGlobals.h +++ b/components/spotify/cspot/bell/include/audio/codec/DecoderGlobals.h @@ -43,7 +43,7 @@ namespace bell } }; - extern std::shared_ptr decodersInstance; + extern bell::DecodersInstance* decodersInstance; void createDecoders(); } diff --git a/components/spotify/cspot/bell/include/audio/codec/MP3Decoder.h b/components/spotify/cspot/bell/include/audio/codec/MP3Decoder.h new file mode 100644 index 00000000..476c9fbe --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/MP3Decoder.h @@ -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; +}; diff --git a/components/spotify/cspot/bell/include/audio/codec/OPUSDecoder.h b/components/spotify/cspot/bell/include/audio/codec/OPUSDecoder.h new file mode 100644 index 00000000..2efd7c22 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/OPUSDecoder.h @@ -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; +}; diff --git a/components/spotify/cspot/bell/include/audio/codec/VorbisDecoder.h b/components/spotify/cspot/bell/include/audio/codec/VorbisDecoder.h new file mode 100644 index 00000000..0ad1dd99 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/codec/VorbisDecoder.h @@ -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; +}; diff --git a/components/spotify/cspot/bell/include/audio/container/AudioContainers.h b/components/spotify/cspot/bell/include/audio/container/AudioContainers.h new file mode 100644 index 00000000..b3942e45 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/AudioContainers.h @@ -0,0 +1,10 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-15. + +#pragma once + +#include "BaseContainer.h" + +class AudioContainers { + public: + static std::unique_ptr create(const char *mimeType); +}; diff --git a/components/spotify/cspot/bell/include/audio/container/BaseContainer.h b/components/spotify/cspot/bell/include/audio/container/BaseContainer.h new file mode 100644 index 00000000..72d12f98 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/BaseContainer.h @@ -0,0 +1,104 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-7. + +#pragma once + +#include "BinaryReader.h" +#include "ByteStream.h" +#include +#include + +/** + * 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 &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 reader; + std::shared_ptr 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); +}; diff --git a/components/spotify/cspot/bell/include/audio/container/Mpeg4Atoms.h b/components/spotify/cspot/bell/include/audio/container/Mpeg4Atoms.h new file mode 100644 index 00000000..ab228f23 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/Mpeg4Atoms.h @@ -0,0 +1,108 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-8. + +#pragma once + +#include + +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; diff --git a/components/spotify/cspot/bell/include/audio/container/Mpeg4Container.h b/components/spotify/cspot/bell/include/audio/container/Mpeg4Container.h new file mode 100644 index 00000000..cb21889a --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/Mpeg4Container.h @@ -0,0 +1,114 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-8. + +#pragma once + +#include "BaseContainer.h" +#include "Mpeg4Atoms.h" +#include + +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 &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(); +}; diff --git a/components/spotify/cspot/bell/include/audio/container/Mpeg4Types.h b/components/spotify/cspot/bell/include/audio/container/Mpeg4Types.h new file mode 100644 index 00000000..a2faea10 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/Mpeg4Types.h @@ -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 +}; diff --git a/components/spotify/cspot/bell/include/audio/container/WebmContainer.h b/components/spotify/cspot/bell/include/audio/container/WebmContainer.h new file mode 100644 index 00000000..d0fd624c --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/WebmContainer.h @@ -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 &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); +}; diff --git a/components/spotify/cspot/bell/include/audio/container/WebmElements.h b/components/spotify/cspot/bell/include/audio/container/WebmElements.h new file mode 100644 index 00000000..c48f4239 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/container/WebmElements.h @@ -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, +}; diff --git a/components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/AC101AudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/AC101AudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/AC101AudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/BufferedAudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/BufferedAudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/BufferedAudioSink.h diff --git a/components/spotify/cspot/bell/include/audio/sinks/esp/ES8311AudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/ES8311AudioSink.h new file mode 100644 index 00000000..348e81df --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/sinks/esp/ES8311AudioSink.h @@ -0,0 +1,27 @@ +#ifndef ES8311AUDIOSINK_H +#define ES8311AUDIOSINK_H + +#include "driver/i2s.h" +#include +#include +#include "BufferedAudioSink.h" +#include +#include +#include "driver/gpio.h" +#include "driver/i2c.h" +#include +#include +#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 \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/ES8388AudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/ES8388AudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/ES8388AudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/ES9018AudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/ES9018AudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/ES9018AudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/InternalAudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/InternalAudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/InternalAudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/PCM5102AudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/PCM5102AudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/PCM5102AudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/SPDIFAudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/SPDIFAudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/SPDIFAudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/esp/TAS5711AudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/TAS5711AudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/TAS5711AudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/ac101.h b/components/spotify/cspot/bell/include/audio/sinks/esp/ac101.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/ac101.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/ac101.h diff --git a/components/spotify/cspot/bell/include/sinks/esp/adac.h b/components/spotify/cspot/bell/include/audio/sinks/esp/adac.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/esp/adac.h rename to components/spotify/cspot/bell/include/audio/sinks/esp/adac.h diff --git a/components/spotify/cspot/bell/include/audio/sinks/esp/es8311.h b/components/spotify/cspot/bell/include/audio/sinks/esp/es8311.h new file mode 100644 index 00000000..8f4c79d0 --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/sinks/esp/es8311.h @@ -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 \ No newline at end of file diff --git a/components/spotify/cspot/bell/include/audio/sinks/esp/esxxx_common.h b/components/spotify/cspot/bell/include/audio/sinks/esp/esxxx_common.h new file mode 100644 index 00000000..bf8b35aa --- /dev/null +++ b/components/spotify/cspot/bell/include/audio/sinks/esp/esxxx_common.h @@ -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__ diff --git a/components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/unix/ALSAAudioSink.h similarity index 99% rename from components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/unix/ALSAAudioSink.h index 03f42985..1b1bd548 100644 --- a/components/spotify/cspot/bell/include/sinks/unix/ALSAAudioSink.h +++ b/components/spotify/cspot/bell/include/audio/sinks/unix/ALSAAudioSink.h @@ -5,7 +5,7 @@ #include "AudioSink.h" #include #include -#include +#include #include #include #include diff --git a/components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/unix/NamedPipeAudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/unix/NamedPipeAudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/unix/NamedPipeAudioSink.h diff --git a/components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h b/components/spotify/cspot/bell/include/audio/sinks/unix/PortAudioSink.h similarity index 100% rename from components/spotify/cspot/bell/include/sinks/unix/PortAudioSink.h rename to components/spotify/cspot/bell/include/audio/sinks/unix/PortAudioSink.h diff --git a/components/spotify/cspot/bell/include/platform/MDNSService.h b/components/spotify/cspot/bell/include/platform/MDNSService.h new file mode 100644 index 00000000..0965f45e --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/MDNSService.h @@ -0,0 +1,18 @@ +#ifndef BELLL_MDNS_SERVICE_H +#define BELLL_MDNS_SERVICE_H +#include +#include + +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 txtData + ); +}; + +#endif diff --git a/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h b/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h index ef9106a6..5eb07777 100644 --- a/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h +++ b/components/spotify/cspot/bell/include/platform/WrappedSemaphore.h @@ -6,6 +6,8 @@ #include "freertos/semphr.h" #elif __APPLE__ #include +#elif _WIN32 +#include #else #include #include @@ -18,6 +20,8 @@ private: xSemaphoreHandle semaphoreHandle; #elif __APPLE__ dispatch_semaphore_t semaphoreHandle; +#elif _WIN32 + HANDLE semaphoreHandle; #else sem_t semaphoreHandle; #endif diff --git a/components/spotify/cspot/bell/include/platform/win32/win32shim.h b/components/spotify/cspot/bell/include/platform/win32/win32shim.h new file mode 100644 index 00000000..908a382a --- /dev/null +++ b/components/spotify/cspot/bell/include/platform/win32/win32shim.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#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); } diff --git a/components/spotify/cspot/bell/nanopb/.github/workflows/cifuzz.yml b/components/spotify/cspot/bell/nanopb/.github/workflows/cifuzz.yml new file mode 100644 index 00000000..e67e68fc --- /dev/null +++ b/components/spotify/cspot/bell/nanopb/.github/workflows/cifuzz.yml @@ -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 diff --git a/components/spotify/cspot/bell/nanopb/.github/workflows/platformio.yaml b/components/spotify/cspot/bell/nanopb/.github/workflows/platformio.yaml new file mode 100644 index 00000000..6390fbf9 --- /dev/null +++ b/components/spotify/cspot/bell/nanopb/.github/workflows/platformio.yaml @@ -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 diff --git a/components/spotify/cspot/bell/nanopb/.github/workflows/spm.yml b/components/spotify/cspot/bell/nanopb/.github/workflows/spm.yml new file mode 100644 index 00000000..999851a0 --- /dev/null +++ b/components/spotify/cspot/bell/nanopb/.github/workflows/spm.yml @@ -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 diff --git a/components/spotify/cspot/bell/nanopb/AUTHORS.txt b/components/spotify/cspot/bell/nanopb/AUTHORS.txt index 465f1071..d966e4c1 100644 --- a/components/spotify/cspot/bell/nanopb/AUTHORS.txt +++ b/components/spotify/cspot/bell/nanopb/AUTHORS.txt @@ -98,3 +98,19 @@ leabut Angel ILIEV Jakub Tymejczyk Matthew Simmons +Anthony Pesch +Avik De +ConradWood +David Sabatie +Sebastian Stockhammer +Gil Shapira +Ian Frosst +Ingo Kresse +Ivan Zrno +Jonathan Seilkopf +Karl Ljungkvist +Mathis Logemann +Oleg Dolgy <60554929+odolgy@users.noreply.github.com> +Pavel Sokolov +Slavey Karadzhov +Tobias Nießen diff --git a/components/spotify/cspot/bell/nanopb/CHANGELOG.txt b/components/spotify/cspot/bell/nanopb/CHANGELOG.txt index f2c06503..51182f68 100644 --- a/components/spotify/cspot/bell/nanopb/CHANGELOG.txt +++ b/components/spotify/cspot/bell/nanopb/CHANGELOG.txt @@ -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) diff --git a/components/spotify/cspot/bell/nanopb/CMakeLists.txt b/components/spotify/cspot/bell/nanopb/CMakeLists.txt index cb90012b..bf42cd0c 100644 --- a/components/spotify/cspot/bell/nanopb/CMakeLists.txt +++ b/components/spotify/cspot/bell/nanopb/CMakeLists.txt @@ -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 $ - $ + $ ) endif() diff --git a/components/spotify/cspot/bell/nanopb/examples/cmake_relpath/CMakeLists.txt b/components/spotify/cspot/bell/nanopb/examples/cmake_relpath/CMakeLists.txt index e7727d85..a8abeb4f 100644 --- a/components/spotify/cspot/bell/nanopb/examples/cmake_relpath/CMakeLists.txt +++ b/components/spotify/cspot/bell/nanopb/examples/cmake_relpath/CMakeLists.txt @@ -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") diff --git a/components/spotify/cspot/bell/nanopb/examples/cmake_simple/CMakeLists.txt b/components/spotify/cspot/bell/nanopb/examples/cmake_simple/CMakeLists.txt index e5f33a02..d9b96be0 100644 --- a/components/spotify/cspot/bell/nanopb/examples/cmake_simple/CMakeLists.txt +++ b/components/spotify/cspot/bell/nanopb/examples/cmake_simple/CMakeLists.txt @@ -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") diff --git a/components/spotify/cspot/bell/nanopb/pb.h b/components/spotify/cspot/bell/nanopb/pb.h index cc32546c..2eee7dc7 100644 --- a/components/spotify/cspot/bell/nanopb/pb.h +++ b/components/spotify/cspot/bell/nanopb/pb.h @@ -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 struct MessageDescriptor; } // namespace nanopb +} #endif /* __cplusplus */ #endif diff --git a/components/spotify/cspot/bell/nanopb/pb_decode.c b/components/spotify/cspot/bell/nanopb/pb_decode.c index f388932f..5405c87d 100644 --- a/components/spotify/cspot/bell/nanopb/pb_decode.c +++ b/components/spotify/cspot/bell/nanopb/pb_decode.c @@ -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)) diff --git a/components/spotify/cspot/bell/nanopb/pb_encode.c b/components/spotify/cspot/bell/nanopb/pb_encode.c index 6fcbfa6e..7f562012 100644 --- a/components/spotify/cspot/bell/nanopb/pb_encode.c +++ b/components/spotify/cspot/bell/nanopb/pb_encode.c @@ -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; diff --git a/components/spotify/cspot/bell/nanopb/tests/site_scons/platforms/avr/run_test.c b/components/spotify/cspot/bell/nanopb/tests/site_scons/platforms/avr/run_test.c index 8e4679e5..eca0d328 100644 --- a/components/spotify/cspot/bell/nanopb/tests/site_scons/platforms/avr/run_test.c +++ b/components/spotify/cspot/bell/nanopb/tests/site_scons/platforms/avr/run_test.c @@ -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); diff --git a/components/spotify/cspot/bell/src/.DS_Store b/components/spotify/cspot/bell/src/.DS_Store deleted file mode 100644 index 3a1ed28f5368706ed3aa7df4213d2f40716c58d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}N6?5T3NvrWBzE1-%8l7Obs`#miFb3%H^OmAXr-F0Px>ZtbBI_O37Fi}*aw zB&k@c;7P>Jz~r0E&xU-vWHtaGTBGhRKm`CKDq*gK!xutv(gn#m51}y6xCIRZcnrtG z{z5i8{vreP?j-afh5%Cd^!~zFhZuVe`*Ac(Yqd9#%NJHwi?S%o^4h=GV?Pbj(XbT^ zE@^hAbsWy@AUuzH<4$G$SSM)^CB1~GQ2#&q{{BB-#4~1q8TeNWh(g - -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 bell::BaseHTTPServer::splitUrl(const std::string &url, char delimiter) -{ - std::stringstream ssb(url); - std::string segment; - std::vector seglist; - - while (std::getline(ssb, segment, delimiter)) - { - seglist.push_back(segment); - } - return seglist; -} - -std::map bell::BaseHTTPServer::parseQueryString(const std::string &queryString) -{ - std::map 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; -} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/BellLogger.cpp b/components/spotify/cspot/bell/src/BellLogger.cpp index 1023d575..975b2975 100644 --- a/components/spotify/cspot/bell/src/BellLogger.cpp +++ b/components/spotify/cspot/bell/src/BellLogger.cpp @@ -1,9 +1,9 @@ #include "BellLogger.h" -std::shared_ptr bell::bellGlobalLogger; +bell::AbstractLogger* bell::bellGlobalLogger; void bell::setDefaultLogger() { - bell::bellGlobalLogger = std::make_shared(); + bell::bellGlobalLogger = new bell::BellLogger(); } void bell::enableSubmoduleLogging() { diff --git a/components/spotify/cspot/bell/src/BinaryReader.cpp b/components/spotify/cspot/bell/src/BinaryReader.cpp index 95db59de..f7ef1f29 100644 --- a/components/spotify/cspot/bell/src/BinaryReader.cpp +++ b/components/spotify/cspot/bell/src/BinaryReader.cpp @@ -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 b(pos); + stream->read(&b[0], pos); } int32_t bell::BinaryReader::readInt() { diff --git a/components/spotify/cspot/bell/src/BufferedStream.cpp b/components/spotify/cspot/bell/src/BufferedStream.cpp new file mode 100644 index 00000000..61a22416 --- /dev/null +++ b/components/spotify/cspot/bell/src/BufferedStream.cpp @@ -0,0 +1,174 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-7. + +#include "BufferedStream.h" +#include + +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(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 &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(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; +} diff --git a/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp b/components/spotify/cspot/bell/src/Crypto.cpp similarity index 99% rename from components/spotify/cspot/bell/src/CryptoMBedTLS.cpp rename to components/spotify/cspot/bell/src/Crypto.cpp index fc596762..8fc830c6 100644 --- a/components/spotify/cspot/bell/src/CryptoMBedTLS.cpp +++ b/components/spotify/cspot/bell/src/Crypto.cpp @@ -1,5 +1,4 @@ -#ifdef BELL_USE_MBEDTLS -#include "CryptoMbedTLS.h" +#include "Crypto.h" CryptoMbedTLS::CryptoMbedTLS() { @@ -224,4 +223,3 @@ std::vector CryptoMbedTLS::generateVectorWithRandomData(size_t length) return randomVector; } -#endif \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp b/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp deleted file mode 100644 index 662e2d84..00000000 --- a/components/spotify/cspot/bell/src/CryptoOpenSSL.cpp +++ /dev/null @@ -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(DH_KEY_SIZE); - this->privateKey = generateVectorWithRandomData(DH_KEY_SIZE); -} - -CryptoOpenSSL::~CryptoOpenSSL() -{ - if (this->dhContext != nullptr) - { - DH_free(this->dhContext); - } -} - -std::vector CryptoOpenSSL::base64Decode(const std::string& data) -{ - // base64 in openssl is an absolute mess lmao - std::unique_ptr 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 decoded(maxlen); - const int len = BIO_read(b64.get(), decoded.data(), maxlen); - decoded.resize(len); - return decoded; -} - -std::string CryptoOpenSSL::base64Encode(const std::vector& data) -{ - // base64 in openssl is an absolute mess lmao x 2 - std::unique_ptr 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(s.begin(), s.end())); -} -void CryptoOpenSSL::sha1Update(const std::vector& vec) -{ - SHA1_Update(&sha1Context, vec.data(), vec.size()); -} - -std::vector CryptoOpenSSL::sha1FinalBytes() -{ - std::vector 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 CryptoOpenSSL::sha1HMAC(const std::vector& inputKey, const std::vector& message) -{ - std::vector 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& key, std::vector& 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& key, std::vector& 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 CryptoOpenSSL::pbkdf2HmacSha1(const std::vector& password, const std::vector& salt, int iterations, int digestSize) -{ - std::vector 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 CryptoOpenSSL::dhCalculateShared(const std::vector& remoteKey) -{ - auto sharedKey = std::vector(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 CryptoOpenSSL::generateVectorWithRandomData(size_t length) -{ - std::vector randomVec(length); - if(RAND_bytes(randomVec.data(), length) == 0) - { - } - return randomVec; -} diff --git a/components/spotify/cspot/bell/src/DecoderGlobals.cpp b/components/spotify/cspot/bell/src/DecoderGlobals.cpp deleted file mode 100644 index 5ecc03fa..00000000 --- a/components/spotify/cspot/bell/src/DecoderGlobals.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "DecoderGlobals.h" - -std::shared_ptr bell::decodersInstance; - -void bell::createDecoders() -{ - bell::decodersInstance = std::make_shared(); -} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/HTTPServer.cpp b/components/spotify/cspot/bell/src/HTTPServer.cpp index 6a5389a4..bff3a4eb 100644 --- a/components/spotify/cspot/bell/src/HTTPServer.cpp +++ b/components/spotify/cspot/bell/src/HTTPServer.cpp @@ -55,14 +55,14 @@ std::vector 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()}); } - 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(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 pathParams; std::map 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( + 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 req = + std::make_unique(); + 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; } } diff --git a/components/spotify/cspot/bell/src/HTTPStream.cpp b/components/spotify/cspot/bell/src/HTTPStream.cpp index ce3f4894..69fe2fb2 100644 --- a/components/spotify/cspot/bell/src/HTTPStream.cpp +++ b/components/spotify/cspot/bell/src/HTTPStream.cpp @@ -8,13 +8,19 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else #include #include #include #include +#include +#endif #include #include -#include bell::HTTPStream::HTTPStream() { diff --git a/components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp b/components/spotify/cspot/bell/src/TLSSocket.cpp similarity index 98% rename from components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp rename to components/spotify/cspot/bell/src/TLSSocket.cpp index ae461477..ed1977b4 100644 --- a/components/spotify/cspot/bell/src/platform/esp/TLSSocket.cpp +++ b/components/spotify/cspot/bell/src/TLSSocket.cpp @@ -1,4 +1,4 @@ -#include "platform/TLSSocket.h" +#include "TLSSocket.h" /** * Platform TLSSocket implementation for the mbedtls diff --git a/components/spotify/cspot/bell/src/audio/codec/AACDecoder.cpp b/components/spotify/cspot/bell/src/audio/codec/AACDecoder.cpp new file mode 100644 index 00000000..b0a8c56c --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/AACDecoder.cpp @@ -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(&inData), + reinterpret_cast(&inLen), + static_cast(this->pcmData)); + AACGetLastFrameInfo(aac, &frame); + if (status != ERR_AAC_NONE) { + lastErrno = status; + return nullptr; + } + outLen = frame.outputSamps * sizeof(int16_t); + return (uint8_t *)pcmData; +} diff --git a/components/spotify/cspot/bell/src/audio/codec/ALACDecoder.cpp b/components/spotify/cspot/bell/src/audio/codec/ALACDecoder.cpp new file mode 100644 index 00000000..c0a9f92d --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/ALACDecoder.cpp @@ -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(&inData), + // reinterpret_cast(&inLen), + // static_cast(this->pcmData)); + // AACGetLastFrameInfo(aac, &frame); + // if (status != ERR_AAC_NONE) { + // lastErrno = status; + // return nullptr; + // } + // outLen = frame.outputSamps * sizeof(int16_t); + // return (uint8_t *)pcmData; +} diff --git a/components/spotify/cspot/bell/src/audio/codec/AudioCodecs.cpp b/components/spotify/cspot/bell/src/audio/codec/AudioCodecs.cpp new file mode 100644 index 00000000..21c7243a --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/AudioCodecs.cpp @@ -0,0 +1,71 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-12. + +#include "AudioCodecs.h" +#include + +#ifdef BELL_CODEC_AAC +#include "AACDecoder.h" +static std::shared_ptr codecAac; +#endif + +#ifdef BELL_CODEC_MP3 +#include "MP3Decoder.h" +static std::shared_ptr codecMp3; +#endif + +#ifdef BELL_CODEC_VORBIS +#include "VorbisDecoder.h" +static std::shared_ptr codecVorbis; +#endif + +#ifdef BELL_CODEC_OPUS +#include "OPUSDecoder.h" +static std::shared_ptr codecOpus; +#endif + +std::map> customCodecs; + +std::shared_ptr 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(); + return codecAac; +#endif +#ifdef BELL_CODEC_MP3 + case AudioCodec::MP3: + if (codecMp3) + return codecMp3; + codecMp3 = std::make_shared(); + return codecMp3; +#endif +#ifdef BELL_CODEC_VORBIS + case AudioCodec::VORBIS: + if (codecVorbis) + return codecVorbis; + codecVorbis = std::make_shared(); + return codecVorbis; +#endif +#ifdef BELL_CODEC_OPUS + case AudioCodec::OPUS: + if (codecOpus) + return codecOpus; + codecOpus = std::make_shared(); + return codecOpus; +#endif + default: + return nullptr; + } +} + +std::shared_ptr AudioCodecs::getCodec(BaseContainer *container) { + return getCodec(container->codec); +} + +void AudioCodecs::addCodec(AudioCodec type, const std::shared_ptr &codec) { + customCodecs[type] = codec; +} diff --git a/components/spotify/cspot/bell/src/audio/codec/BaseCodec.cpp b/components/spotify/cspot/bell/src/audio/codec/BaseCodec.cpp new file mode 100644 index 00000000..7f995fe7 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/BaseCodec.cpp @@ -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); +} diff --git a/components/spotify/cspot/bell/src/audio/codec/DecoderGlobals.cpp b/components/spotify/cspot/bell/src/audio/codec/DecoderGlobals.cpp new file mode 100644 index 00000000..f7c04e5e --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/DecoderGlobals.cpp @@ -0,0 +1,8 @@ +#include "DecoderGlobals.h" + +bell::DecodersInstance* bell::decodersInstance; + +void bell::createDecoders() +{ + bell::decodersInstance = new bell::DecodersInstance(); +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/audio/codec/MP3Decoder.cpp b/components/spotify/cspot/bell/src/audio/codec/MP3Decoder.cpp new file mode 100644 index 00000000..c15329e7 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/MP3Decoder.cpp @@ -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(&inData), + reinterpret_cast(&inLen), + static_cast(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; +} diff --git a/components/spotify/cspot/bell/src/audio/codec/OPUSDecoder.cpp b/components/spotify/cspot/bell/src/audio/codec/OPUSDecoder.cpp new file mode 100644 index 00000000..dfe5a899 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/OPUSDecoder.cpp @@ -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(inData), + static_cast(inLen), + pcmData, + MAX_FRAME_SIZE, + false); + outLen *= opus->channels * sizeof(int16_t); + return (uint8_t *)pcmData; +} diff --git a/components/spotify/cspot/bell/src/audio/codec/VorbisDecoder.cpp b/components/spotify/cspot/bell/src/audio/codec/VorbisDecoder.cpp new file mode 100644 index 00000000..df630eb0 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/codec/VorbisDecoder.cpp @@ -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 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(inData); + op.packet->buffer->size = static_cast(inLen); + op.packet->length = static_cast(inLen); +} diff --git a/components/spotify/cspot/bell/src/audio/container/AudioContainers.cpp b/components/spotify/cspot/bell/src/audio/container/AudioContainers.cpp new file mode 100644 index 00000000..38e1fbf3 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/AudioContainers.cpp @@ -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 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(); + + return nullptr; +} diff --git a/components/spotify/cspot/bell/src/audio/container/BaseContainer.cpp b/components/spotify/cspot/bell/src/audio/container/BaseContainer.cpp new file mode 100644 index 00000000..c06778a1 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/BaseContainer.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Kuba SzczodrzyƄski 2022-1-7. + +#include "BaseContainer.h" + +void BaseContainer::feed(const std::shared_ptr &stream, uint32_t position) { + this->reader = std::make_unique(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((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); +} diff --git a/components/spotify/cspot/bell/src/audio/container/Mpeg4Container.cpp b/components/spotify/cspot/bell/src/audio/container/Mpeg4Container.cpp new file mode 100644 index 00000000..a86d3fba --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/Mpeg4Container.cpp @@ -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 &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; +} diff --git a/components/spotify/cspot/bell/src/audio/container/Mpeg4Parser.cpp b/components/spotify/cspot/bell/src/audio/container/Mpeg4Parser.cpp new file mode 100644 index 00000000..fc8bbef8 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/Mpeg4Parser.cpp @@ -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); + } +} diff --git a/components/spotify/cspot/bell/src/audio/container/Mpeg4ParserFrag.cpp b/components/spotify/cspot/bell/src/audio/container/Mpeg4ParserFrag.cpp new file mode 100644 index 00000000..e31b71e5 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/Mpeg4ParserFrag.cpp @@ -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(); +} diff --git a/components/spotify/cspot/bell/src/audio/container/Mpeg4Utils.cpp b/components/spotify/cspot/bell/src/audio/container/Mpeg4Utils.cpp new file mode 100644 index 00000000..cf9df7fa --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/Mpeg4Utils.cpp @@ -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; +} diff --git a/components/spotify/cspot/bell/src/audio/container/WebmContainer.cpp b/components/spotify/cspot/bell/src/audio/container/WebmContainer.cpp new file mode 100644 index 00000000..e9f74c2d --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/WebmContainer.cpp @@ -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 &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(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(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(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; +} diff --git a/components/spotify/cspot/bell/src/audio/container/WebmParser.cpp b/components/spotify/cspot/bell/src/audio/container/WebmParser.cpp new file mode 100644 index 00000000..db94c999 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/WebmParser.cpp @@ -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(realloc(cues, cuesLen * sizeof(CuePoint))); + continue; // read the next child + case ElementId::CuePoint: + if (cueIdx >= cuesLen) { + cuesLen++; + cues = static_cast(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(malloc(esize + 1)); + trackCodecId[esize] = '\0'; + readBytes((uint8_t *)trackCodecId, esize); + break; + case ElementId::CodecPrivate: + trackCodecPrivate = static_cast(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); +} diff --git a/components/spotify/cspot/bell/src/audio/container/WebmUtils.cpp b/components/spotify/cspot/bell/src/audio/container/WebmUtils.cpp new file mode 100644 index 00000000..502c2b47 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/container/WebmUtils.cpp @@ -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(); +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/AC101AudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/AC101AudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/esp/AC101AudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/AC101AudioSink.cpp diff --git a/components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/BufferedAudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/esp/BufferedAudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/BufferedAudioSink.cpp diff --git a/components/spotify/cspot/bell/src/audio/sinks/esp/ES8311AudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/ES8311AudioSink.cpp new file mode 100644 index 00000000..e053d942 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/sinks/esp/ES8311AudioSink.cpp @@ -0,0 +1,105 @@ +#include "ES8311AudioSink.h" +extern "C" { + #include "es8311.h" +} +ES8311AudioSink::ES8311AudioSink() +{ + this->softwareVolumeControl = false; + esp_err_t ret_val = ESP_OK; + Es8311Config cfg = { + .esMode = ES_MODE_SLAVE, + .i2c_port_num = I2C_NUM_0, + .i2c_cfg = { + .mode = I2C_MODE_MASTER, + .sda_io_num = 1, + .scl_io_num = 2, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + }, + .dacOutput = (DacOutput) (DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2), + .adcInput = ADC_INPUT_LINPUT1_RINPUT1, + }; + cfg.i2c_cfg.master.clk_speed = 100000; + Es8311Init(&cfg); + Es8311SetBitsPerSample(ES_MODULE_DAC, BIT_LENGTH_16BITS); + Es8311ConfigFmt(ES_MODULE_DAC, ES_I2S_NORMAL); + Es8311SetVoiceVolume(60); + Es8311Start(ES_MODULE_DAC); + ES8311WriteReg(ES8311_CLK_MANAGER_REG01, 0xbf); + ES8311WriteReg(ES8311_CLK_MANAGER_REG02, 0x18); + + // .codec_mode = AUDIO_HAL_CODEC_MODE_DECODE, + // .i2s_iface = { + // .mode = AUDIO_HAL_MODE_SLAVE, + // .fmt = AUDIO_HAL_I2S_NORMAL, + // .samples = AUDIO_HAL_44K_SAMPLES, + // .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + // }, + // }; + + // ret_val |= es8311_codec_init(&cfg); + // ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); + // ret_val |= es8311_config_fmt((es_i2s_fmt_t) cfg.i2s_iface.fmt); + // ret_val |= es8311_codec_set_voice_volume(60); + // ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + // ret_val |= es8311_codec_set_clk(); + + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX + .sample_rate = 44100, + .bits_per_sample = (i2s_bits_per_sample_t)16, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // 2-channels + .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S, + .intr_alloc_flags = 0, // Default interrupt priority + .dma_buf_count = 8, + .dma_buf_len = 512, + .use_apll = false, + .tx_desc_auto_clear = true, // Auto clear tx descriptor on underflow + .fixed_mclk = 256 * 44100}; + + i2s_pin_config_t pin_config = { + .mck_io_num = 42, + .bck_io_num = 40, + .ws_io_num = 41, + .data_out_num = 39, + .data_in_num = -1, + }; + + int err; + + err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + if (err != ESP_OK) + { + ESP_LOGE("OI", "i2s driver installation error: %d", err); + } + + err = i2s_set_pin((i2s_port_t)0, &pin_config); + if (err != ESP_OK) + { + ESP_LOGE("OI", "i2s set pin error: %d", err); + } + + // PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + // REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); + // ESP_LOGI("OI", "MCLK output on CLK_OUT1"); + + startI2sFeed(); +} + +void ES8311AudioSink::volumeChanged(uint16_t volume) +{ + // int volAdj = (int)((((float)volume) / 65536) * 100); + // es8311_codec_set_voice_vo0lume(volAdj); + if (volume > 79) { + volume = 79; + } + Es8311SetVoiceVolume(volume); +} + +void ES8311AudioSink::writeReg(uint8_t reg_add, uint8_t data) +{ +} + +ES8311AudioSink::~ES8311AudioSink() +{ +} diff --git a/components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/ES8388AudioSink.cpp similarity index 96% rename from components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/ES8388AudioSink.cpp index a7f7baa9..f36a5e08 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/ES8388AudioSink.cpp +++ b/components/spotify/cspot/bell/src/audio/sinks/esp/ES8388AudioSink.cpp @@ -51,11 +51,6 @@ ES8388AudioSink::ES8388AudioSink() ESP_LOGE("OI", "i2s set pin error: %d", err); } - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); - ESP_LOGI("OI", "MCLK output on CLK_OUT1"); - - err = i2c_param_config(0, &i2c_config); if (err != ESP_OK) { ESP_LOGE("OI", "i2c param config error: %d", err); diff --git a/components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/ES9018AudioSink.cpp similarity index 86% rename from components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/ES9018AudioSink.cpp index 50acba92..94dc5817 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/ES9018AudioSink.cpp +++ b/components/spotify/cspot/bell/src/audio/sinks/esp/ES9018AudioSink.cpp @@ -28,10 +28,6 @@ ES9018AudioSink::ES9018AudioSink() i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); i2s_set_pin((i2s_port_t)0, &pin_config); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); - ESP_LOGI("OI", "MCLK output on CLK_OUT1"); - startI2sFeed(); } diff --git a/components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/InternalAudioSink.cpp similarity index 95% rename from components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/InternalAudioSink.cpp index abd1e49c..26872a2e 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/InternalAudioSink.cpp +++ b/components/spotify/cspot/bell/src/audio/sinks/esp/InternalAudioSink.cpp @@ -5,6 +5,7 @@ InternalAudioSink::InternalAudioSink() { softwareVolumeControl = true; usign = true; + #ifdef I2S_MODE_DAC_BUILT_IN i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), // Only TX @@ -24,6 +25,7 @@ InternalAudioSink::InternalAudioSink() i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); //init DAC i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); + #endif startI2sFeed(); } diff --git a/components/spotify/cspot/bell/src/sinks/esp/PCM5102AudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/PCM5102AudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/esp/PCM5102AudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/PCM5102AudioSink.cpp diff --git a/components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/SPDIFAudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/esp/SPDIFAudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/SPDIFAudioSink.cpp diff --git a/components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/esp/TAS5711AudioSink.cpp similarity index 95% rename from components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/esp/TAS5711AudioSink.cpp index 0d2d847e..01a24c98 100644 --- a/components/spotify/cspot/bell/src/sinks/esp/TAS5711AudioSink.cpp +++ b/components/spotify/cspot/bell/src/audio/sinks/esp/TAS5711AudioSink.cpp @@ -44,10 +44,6 @@ TAS5711AudioSink::TAS5711AudioSink() i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); i2s_set_pin((i2s_port_t)0, &pin_config); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - REG_SET_FIELD(PIN_CTRL, CLK_OUT1, 0); - ESP_LOGI("OI", "MCLK output on CLK_OUT1"); - // configure i2c i2c_config = { .mode = I2C_MODE_MASTER, diff --git a/components/spotify/cspot/bell/src/sinks/esp/ac101.c b/components/spotify/cspot/bell/src/audio/sinks/esp/ac101.c similarity index 100% rename from components/spotify/cspot/bell/src/sinks/esp/ac101.c rename to components/spotify/cspot/bell/src/audio/sinks/esp/ac101.c diff --git a/components/spotify/cspot/bell/src/audio/sinks/esp/es8311.c b/components/spotify/cspot/bell/src/audio/sinks/esp/es8311.c new file mode 100644 index 00000000..981c5db7 --- /dev/null +++ b/components/spotify/cspot/bell/src/audio/sinks/esp/es8311.c @@ -0,0 +1,798 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include "esp_log.h" +#include "es8311.h" +// #include "board.h" + +/* ES8311 address + * 0x32:CE=1;0x30:CE=0 + */ +#define ES8311_ADDR 0x32 + +#define ES7243_ADDR 0x26 + +/* +* to define the clock soure of MCLK +*/ +#define FROM_MCLK_PIN 0 +#define FROM_SCLK_PIN 1 +/* +* to define work mode(master or slave) +*/ +#define MASTER_MODE 0 +#define SLAVE_MODE 1 +/* +* to define serial digital audio format +*/ +#define I2S_FMT 0 +#define LEFT_JUSTIFIED_FMT 1 +#define DPS_PCM_A_FMT 2 +#define DPS_PCM_B_FMT 3 +/* +* to define resolution of PCM interface +*/ +#define LENGTH_16BIT 0 +#define LENGTH_24BIT 1 +#define LENGTH_32BIT 2 + +/* +* codec private data +*/ +struct es8311_private { + bool dmic_enable; + bool mclkinv; + bool sclkinv; + uint8_t master_slave_mode; + uint8_t pcm_format; + uint8_t pcm_resolution; + uint8_t mclk_src; +}; +static struct es8311_private *es8311_priv; + +/* +* Clock coefficient structer +*/ +struct _coeff_div { + uint32_t mclk; /* mclk frequency */ + uint32_t rate; /* sample rate */ + uint8_t prediv; /* the pre divider with range from 1 to 8 */ + uint8_t premulti; /* the pre multiplier with x1, x2, x4 and x8 selection */ + uint8_t adcdiv; /* adcclk divider */ + uint8_t dacdiv; /* dacclk divider */ + uint8_t fsmode; /* double speed or single speed, =0, ss, =1, ds */ + uint8_t lrck_h; /* adclrck divider and daclrck divider */ + uint8_t lrck_l; + uint8_t bclkdiv; /* sclk divider */ + uint8_t adcosr; /* adc osr */ + uint8_t dacosr; /* dac osr */ +}; +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + //mclk rate prediv mult adcdiv dacdiv fsmode lrch lrcl bckdiv osr + /* 8k */ + {12288000, 8000 , 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 8000 , 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10}, + {16384000, 8000 , 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 8000 , 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 8000 , 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {4096000 , 8000 , 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2048000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 8000 , 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1024000 , 8000 , 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 11.025k */ + {11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 12k */ + {12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 16k */ + {12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, + {16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {4096000 , 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2048000 , 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1024000 , 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 22.05k */ + {11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 24k */ + {12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 32k */ + {12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, + {16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {4096000 , 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2048000 , 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + {1024000 , 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 44.1k */ + {11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 48k */ + {12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 64k */ + {12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {4096000 , 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {2048000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18}, + {1024000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + /* 88.2k */ + {11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + /* 96k */ + {12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, +}; + +static char *TAG = "DRV8311"; + +#define ES_ASSERT(a, format, b, ...) \ + if ((a) != 0) { \ + ESP_LOGE(TAG, format, ##__VA_ARGS__); \ + return b;\ + } + +static int Es8311WriteReg(uint8_t regAdd, uint8_t data) +{ + int res = 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES8311_ADDR, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_write_byte(cmd, data, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + ES_ASSERT(res, "Es8311 Write Reg error", -1); + return res; +} + +int Es8311ReadReg(uint8_t regAdd) +{ + uint8_t data; + int res = 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES8311_ADDR, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + cmd = i2c_cmd_link_create(); + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES8311_ADDR | 0x01, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_read_byte(cmd, &data, 0x01 /*NACK_VAL*/); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + ES_ASSERT(res, "Es8311 Read Reg error", -1); + return (int)data; +} + +static int Es7243WriteReg(uint8_t regAdd, uint8_t data) +{ + int res = 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES7243_ADDR, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_write_byte(cmd, data, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + ES_ASSERT(res, "Es7243 Write Reg error", -1); + return res; +} + + +int Es7243ReadReg(uint8_t regAdd) +{ + uint8_t data; + int res = 0; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES7243_ADDR, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_write_byte(cmd, regAdd, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + cmd = i2c_cmd_link_create(); + res |= i2c_master_start(cmd); + res |= i2c_master_write_byte(cmd, ES7243_ADDR | 0x01, 1 /*ACK_CHECK_EN*/); + res |= i2c_master_read_byte(cmd, &data, 0x01 /*NACK_VAL*/); + res |= i2c_master_stop(cmd); + res |= i2c_master_cmd_begin(0, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + + ES_ASSERT(res, "Es7243 Read Reg error", -1); + return (int)data; +} + +esp_err_t Es7243Init(void) +{ + esp_err_t ret = ESP_OK; + ret |= Es7243WriteReg(0x00, 0x01); + ret |= Es7243WriteReg(0x06, 0x00); + ret |= Es7243WriteReg(0x05, 0x1B); + ret |= Es7243WriteReg(0x01, 0x0C); + ret |= Es7243WriteReg(0x08, 0x43); + ret |= Es7243WriteReg(0x05, 0x13); + if (ret) { + ESP_LOGE(TAG, "Es7243 initialize failed!"); + return ESP_FAIL; + } + return ret; +} + +static int I2cInit(i2c_config_t *conf, int i2cMasterPort) +{ + int res; + res = i2c_param_config(i2cMasterPort, conf); + res |= i2c_driver_install(i2cMasterPort, conf->mode, 0, 0, 0); + ES_ASSERT(res, "I2cInit error", -1); + return res; +} + +/* +* look for the coefficient in coeff_div[] table +*/ +static int get_coeff(uint32_t mclk, uint32_t rate) +{ + for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -1; +} +/* +* set es8311 clock parameter and PCM/I2S interface +*/ +static void es8311_pcm_hw_params(uint32_t mclk, uint32_t lrck) +{ + int coeff; + uint8_t regv, datmp; + ESP_LOGI(TAG, "Enter into es8311_pcm_hw_params()\n"); + coeff = get_coeff(mclk, lrck); + if (coeff < 0) { + ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK\n", lrck, mclk); + return; + } + + /* + * set clock parammeters + */ + if (coeff >= 0) { + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG02) & 0x07; + regv |= (coeff_div[coeff].prediv - 1) << 5; + datmp = 0; + switch (coeff_div[coeff].premulti) { + case 1: + datmp = 0; + break; + case 2: + datmp = 1; + break; + case 4: + datmp = 2; + break; + case 8: + datmp = 3; + break; + default: + break; + } +#if CONFIG_ESP32_KORVO_V1_1_BOARD + datmp = 3; +#endif + regv |= (datmp) << 3; + Es8311WriteReg(ES8311_CLK_MANAGER_REG02, regv); + + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG05) & 0x00; + regv |= (coeff_div[coeff].adcdiv - 1) << 4; + regv |= (coeff_div[coeff].dacdiv - 1) << 0; + Es8311WriteReg(ES8311_CLK_MANAGER_REG05, regv); + + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG03) & 0x80; + regv |= coeff_div[coeff].fsmode << 6; + regv |= coeff_div[coeff].adcosr << 0; + Es8311WriteReg(ES8311_CLK_MANAGER_REG03, regv); + + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG04) & 0x80; + regv |= coeff_div[coeff].dacosr << 0; + Es8311WriteReg(ES8311_CLK_MANAGER_REG04, regv); + + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG07) & 0xC0; + regv |= coeff_div[coeff].lrck_h << 0; + Es8311WriteReg(ES8311_CLK_MANAGER_REG07, regv); + + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG08) & 0x00; + regv |= coeff_div[coeff].lrck_l << 0; + Es8311WriteReg(ES8311_CLK_MANAGER_REG08, regv); + + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG06) & 0xE0; + if (coeff_div[coeff].bclkdiv < 19) { + regv |= (coeff_div[coeff].bclkdiv - 1) << 0; + } else { + regv |= (coeff_div[coeff].bclkdiv) << 0; + } + Es8311WriteReg(ES8311_CLK_MANAGER_REG06, regv); + } +} +/* +* set data and clock in tri-state mode +* if tristate = 0, tri-state is disabled for normal mode +* if tristate = 1, tri-state is enabled +*/ +// static void es8311_set_tristate(int tristate) +// { +// uint8_t regv; +// ESP_LOGI(TAG, "Enter into es8311_set_tristate(), tristate = %d\n", tristate); +// regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG07) & 0xcf; +// if (tristate) { +// Es8311WriteReg(ES8311_CLK_MANAGER_REG07, regv | 0x30); +// } else { +// Es8311WriteReg(ES8311_CLK_MANAGER_REG07, regv); +// } +// } +/* +* set es8311 dac mute or not +* if mute = 0, dac un-mute +* if mute = 1, dac mute +*/ +static void es8311_mute(int mute) +{ + uint8_t regv; + ESP_LOGI(TAG, "Enter into es8311_mute(), mute = %d\n", mute); + regv = Es8311ReadReg(ES8311_DAC_REG31) & 0x9f; + if (mute) { + Es8311WriteReg(ES8311_SYSTEM_REG12, 0x02); + Es8311WriteReg(ES8311_DAC_REG31, regv | 0x60); + Es8311WriteReg(ES8311_DAC_REG32, 0x00); + Es8311WriteReg(ES8311_DAC_REG37, 0x08); + } else { + Es8311WriteReg(ES8311_DAC_REG31, regv); + Es8311WriteReg(ES8311_SYSTEM_REG12, 0x00); + } +} +/* +* set es8311 into suspend mode +*/ +// static void es8311_suspend(void) +// { +// ESP_LOGI(TAG, "Enter into es8311_suspend()\n"); +// Es8311WriteReg(ES8311_DAC_REG32, 0x00); +// Es8311WriteReg(ES8311_ADC_REG17, 0x00); +// Es8311WriteReg(ES8311_SYSTEM_REG0E, 0xFF); +// Es8311WriteReg(ES8311_SYSTEM_REG12, 0x02); +// Es8311WriteReg(ES8311_SYSTEM_REG14, 0x00); +// Es8311WriteReg(ES8311_SYSTEM_REG0D, 0xFA); +// Es8311WriteReg(ES8311_ADC_REG15, 0x00); +// Es8311WriteReg(ES8311_DAC_REG37, 0x08); +// Es8311WriteReg(ES8311_RESET_REG00, 0x00); +// Es8311WriteReg(ES8311_RESET_REG00, 0x1F); +// Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x30); +// Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x00); +// Es8311WriteReg(ES8311_GP_REG45, 0x01); +// } +/* +* initialize es8311 codec +*/ +static void es8311_init(uint32_t mclk_freq, uint32_t lrck_freq) +{ + int regv; + Es8311WriteReg(ES8311_GP_REG45, 0x00); + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x30); + Es8311WriteReg(ES8311_CLK_MANAGER_REG02, 0x00); + Es8311WriteReg(ES8311_CLK_MANAGER_REG03, 0x10); + Es8311WriteReg(ES8311_ADC_REG16, 0x24); + Es8311WriteReg(ES8311_CLK_MANAGER_REG04, 0x10); + Es8311WriteReg(ES8311_CLK_MANAGER_REG05, 0x00); + Es8311WriteReg(ES8311_SYSTEM_REG0B, 0x00); + Es8311WriteReg(ES8311_SYSTEM_REG0C, 0x00); + Es8311WriteReg(ES8311_SYSTEM_REG10, 0x1F); + Es8311WriteReg(ES8311_SYSTEM_REG11, 0x7F); + Es8311WriteReg(ES8311_RESET_REG00, 0x80); + /* + * Set Codec into Master or Slave mode + */ + regv = Es8311ReadReg(ES8311_RESET_REG00); + /* set master/slave audio interface */ + switch (es8311_priv->master_slave_mode) { + case MASTER_MODE: /* MASTER MODE */ + ESP_LOGI(TAG, "ES8311 in Master mode\n"); + regv |= 0x40; + break; + case SLAVE_MODE: /* SLAVE MODE */ + ESP_LOGI(TAG, "ES8311 in Slave mode\n"); + regv &= 0xBF; + break; + default: + regv &= 0xBF; + } + Es8311WriteReg(ES8311_RESET_REG00, regv); + Es8311WriteReg(ES8311_SYSTEM_REG0D, 0x01); + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x3F); + /* + * select clock source for internal mclk + */ + switch (es8311_priv->mclk_src) { + case FROM_MCLK_PIN: + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01); + regv &= 0x7F; + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv); + break; + case FROM_SCLK_PIN: + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01); + regv |= 0x80; + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv); + break; + default: + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01); + regv &= 0x7F; + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv); + break; + } + es8311_pcm_hw_params(mclk_freq, lrck_freq); + + /* + * mclk inverted or not + */ + if (es8311_priv->mclkinv == true) { + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01); + regv |= 0x40; + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv); + } else { + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG01); + regv &= ~(0x40); + Es8311WriteReg(ES8311_CLK_MANAGER_REG01, regv); + } + /* + * sclk inverted or not + */ + if (es8311_priv->sclkinv == true) { + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG06); + regv |= 0x20; + Es8311WriteReg(ES8311_CLK_MANAGER_REG06, regv); + } else { + regv = Es8311ReadReg(ES8311_CLK_MANAGER_REG06); + regv &= ~(0x20); + Es8311WriteReg(ES8311_CLK_MANAGER_REG06, regv); + } + Es8311WriteReg(ES8311_SYSTEM_REG14, 0x1A); + /* + * pdm dmic enable or disable + */ + if (es8311_priv->dmic_enable == true) { + regv = Es8311ReadReg(ES8311_SYSTEM_REG14); + regv |= 0x40; + Es8311WriteReg(ES8311_SYSTEM_REG14, regv); + } else { + regv = Es8311ReadReg(ES8311_SYSTEM_REG14); + regv &= ~(0x40); + Es8311WriteReg(ES8311_SYSTEM_REG14, regv); + } + + Es8311WriteReg(ES8311_SYSTEM_REG13, 0x10); + Es8311WriteReg(ES8311_SYSTEM_REG0E, 0x02); + Es8311WriteReg(ES8311_ADC_REG15, 0x40); + Es8311WriteReg(ES8311_ADC_REG1B, 0x0A); + Es8311WriteReg(ES8311_ADC_REG1C, 0x6A); + Es8311WriteReg(ES8311_DAC_REG37, 0x48); + Es8311WriteReg(ES8311_GPIO_REG44, 0x08); + Es8311WriteReg(ES8311_DAC_REG32, 0xBF); + +#ifdef CONFIG_USE_ES7243 + Es7243Init(); +#endif +} +/* +* set codec private data and initialize codec +*/ +static void es8311_Codec_Startup(uint32_t mclk_freq, uint32_t lrck_freq) +{ + ESP_LOGI(TAG, "Enter into es8311_Codec_Startup()\n"); + es8311_priv->dmic_enable = false; + es8311_priv->mclkinv = false; + es8311_priv->sclkinv = false; + es8311_priv->pcm_format = I2S_FMT; + es8311_priv->pcm_resolution = LENGTH_16BIT; + es8311_priv->master_slave_mode = SLAVE_MODE; +#ifdef CONFIG_ESP32_KORVO_V1_1_BOARD + es8311_priv->mclk_src = FROM_SCLK_PIN; +#else + es8311_priv->mclk_src = FROM_MCLK_PIN; +#endif + + es8311_init(mclk_freq, lrck_freq); + + ESP_LOGI(TAG, "Exit es8311_Codec_Startup()\n"); +} + +// static int Es8311SetAdcDacVolume(int mode, int volume, int dot) +// { +// int res = 0; +// if ( volume < -96 || volume > 0 ) { +// ESP_LOGI(TAG, "Warning: volume < -96! or > 0!\n"); +// if (volume < -96) { +// volume = -96; +// } else { +// volume = 0; +// } +// } +// dot = (dot >= 5 ? 1 : 0); +// return res; +// } + +esp_err_t Es8311GetRef(bool flag) +{ + esp_err_t ret = ESP_OK; + uint8_t regv = 0; + if (flag) { + regv = Es8311ReadReg(ES8311_GPIO_REG44); + regv |= 0x50; + ret |= Es8311WriteReg(ES8311_GPIO_REG44, regv); + } else { + ret |= Es8311WriteReg(ES8311_GPIO_REG44, 0x08); + } + return ret; +} + +int Es8311Init(Es8311Config *cfg) +{ + es8311_priv = calloc(1, sizeof(struct es8311_private)); + I2cInit(&cfg->i2c_cfg, cfg->i2c_port_num); // ESP32 in master mode + es8311_Codec_Startup(11289600, 44100); + return 0; +} + +void Es8311Uninit() +{ + Es8311WriteReg(ES8311_RESET_REG00, 0x3f); + free(es8311_priv); + es8311_priv = NULL; +} + +int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt) +{ + int res = 0; + uint8_t regAdc = 0, regDac = 0; + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res |= Es8311WriteReg(ES8311_SYSTEM_REG12, 0x00); + } + regAdc = Es8311ReadReg(ES8311_SDPIN_REG09); + regDac = Es8311ReadReg(ES8311_SDPOUT_REG0A); + switch (fmt) { + case ES_I2S_NORMAL: + ESP_LOGI(TAG, "ES8311 in I2S Format"); + regAdc &= ~0x03; + regDac &= ~0x03; + break; + case ES_I2S_LEFT: + case ES_I2S_RIGHT: + ESP_LOGI(TAG, "ES8311 in LJ Format"); + regAdc &= ~0x03; + regAdc |= 0x01; + regDac &= ~0x03; + regDac |= 0x01; + break; + case ES_I2S_DSP: + ESP_LOGI(TAG, "ES8311 in DSP Format"); + regAdc |= 0x03; + regDac |= 0x03; + break; + default: + ESP_LOGE(TAG, "Not Supported Format"); + break; + } + res |= Es8311WriteReg(ES8311_SDPIN_REG09, regAdc); + res |= Es8311WriteReg(ES8311_SDPOUT_REG0A, regDac); + return res; +} + +int Es8311I2sConfigClock(ESCodecI2sClock cfg) +{ + int res = 0; + return res; +} + +int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample) +{ + int res = 0; + uint8_t reg = 0; + int bits = (int)bitPerSample; + + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + reg = Es8311ReadReg(ES8311_SDPIN_REG09); + reg = reg & 0xe3; + res |= Es8311WriteReg(ES8311_SDPIN_REG09, reg | (bits << 2)); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + reg = Es8311ReadReg(ES8311_SDPOUT_REG0A); + reg = reg & 0xe3; + res |= Es8311WriteReg(ES8311_SDPOUT_REG0A, reg | (bits << 2)); + } + return res; +} + +int Es8311Start(ESCodecModule mode) +{ + int res = 0; + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res |= Es8311WriteReg(ES8311_SYSTEM_REG12, Es8311ReadReg(ES8311_SYSTEM_REG12) & 0xfd); + } + return res; +} + +int Es8311Stop(ESCodecModule mode) +{ + int res = 0; + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + res |= Es8311WriteReg(ES8311_ADC_REG17, 0x00); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res |= Es8311WriteReg(ES8311_SYSTEM_REG12, Es8311ReadReg(ES8311_SYSTEM_REG12) | 0x02); + } + return res; +} + +int Es8311SetVoiceVolume(int volume) +{ + int res = 0; + if (volume < 0) { + volume = 0; + } else if (volume > 100) { + volume = 100; + } + int vol = (volume) * 2550 / 1000 + 0.5; + ESP_LOGI(TAG, "SET: volume:%d\n", vol); + Es8311WriteReg(ES8311_DAC_REG32, vol); + return res; +} + +int Es8311GetVoiceVolume(int *volume) +{ + int res = ESP_OK; + int regv = Es8311ReadReg(ES8311_DAC_REG32); + if (regv == ESP_FAIL) { + *volume = 0; + res = ESP_FAIL; + } else { + *volume = regv * 100 / 256; + } + ESP_LOGI(TAG, "GET: res:%d, volume:%d\n", regv, *volume); + return res; +} + +int Es8311SetVoiceMute(int enable) +{ + int res = 0; + ESP_LOGI(TAG, "Es8311SetVoiceMute volume:%d\n", enable); + es8311_mute(enable); + return res; +} + +int Es8311GetVoiceMute(int *mute) +{ + int res = -1; + uint8_t reg = 0; + res = Es8311ReadReg(ES8311_DAC_REG31); + if (res != ESP_FAIL) { + reg = (res & 0x20) >> 5; + } + *mute = reg; + return res; +} + +int Es8311SetMicGain(MicGain gain) +{ + int res = 0; + uint8_t gain_n = Es8311ReadReg(ES8311_ADC_REG16) & 0x07; + gain_n |= gain / 6; + res = Es8311WriteReg(ES8311_ADC_REG16, gain_n); // MIC gain scale + return res; +} + +int Es8311ConfigAdcInput(AdcInput input) +{ + int res = 0; + return res; +} + +int Es8311SetAdcVolume(uint8_t adc_vol) +{ + int res = 0; + res = Es8311WriteReg(ES8311_ADC_REG17, adc_vol); // MIC ADC Volume + return res; +} + +int ES8311WriteReg(uint8_t regAdd, uint8_t data) +{ + return Es8311WriteReg(regAdd, data); +} + +void Es8311ReadAll() +{ + for (int i = 0; i < 0x4A; i++) { + uint8_t reg = Es8311ReadReg(i); + ets_printf("REG:%02x, %02x\n", reg, i); + } +} \ No newline at end of file diff --git a/components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/unix/ALSAAudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/unix/ALSAAudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/unix/ALSAAudioSink.cpp diff --git a/components/spotify/cspot/bell/src/sinks/unix/NamedPipeAudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/unix/NamedPipeAudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/unix/NamedPipeAudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/unix/NamedPipeAudioSink.cpp diff --git a/components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp b/components/spotify/cspot/bell/src/audio/sinks/unix/PortAudioSink.cpp similarity index 100% rename from components/spotify/cspot/bell/src/sinks/unix/PortAudioSink.cpp rename to components/spotify/cspot/bell/src/audio/sinks/unix/PortAudioSink.cpp diff --git a/components/spotify/cspot/bell/src/platform/.DS_Store b/components/spotify/cspot/bell/src/platform/.DS_Store deleted file mode 100644 index c77653c9fd899b241cf192638799a6b9327bd9ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5Z>*NO(;SR3VK`cTClb%7B3;z7cim+m70*C!I&*gVuVu2U0=u-@p+v6 zX=$upMP(-Je!H_X3G;2(*&&2bClB@ru?QgsG=Vh}x-Uemi?$*)t!V&K`Hs*p(yd_8Yu1ef?i`!WlAv4E!qwXk+LO9SBM1)|KEyu4T|}p$QRo nrNV^^3}h+>Tak*lph*Ee69Yh7W2q1{Ab1gwG+>7e{3rt-FsO)C diff --git a/components/spotify/cspot/bell/src/platform/apple/MDNSService.cpp b/components/spotify/cspot/bell/src/platform/apple/MDNSService.cpp new file mode 100644 index 00000000..9421ff62 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/apple/MDNSService.cpp @@ -0,0 +1,38 @@ +#include "platform/MDNSService.h" +#include "dns_sd.h" +#include + +/** + * MacOS implementation of MDNSService. + * @see https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister + **/ +void MDNSService::registerService( + const std::string& serviceName, + const std::string& serviceType, + const std::string& serviceProto, + const std::string& serviceHost, + int servicePort, + const std::map txtData +) { + DNSServiceRef ref = NULL; + TXTRecordRef txtRecord; + TXTRecordCreate(&txtRecord, 0, NULL); + for (auto& data : txtData) { + TXTRecordSetValue(&txtRecord, data.first.c_str(), data.second.size(), data.second.c_str()); + } + DNSServiceRegister( + &ref, /* sdRef */ + 0, /* flags */ + 0, /* interfaceIndex */ + serviceName.c_str(), /* name */ + (serviceType + "." + serviceProto).c_str(), /* regType (_spotify-connect._tcp) */ + NULL, /* domain */ + NULL, /* host */ + htons(servicePort), /* port */ + TXTRecordGetLength(&txtRecord), /* txtLen */ + TXTRecordGetBytesPtr(&txtRecord), /* txtRecord */ + NULL, /* callBack */ + NULL /* context */ + ); + TXTRecordDeallocate(&txtRecord); +} diff --git a/components/spotify/cspot/bell/src/platform/esp/MDNSService.cpp b/components/spotify/cspot/bell/src/platform/esp/MDNSService.cpp new file mode 100644 index 00000000..ee5b1980 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/esp/MDNSService.cpp @@ -0,0 +1,35 @@ +#include "platform/MDNSService.h" +#include +#include +#include "mdns.h" + +/** + * ESP32 implementation of MDNSService + * @see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mdns.html + **/ +void MDNSService::registerService( + const std::string& serviceName, + const std::string& serviceType, + const std::string& serviceProto, + const std::string& serviceHost, + int servicePort, + const std::map txtData +) { + std::vector txtItems; + txtItems.reserve(txtData.size()); + for (auto& data : txtData) { + mdns_txt_item_t item; + item.key = data.first.c_str(); + item.value = data.second.c_str(); + txtItems.push_back(item); + } + + mdns_service_add( + serviceName.c_str(), /* instance_name */ + serviceType.c_str(), /* service_type */ + serviceProto.c_str(), /* proto */ + servicePort, /* port */ + txtItems.data(), /* txt */ + txtItems.size() /* num_items */ + ); +} diff --git a/components/spotify/cspot/bell/src/platform/linux/MDNSService.cpp b/components/spotify/cspot/bell/src/platform/linux/MDNSService.cpp new file mode 100644 index 00000000..fd49aa46 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/linux/MDNSService.cpp @@ -0,0 +1,16 @@ +#include "platform/MDNSService.h" + +/** + * Linux implementation of MDNSService using avahi. + * @see https://www.avahi.org/doxygen/html/ + **/ +void MDNSService::registerService( + const std::string& serviceName, + const std::string& serviceType, + const std::string& serviceProto, + const std::string& serviceHost, + int servicePort, + const std::map txtData +) { + //TODO: add avahi stuff +} diff --git a/components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp b/components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp deleted file mode 100644 index b6c20844..00000000 --- a/components/spotify/cspot/bell/src/platform/linux/TLSSocket.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "platform/TLSSocket.h" - -/** - * Platform TLSSocket implementation for the openssl - */ - -bell::TLSSocket::TLSSocket() { - ERR_load_crypto_strings(); - ERR_load_SSL_strings(); - OpenSSL_add_all_algorithms(); - ctx = SSL_CTX_new(SSLv23_client_method()); -} - -void bell::TLSSocket::open(std::string host, uint16_t port) { - - /* We'd normally set some stuff like the verify paths and - * mode here because as things stand this will connect to - * any server whose certificate is signed by any CA. - */ - - sbio = BIO_new_ssl_connect(ctx); - - BIO_get_ssl(sbio, &ssl); - SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); - - BELL_LOG(info, "http_tls", "Connecting with %s", host.c_str()); - BIO_set_conn_hostname(sbio, std::string(host + ":" + std::to_string(port)).c_str()); - - out = BIO_new_fp(stdout, BIO_NOCLOSE); - if (BIO_do_connect(sbio) <= 0) { - BELL_LOG(error, "http_tls", "Error connecting with server"); - /* whatever ... */ - } - - if (BIO_do_handshake(sbio) <= 0) { - - BELL_LOG(error, "http_tls", "Error TLS connection"); - - /* whatever ... */ - } // remove https or http from url - - // split by first "/" in url -} - -size_t bell::TLSSocket::read(uint8_t *buf, size_t len) { - return BIO_read(sbio, buf, len); -} - -size_t bell::TLSSocket::write(uint8_t *buf, size_t len) { - return BIO_write(sbio, buf, len); -} - -size_t bell::TLSSocket::poll() { - return BIO_pending(sbio); -} - -void bell::TLSSocket::close() { - if (!isClosed) { - BIO_free_all(sbio); - BIO_free(out); - isClosed = true; - } -} diff --git a/components/spotify/cspot/bell/src/platform/win32/WrappedSemaphore.cpp b/components/spotify/cspot/bell/src/platform/win32/WrappedSemaphore.cpp new file mode 100644 index 00000000..d3c96538 --- /dev/null +++ b/components/spotify/cspot/bell/src/platform/win32/WrappedSemaphore.cpp @@ -0,0 +1,27 @@ +#include "platform/WrappedSemaphore.h" + +WrappedSemaphore::WrappedSemaphore(int count) +{ + this->semaphoreHandle = CreateSemaphore(NULL, 0, count, NULL); +} + +WrappedSemaphore::~WrappedSemaphore() +{ + CloseHandle(this->semaphoreHandle); +} + +int WrappedSemaphore::wait() +{ + WaitForSingleObject(this->semaphoreHandle, INFINITE); + return 0; +} + +int WrappedSemaphore::twait(long milliseconds) +{ + return WaitForSingleObject(this->semaphoreHandle, milliseconds) != WAIT_OBJECT_0; +} + +void WrappedSemaphore::give() +{ + ReleaseSemaphore(this->semaphoreHandle, 1, NULL); +} diff --git a/components/spotify/cspot/include/AudioChunk.h b/components/spotify/cspot/include/AudioChunk.h index 3b75e789..e1d16e58 100644 --- a/components/spotify/cspot/include/AudioChunk.h +++ b/components/spotify/cspot/include/AudioChunk.h @@ -4,7 +4,6 @@ #include #include #include -#include "pthread.h" #include "platform/WrappedSemaphore.h" #include "Crypto.h" #include "Utils.h" @@ -28,7 +27,6 @@ public: std::vector decryptedData; std::vector audioKey; bool keepInMemory = false; - pthread_mutex_t loadingMutex; std::mutex dataAccessMutex; uint32_t startPosition; uint32_t endPosition; diff --git a/components/spotify/cspot/include/AudioChunkManager.h b/components/spotify/cspot/include/AudioChunkManager.h index f120a129..5bcef9bf 100644 --- a/components/spotify/cspot/include/AudioChunkManager.h +++ b/components/spotify/cspot/include/AudioChunkManager.h @@ -8,7 +8,7 @@ #include "Utils.h" #include "AudioChunk.h" #include "Queue.h" -#include "Task.h" +#include "BellTask.h" #define DATA_SIZE_HEADER 24 #define DATA_SIZE_FOOTER 2 diff --git a/components/spotify/cspot/include/ChunkedAudioStream.h b/components/spotify/cspot/include/ChunkedAudioStream.h index 3ca5b024..4313446c 100644 --- a/components/spotify/cspot/include/ChunkedAudioStream.h +++ b/components/spotify/cspot/include/ChunkedAudioStream.h @@ -5,7 +5,9 @@ #include #include #include +#ifndef _WIN32 #include +#endif #include #include "ivorbisfile.h" #include "MercuryManager.h" diff --git a/components/spotify/cspot/include/MercuryManager.h b/components/spotify/cspot/include/MercuryManager.h index 38e9029d..400cec5f 100644 --- a/components/spotify/cspot/include/MercuryManager.h +++ b/components/spotify/cspot/include/MercuryManager.h @@ -14,7 +14,7 @@ #include "AudioChunk.h" #include "AudioChunkManager.h" #include -#include "Task.h" +#include "BellTask.h" #include "platform/WrappedSemaphore.h" #include "TimeProvider.h" #include "Session.h" diff --git a/components/spotify/cspot/include/PlainConnection.h b/components/spotify/cspot/include/PlainConnection.h index 370806d4..78e84524 100644 --- a/components/spotify/cspot/include/PlainConnection.h +++ b/components/spotify/cspot/include/PlainConnection.h @@ -1,13 +1,18 @@ #ifndef PLAINCONNECTION_H #define PLAINCONNECTION_H - +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else #include "sys/socket.h" +#include +#include +#endif #include #include #include #include -#include -#include #include "Packet.h" #include "Utils.h" diff --git a/components/spotify/cspot/include/Player.h b/components/spotify/cspot/include/Player.h index 4cce75c4..60477414 100644 --- a/components/spotify/cspot/include/Player.h +++ b/components/spotify/cspot/include/Player.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -14,7 +15,7 @@ #include "SpotifyTrack.h" #include "AudioSink.h" #include -#include "Task.h" +#include "BellTask.h" class Player : public bell::Task { private: diff --git a/components/spotify/cspot/include/ShannonConnection.h b/components/spotify/cspot/include/ShannonConnection.h index 58c85627..fdbc1764 100644 --- a/components/spotify/cspot/include/ShannonConnection.h +++ b/components/spotify/cspot/include/ShannonConnection.h @@ -2,11 +2,17 @@ #define SHANNONCONNECTION_H #include +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else #include -#include #include -#include #include +#endif +#include +#include #include #include #include "platform/WrappedMutex.h" diff --git a/components/spotify/cspot/include/Utils.h b/components/spotify/cspot/include/Utils.h index d73c0e66..5e2eadf5 100644 --- a/components/spotify/cspot/include/Utils.h +++ b/components/spotify/cspot/include/Utils.h @@ -1,11 +1,16 @@ #ifndef UTILS_H #define UTILS_H - -#include #include +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else +#include #include "sys/socket.h" -#include #include +#endif +#include #include #include #include diff --git a/components/spotify/cspot/include/ZeroconfAuthenticator.h b/components/spotify/cspot/include/ZeroconfAuthenticator.h index 1e9e9f10..06c662a2 100644 --- a/components/spotify/cspot/include/ZeroconfAuthenticator.h +++ b/components/spotify/cspot/include/ZeroconfAuthenticator.h @@ -2,19 +2,23 @@ #define ZEROCONFAUTHENTICATOR_H #include +#ifndef _WIN32 #include +#endif #include #include #include #include "Utils.h" #include "LoginBlob.h" #include "Crypto.h" -#include "Task.h" +#include "BellTask.h" #include "ConstantParameters.h" #ifdef ESP_PLATFORM #include "mdns.h" +#elif defined(_WIN32) +#include "mdnssvc.h" #else #include "dns_sd.h" #include @@ -31,6 +35,9 @@ typedef std::function)> authCallback; class ZeroconfAuthenticator { private: +#ifdef _WIN32 + struct mdnsd* service; +#endif int serverPort; bool authorized = false; std::unique_ptr crypto; diff --git a/components/spotify/cspot/src/ApResolve.cpp b/components/spotify/cspot/src/ApResolve.cpp index c28a4012..60a583da 100644 --- a/components/spotify/cspot/src/ApResolve.cpp +++ b/components/spotify/cspot/src/ApResolve.cpp @@ -7,10 +7,16 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#include "win32shim.h" +#else #include #include #include #include +#endif #include #include #include "Logger.h" diff --git a/components/spotify/cspot/src/AudioChunkManager.cpp b/components/spotify/cspot/src/AudioChunkManager.cpp index 6d69ba78..7c7a2b08 100644 --- a/components/spotify/cspot/src/AudioChunkManager.cpp +++ b/components/spotify/cspot/src/AudioChunkManager.cpp @@ -3,7 +3,7 @@ #include "Logger.h" AudioChunkManager::AudioChunkManager() - : bell::Task("AudioChunkManager", 4 * 1024, -1, 1) { + : bell::Task("AudioChunkManager", 4 * 1024, 0, 1) { this->chunks = std::vector>(); startTask(); } diff --git a/components/spotify/cspot/src/MercuryManager.cpp b/components/spotify/cspot/src/MercuryManager.cpp index 2805516b..856961db 100644 --- a/components/spotify/cspot/src/MercuryManager.cpp +++ b/components/spotify/cspot/src/MercuryManager.cpp @@ -9,7 +9,7 @@ std::map MercuryTypeMap({ {MercuryType::UNSUB, "UNSUB"}, }); -MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, 0, 1) +MercuryManager::MercuryManager(std::unique_ptr session): bell::Task("mercuryManager", 6 * 1024, 1, 1) { tempMercuryHeader = {}; this->timeProvider = std::make_shared(); @@ -115,6 +115,7 @@ std::shared_ptr MercuryManager::fetchAudioChunk(std::vector this->session->shanConn->sendPacket(static_cast(MercuryType::AUDIO_CHUNK_REQUEST_COMMAND), buffer); // Used for broken connection detection +//CSPOT_LOG(info, "requesting Chunk %hu", this->audioChunkSequence - 1); this->lastRequestTimestamp = this->timeProvider->getSyncedTimestamp(); return this->audioChunkManager->registerNewChunk(this->audioChunkSequence - 1, audioKey, startPos, endPos); } diff --git a/components/spotify/cspot/src/PlainConnection.cpp b/components/spotify/cspot/src/PlainConnection.cpp index e1e86d77..a9d8d4f0 100644 --- a/components/spotify/cspot/src/PlainConnection.cpp +++ b/components/spotify/cspot/src/PlainConnection.cpp @@ -1,10 +1,26 @@ #include "PlainConnection.h" #include +#ifdef _WIN32 +#include +#else #include +#endif #include #include "Logger.h" +static int getErrno() +{ +#ifdef _WIN32 + int code = WSAGetLastError(); + if (code == WSAETIMEDOUT) return ETIMEDOUT; + if (code == WSAEINTR) return EINTR; + return code; +#else + return errno; +#endif +} + PlainConnection::PlainConnection() { this->apSock = -1; @@ -46,11 +62,15 @@ void PlainConnection::connectToAp(std::string apAddress) (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) != -1) { +#ifdef _WIN32 + uint32_t tv = 3000; +#else struct timeval tv; tv.tv_sec = 3; tv.tv_usec = 0; - setsockopt(this->apSock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv); - setsockopt(this->apSock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv); +#endif + setsockopt(this->apSock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv); + setsockopt(this->apSock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof tv); int flag = 1; setsockopt(this->apSock, /* socket affected */ @@ -75,7 +95,6 @@ std::vector PlainConnection::recvPacket() // Read packet size auto sizeData = readBlock(4); uint32_t packetSize = ntohl(extract(sizeData, 0)); - // Read actual data auto data = readBlock(packetSize - 4); sizeData.insert(sizeData.end(), data.begin(), data.end()); @@ -110,9 +129,9 @@ std::vector PlainConnection::readBlock(size_t size) while (idx < size) { READ: - if ((n = recv(this->apSock, &buf[idx], size - idx, 0)) <= 0) + if ((n = recv(this->apSock, (char*) &buf[idx], size - idx, 0)) <= 0) { - switch (errno) + switch (getErrno()) { case EAGAIN: case ETIMEDOUT: @@ -145,9 +164,9 @@ size_t PlainConnection::writeBlock(const std::vector &data) while (idx < data.size()) { WRITE: - if ((n = send(this->apSock, &data[idx], data.size() - idx < 64 ? data.size() - idx : 64, 0)) <= 0) + if ((n = send(this->apSock, (char*) &data[idx], data.size() - idx < 64 ? data.size() - idx : 64, 0)) <= 0) { - switch (errno) + switch (getErrno()) { case EAGAIN: case ETIMEDOUT: diff --git a/components/spotify/cspot/src/Shannon.cpp b/components/spotify/cspot/src/Shannon.cpp index c59d56c6..67e55cd5 100644 --- a/components/spotify/cspot/src/Shannon.cpp +++ b/components/spotify/cspot/src/Shannon.cpp @@ -9,20 +9,16 @@ using std::size_t; static inline uint32_t rotl(uint32_t n, unsigned int c) { - const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. - // assert ( (c<=mask) &&"rotate by type width or more"); - c &= mask; - return (n << c) | (n >> ((-c) & mask)); + c &= sizeof(n) * CHAR_BIT - 1; + return (n << c) | (n >> (sizeof(n)*CHAR_BIT-c)); } static inline uint32_t rotr(uint32_t n, unsigned int c) { - const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); - // assert ( (c<=mask) &&"rotate by type width or more"); - c &= mask; - return (n >> c) | (n << ((-c) & mask)); + c &= sizeof(n) * CHAR_BIT - 1; + return (n >> c) | (n << (sizeof(n)*CHAR_BIT-c)); } uint32_t Shannon::sbox1(uint32_t w) diff --git a/components/spotify/cspot/src/SpircController.cpp b/components/spotify/cspot/src/SpircController.cpp index 0e4d6e0e..715c78d7 100644 --- a/components/spotify/cspot/src/SpircController.cpp +++ b/components/spotify/cspot/src/SpircController.cpp @@ -108,7 +108,7 @@ void SpircController::prevSong() { void SpircController::handleFrame(std::vector &data) { pb_release(Frame_fields, &state->remoteFrame); pbDecode(state->remoteFrame, Frame_fields, data); - +//CSPOT_LOG(info, "FRAME RECEIVED %d", (int) state->remoteFrame.typ); switch (state->remoteFrame.typ) { case MessageType_kMessageTypeNotify: { CSPOT_LOG(debug, "Notify frame"); diff --git a/components/spotify/cspot/src/SpotifyTrack.cpp b/components/spotify/cspot/src/SpotifyTrack.cpp index c46bac23..f03b5089 100644 --- a/components/spotify/cspot/src/SpotifyTrack.cpp +++ b/components/spotify/cspot/src/SpotifyTrack.cpp @@ -1,5 +1,7 @@ #include "SpotifyTrack.h" +#ifndef _WIN32 #include "unistd.h" +#endif #include "MercuryManager.h" #include #include "CspotAssert.h" diff --git a/components/spotify/cspot/src/ZeroconfAuthenticator.cpp b/components/spotify/cspot/src/ZeroconfAuthenticator.cpp index e4500df4..23a17285 100644 --- a/components/spotify/cspot/src/ZeroconfAuthenticator.cpp +++ b/components/spotify/cspot/src/ZeroconfAuthenticator.cpp @@ -1,14 +1,24 @@ #include "ZeroconfAuthenticator.h" #include "JSONObject.h" #include +#ifndef _WIN32 #include +#else +#include +#pragma comment(lib, "IPHLPAPI.lib") +#endif #include #include #include "Logger.h" +#include "CspotAssert.h" #include "ConfigJSON.h" // provide weak deviceId (see ConstantParameters.h) +#if _MSC_VER +char deviceId[] = "142137fd329622137a14901634264e6f332e2411"; +#else char deviceId[] __attribute__((weak)) = "142137fd329622137a14901634264e6f332e2411"; +#endif ZeroconfAuthenticator::ZeroconfAuthenticator(authCallback callback, std::shared_ptr httpServer) { this->gotBlobCallback = callback; @@ -17,15 +27,43 @@ ZeroconfAuthenticator::ZeroconfAuthenticator(authCallback callback, std::shared_ this->crypto = std::make_unique(); this->crypto->dhInit(); this->server = httpServer; + +#ifdef _WIN32 + char hostname[128]; + gethostname(hostname, sizeof(hostname)); + + struct sockaddr_in* host = NULL; + ULONG size = sizeof(IP_ADAPTER_ADDRESSES) * 32; + IP_ADAPTER_ADDRESSES* adapters = (IP_ADAPTER_ADDRESSES*) malloc(size); + int ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, 0, adapters, &size); + + for (PIP_ADAPTER_ADDRESSES adapter = adapters; adapter && !host; adapter = adapter->Next) { + if (adapter->TunnelType == TUNNEL_TYPE_TEREDO) continue; + if (adapter->OperStatus != IfOperStatusUp) continue; + + for (IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; unicast; + unicast = unicast->Next) { + if (adapter->FirstGatewayAddress && unicast->Address.lpSockaddr->sa_family == AF_INET) { + host = (struct sockaddr_in*)unicast->Address.lpSockaddr; + BELL_LOG(info, "mdns", "mDNS on interface %s", inet_ntoa(host->sin_addr)); + this->service = mdnsd_start(host->sin_addr, false); + break; + } + } + } + + CSPOT_ASSERT(this->service, "can't start mDNS service"); + mdnsd_set_hostname(this->service, hostname, host->sin_addr); +#endif } void ZeroconfAuthenticator::registerHandlers() { // Make it discoverable for spoti clients registerZeroconf(); - auto getInfoHandler = [this](bell::HTTPRequest& request) { + auto getInfoHandler = [this](std::unique_ptr request) { CSPOT_LOG(info, "Got request for info"); bell::HTTPResponse response = { - .connectionFd = request.connection, + .connectionFd = request->connection, .status = 200, .body = this->buildJsonInfo(), .contentType = "application/json", @@ -33,7 +71,7 @@ void ZeroconfAuthenticator::registerHandlers() { server->respond(response); }; - auto addUserHandler = [this](bell::HTTPRequest& request) { + auto addUserHandler = [this](std::unique_ptr request) { BELL_LOG(info, "http", "Got request for adding user"); bell::JSONObject obj; obj["status"] = 101; @@ -41,15 +79,15 @@ void ZeroconfAuthenticator::registerHandlers() { obj["statusString"] = "ERROR-OK"; bell::HTTPResponse response = { - .connectionFd = request.connection, + .connectionFd = request->connection, .status = 200, .body = obj.toString(), .contentType = "application/json", }; server->respond(response); - auto correctBlob = this->getParameterFromUrlEncoded(request.body, "blob"); - this->handleAddUser(request.queryParams); + auto correctBlob = this->getParameterFromUrlEncoded(request->body, "blob"); + this->handleAddUser(request->queryParams); }; BELL_LOG(info, "cspot", "Zeroconf registering handlers"); @@ -62,12 +100,18 @@ void ZeroconfAuthenticator::registerZeroconf() const char* service = "_spotify-connect._tcp"; #ifdef ESP_PLATFORM - mdns_txt_item_t serviceTxtData[3] = { - {"VERSION", "1.0"}, - {"CPath", "/spotify_info"}, - {"Stack", "SP"} }; - mdns_service_add("cspot", "_spotify-connect", "_tcp", this->server->serverPort, serviceTxtData, 3); - + mdns_txt_item_t serviceTxtData[3] = { + {"VERSION", "1.0"}, + {"CPath", "/spotify_info"}, + {"Stack", "SP"} }; + mdns_service_add("cspot", "_spotify-connect", "_tcp", this->server->serverPort, serviceTxtData, 3); +#elif _WIN32 + const char *serviceTxtData[] = { + "VERSION=1.0", + "CPath=/spotify_info", + "Stack=SP", + NULL }; + mdnsd_register_svc(this->service, "cspot", "_spotify-connect._tcp.local", this->server->serverPort, NULL, serviceTxtData); #else DNSServiceRef ref = NULL; TXTRecordRef txtRecord;