mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 03:57:07 +03:00
idf overriding method to bring back SPDIF and fix SPI + new CSPOT (which crashes)
This commit is contained in:
@@ -3,26 +3,18 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
idf_component_register(
|
||||
SRC_DIRS .
|
||||
INCLUDE_DIRS . "cspot/include" "cspot/bell/include" "cspot/protos"
|
||||
PRIV_REQUIRES mbedtls mdns nvs_flash platform_config services esp_http_server tools
|
||||
INCLUDE_DIRS . "cspot/include" "cspot/bell/include"
|
||||
PRIV_REQUIRES mbedtls mdns nvs_flash platform_config services esp_http_server tools codecs
|
||||
LDFRAGMENTS "linker.lf"
|
||||
)
|
||||
|
||||
include_directories("../codecs/inc")
|
||||
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 1)
|
||||
set(BELL_TREMOR_EXTERNAL "idf::codecs")
|
||||
set(BELL_CJSON_EXTERNAL "idf::json")
|
||||
set(BELL_DISABLE_CODECS ON)
|
||||
set(BELL_EXTERNAL_TREMOR "idf::codecs")
|
||||
set(BELL_EXTERNAL_CJSON "idf::json")
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cspot ${CMAKE_CURRENT_BINARY_DIR}/cspot)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE cspot ${EXTRA_REQ_LIBS})
|
||||
|
||||
#if (!WIN32)
|
||||
# message(${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh)
|
||||
# execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
#endif ()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ static void cspotTask(void *pvParameters) {
|
||||
ESP_LOGW(TAG, "Cannot load config, using default");
|
||||
|
||||
configMan->deviceName = cspot.name;
|
||||
configMan->format = AudioFormat::OGG_VORBIS_160;
|
||||
configMan->format = AudioFormat_OGG_VORBIS_160;
|
||||
configMan->volume = 32767;
|
||||
|
||||
configMan->save();
|
||||
@@ -80,7 +80,7 @@ static void cspotTask(void *pvParameters) {
|
||||
// safely load config now
|
||||
configMan->load();
|
||||
if (!configMan->deviceName.length()) configMan->deviceName = cspot.name;
|
||||
ESP_LOGI(TAG, "Started CSpot with %s (bitrate %d)", configMan->deviceName.c_str(), configMan->format == AudioFormat::OGG_VORBIS_320 ? 320 : (configMan->format == AudioFormat::OGG_VORBIS_160 ? 160 : 96));
|
||||
ESP_LOGI(TAG, "Started CSpot with %s (bitrate %d)", configMan->deviceName.c_str(), configMan->format == AudioFormat_OGG_VORBIS_320 ? 320 : (configMan->format == AudioFormat_OGG_VORBIS_160 ? 160 : 96));
|
||||
|
||||
// All we do here is notify the task to start the mercury loop
|
||||
auto createPlayerCallback = [](std::shared_ptr<LoginBlob> blob) {
|
||||
|
||||
@@ -1,66 +1,40 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
project(cspot)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.9)
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
# Configurable options
|
||||
set(CSPOT_EXTERNAL_BELL "" CACHE STRING "External bell library target name, optional")
|
||||
|
||||
# CMake options
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Main library sources
|
||||
file(GLOB SOURCES "src/*.cpp" "src/*.c")
|
||||
|
||||
if (NOT DEFINED ${USE_EXTERNAL_BELL})
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/bell ${CMAKE_CURRENT_BINARY_DIR}/bell)
|
||||
# Use externally specified bell library or the submodule
|
||||
if(CSPOT_EXTERNAL_BELL)
|
||||
list(APPEND EXTRA_LIBS ${CSPOT_EXTERNAL_BELL})
|
||||
else()
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/bell)
|
||||
list(APPEND EXTRA_LIBS bell)
|
||||
endif()
|
||||
# Add platform specific sources
|
||||
# if(${ESP_PLATFORM})
|
||||
# file(GLOB ESP_PLATFORM_SOURCES "src/platform/esp/*.cpp" "src/platform/esp/*.c")
|
||||
# set(SOURCES ${SOURCES} ${ESP_PLATFORM_SOURCES} )
|
||||
# endif()
|
||||
|
||||
# if(UNIX)
|
||||
# file(GLOB UNIX_PLATFORM_SOURCES "src/platform/unix/*.cpp" "src/platform/unix/*.c")
|
||||
# set(SOURCES ${SOURCES} ${UNIX_PLATFORM_SOURCES} )
|
||||
# endif()
|
||||
|
||||
# if(APPLE)
|
||||
# file(GLOB APPLE_PLATFORM_SOURCES "src/platform/apple/*.cpp" "src/platform/apple/*.c")
|
||||
# set(SOURCES ${SOURCES} ${APPLE_PLATFORM_SOURCES} )
|
||||
# endif()
|
||||
|
||||
# if(UNIX AND NOT APPLE)
|
||||
# # file(GLOB LINUX_PLATFORM_SOURCES "src/platform/linux/*.cpp" "src/platform/linux/*.c")
|
||||
# # set(SOURCES ${SOURCES} ${LINUX_PLATFORM_SOURCES} )
|
||||
# # endif()
|
||||
|
||||
|
||||
# if(${ESP_PLATFORM})
|
||||
# list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoOpenSSL.cpp) # use MBedTLS
|
||||
# idf_build_set_property(COMPILE_DEFINITIONS "-DCSPOT_USE_MBEDTLS" APPEND)
|
||||
# set(EXTRA_REQ_LIBS idf::mbedtls idf::pthread idf::mdns)
|
||||
# add_definitions(-Wunused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation)
|
||||
# else()
|
||||
# list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoMbedTLS.cpp) # use OpenSSL
|
||||
# find_package(OpenSSL REQUIRED)
|
||||
# if(OPENSSL_FOUND)
|
||||
# set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
# endif()
|
||||
# set(EXTRA_REQ_LIBS OpenSSL::Crypto Threads::Threads)
|
||||
# set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
# find_package(Threads REQUIRED)
|
||||
# endif()
|
||||
|
||||
# Add Apple Bonjour compatibility library for Linux
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} dns_sd) # add apple bonjur compatibility library for linux
|
||||
list(APPEND EXTRA_LIBS dns_sd)
|
||||
# TODO: migrate from this to native linux mDNS
|
||||
endif()
|
||||
set(NANOPB_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(PROTOS protobuf/authentication.proto protobuf/mercury.proto protobuf/keyexchange.proto protobuf/spirc.proto protobuf/metadata.proto)
|
||||
message(${PROTOS})
|
||||
message("building protobuf")
|
||||
message(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
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)
|
||||
|
||||
include_directories("include")
|
||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||
include_directories("protos")
|
||||
|
||||
message(${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh)
|
||||
execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/cmake/generate_protos.sh" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
set(SOURCES ${SOURCES} "protos/AnyRefImpl.cpp" "protos/ReflectTypeInfo.cpp")
|
||||
|
||||
add_library(cspot STATIC ${SOURCES})
|
||||
target_link_libraries(cspot PRIVATE bell ${EXTRA_REQ_LIBS})
|
||||
|
||||
target_include_directories(cspot PUBLIC "include" "protos" bell ${EXTRA_REQ_LIBS} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_library(cspot STATIC ${SOURCES} ${PROTO_SRCS})
|
||||
# PUBLIC to propagate includes from bell to cspot dependents
|
||||
target_link_libraries(cspot PUBLIC ${EXTRA_LIBS})
|
||||
target_include_directories(cspot PUBLIC "include" ${CMAKE_CURRENT_BINARY_DIR} ${NANOPB_INCLUDE_DIRS})
|
||||
|
||||
@@ -1,80 +1,100 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
project(bell)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.9)
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
# Configurable options
|
||||
option(BELL_DISABLE_CODECS "Disable libhelix AAC and MP3 codecs" OFF)
|
||||
#set(BELL_EXTERNAL_CJSON "" CACHE STRING "External cJSON library target name, optional")
|
||||
#set(BELL_EXTERNAL_TREMOR "" CACHE STRING "External tremor library target name, optional")
|
||||
|
||||
file(GLOB SOURCES "src/*.cpp" "src/*.c")
|
||||
add_definitions(-DPB_ENABLE_MALLOC)
|
||||
|
||||
# Include nanoPB library
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/nanopb/extra)
|
||||
find_package(Nanopb REQUIRED)
|
||||
include_directories(${NANOPB_INCLUDE_DIRS})
|
||||
|
||||
# CMake options
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
add_definitions(-DUSE_DEFAULT_STDLIB=1)
|
||||
|
||||
# Main library sources
|
||||
file(GLOB SOURCES "src/*.cpp" "src/*.c" "nanopb/*.c")
|
||||
|
||||
# Add platform specific sources
|
||||
|
||||
if(${ESP_PLATFORM})
|
||||
if(ESP_PLATFORM)
|
||||
file(GLOB ESP_PLATFORM_SOURCES "src/platform/esp/*.cpp" "src/platform/esp/*.c" "src/asm/biquad_f32_ae32.S")
|
||||
set(SOURCES ${SOURCES} ${ESP_PLATFORM_SOURCES} )
|
||||
list(APPEND SOURCES ${ESP_PLATFORM_SOURCES})
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
file(GLOB UNIX_PLATFORM_SOURCES "src/platform/unix/*.cpp" "src/platform/linux/TLSSocket.cpp" "src/platform/unix/*.c")
|
||||
set(SOURCES ${SOURCES} ${UNIX_PLATFORM_SOURCES} )
|
||||
list(APPEND SOURCES ${UNIX_PLATFORM_SOURCES})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
file(GLOB APPLE_PLATFORM_SOURCES "src/platform/apple/*.cpp" "src/platform/linux/TLSSocket.cpp" "src/platform/apple/*.c")
|
||||
set(SOURCES ${SOURCES} ${APPLE_PLATFORM_SOURCES} )
|
||||
list(APPEND SOURCES ${APPLE_PLATFORM_SOURCES})
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
file(GLOB LINUX_PLATFORM_SOURCES "src/platform/linux/*.cpp" "src/platform/linux/*.c")
|
||||
set(SOURCES ${SOURCES} ${LINUX_PLATFORM_SOURCES} )
|
||||
list(APPEND SOURCES ${LINUX_PLATFORM_SOURCES})
|
||||
endif()
|
||||
|
||||
if(${ESP_PLATFORM})
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoOpenSSL.cpp) # use MBedTLS
|
||||
if(ESP_PLATFORM)
|
||||
# Use MBedTLS on ESP32
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoOpenSSL.cpp)
|
||||
idf_build_set_property(COMPILE_DEFINITIONS "-DBELL_USE_MBEDTLS" APPEND)
|
||||
set(EXTRA_REQ_LIBS idf::mbedtls idf::pthread idf::mdns)
|
||||
list(APPEND EXTRA_LIBS idf::mbedtls idf::pthread idf::mdns)
|
||||
add_definitions(-Wunused-const-variable -Wchar-subscripts -Wunused-label -Wmaybe-uninitialized -Wmisleading-indentation)
|
||||
else()
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoMbedTLS.cpp) # use OpenSSL
|
||||
# Use OpenSSL elsewhere
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/CryptoMbedTLS.cpp)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
if(OPENSSL_FOUND)
|
||||
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
endif()
|
||||
set(EXTRA_REQ_LIBS OpenSSL::Crypto OpenSSL::SSL Threads::Threads)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
list(APPEND EXTRA_LIBS OpenSSL::Crypto OpenSSL::SSL Threads::Threads)
|
||||
endif()
|
||||
|
||||
if (BELL_DISABLE_CODECS)
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/DecoderGlobals.cpp) # use OpenSSL
|
||||
add_definitions(-DBELL_DISABLE_CODECS)
|
||||
if(BELL_DISABLE_CODECS)
|
||||
list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/DecoderGlobals.cpp)
|
||||
else()
|
||||
set(EXTRA_INC ${EXTRA_INC} "libhelix-aac" "libhelix-mp3")
|
||||
set(SOURCES ${SOURCES} "libhelix-aac/aacdec.c" "libhelix-aac/aactabs.c" "libhelix-aac/bitstream.c" "libhelix-aac/buffers.c" "libhelix-aac/dct4.c" "libhelix-aac/decelmnt.c" "libhelix-aac/dequant.c" "libhelix-aac/fft.c" "libhelix-aac/filefmt.c" "libhelix-aac/huffman.c" "libhelix-aac/hufftabs.c" "libhelix-aac/imdct.c" "libhelix-aac/noiseless.c" "libhelix-aac/pns.c" "libhelix-aac/sbr.c" "libhelix-aac/sbrfft.c" "libhelix-aac/sbrfreq.c" "libhelix-aac/sbrhfadj.c" "libhelix-aac/sbrhfgen.c" "libhelix-aac/sbrhuff.c" "libhelix-aac/sbrimdct.c" "libhelix-aac/sbrmath.c" "libhelix-aac/sbrqmf.c" "libhelix-aac/sbrside.c" "libhelix-aac/sbrtabs.c" "libhelix-aac/stproc.c" "libhelix-aac/tns.c" "libhelix-aac/trigtabs.c")
|
||||
set(SOURCES ${SOURCES} "libhelix-mp3/bitstream.c" "libhelix-mp3/buffers.c" "libhelix-mp3/dct32.c" "libhelix-mp3/dequant.c" "libhelix-mp3/dqchan.c" "libhelix-mp3/huffman.c" "libhelix-mp3/hufftabs.c" "libhelix-mp3/imdct.c" "libhelix-mp3/mp3dec.c" "libhelix-mp3/mp3tabs.c" "libhelix-mp3/polyphase.c" "libhelix-mp3/scalfact.c" "libhelix-mp3/stproc.c" "libhelix-mp3/subband.c" "libhelix-mp3/trigtabs.c")
|
||||
file(GLOB LIBHELIX_AAC_SOURCES "libhelix-aac/*.c")
|
||||
file(GLOB LIBHELIX_MP3_SOURCES "libhelix-mp3/*.c")
|
||||
list(APPEND EXTRA_INCLUDES "libhelix-aac" "libhelix-mp3")
|
||||
list(APPEND SOURCES ${LIBHELIX_MP3_SOURCES} ${LIBHELIX_AAC_SOURCES})
|
||||
|
||||
if(CYGWIN)
|
||||
# Both Cygwin and ESP are Unix-like so this seems to work (or, at least, compile)
|
||||
set_source_files_properties(src/DecoderGlobals.cpp PROPERTIES COMPILE_FLAGS -DESP_PLATFORM)
|
||||
set_source_files_properties(${LIBHELIX_AAC_SOURCES} PROPERTIES COMPILE_FLAGS -DESP_PLATFORM)
|
||||
set_source_files_properties(${LIBHELIX_MP3_SOURCES} PROPERTIES COMPILE_FLAGS -DESP_PLATFORM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} dns_sd) # add apple bonjur compatibility library for linux
|
||||
# TODO: migrate from this to native linux mDNS
|
||||
endif()
|
||||
add_definitions( -DUSE_DEFAULT_STDLIB=1)
|
||||
|
||||
if (BELL_CJSON_EXTERNAL)
|
||||
message("Using external cJSON")
|
||||
set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} ${BELL_CJSON_EXTERNAL})
|
||||
else()
|
||||
set(EXTRA_INC ${EXTRA_INC} "cJSON")
|
||||
set(SOURCES ${SOURCES} "cJSON/cJSON.c")
|
||||
endif()
|
||||
|
||||
if (BELL_TREMOR_EXTERNAL)
|
||||
message("Using external TREMOR")
|
||||
set(EXTRA_REQ_LIBS ${EXTRA_REQ_LIBS} ${BELL_TREMOR_EXTERNAL})
|
||||
if(BELL_EXTERNAL_CJSON)
|
||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
|
||||
else()
|
||||
set(EXTRA_INC ${EXTRA_INC} "tremor")
|
||||
set(SOURCES ${SOURCES} "tremor/mdct.c" "tremor/dsp.c" "tremor/info.c" "tremor/misc.c" "tremor/floor1.c" "tremor/floor0.c" "tremor/vorbisfile.c" "tremor/res012.c" "tremor/mapping0.c" "tremor/codebook.c" "tremor/framing.c" "tremor/bitwise.c" "tremor/floor_lookup.c")
|
||||
list(APPEND EXTRA_INCLUDES "cJSON")
|
||||
list(APPEND SOURCES "cJSON/cJSON.c")
|
||||
endif()
|
||||
|
||||
if(BELL_EXTERNAL_TREMOR)
|
||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_TREMOR})
|
||||
else()
|
||||
file(GLOB TREMOR_SOURCES "tremor/*.c")
|
||||
list(REMOVE_ITEM TREMOR_SOURCES "tremor/ivorbisfile_example.c")
|
||||
list(APPEND EXTRA_INCLUDES "tremor")
|
||||
list(APPEND SOURCES ${TREMOR_SOURCES})
|
||||
endif()
|
||||
|
||||
add_library(bell STATIC ${SOURCES})
|
||||
target_link_libraries(bell PRIVATE ${EXTRA_REQ_LIBS})
|
||||
target_include_directories(bell PUBLIC "include" "protos" ${EXTRA_INC} ${EXTRA_REQ_LIBS} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
message(${NANOPB_INCLUDE_DIRS})
|
||||
# PUBLIC to propagate esp-idf includes to bell dependents
|
||||
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
|
||||
target_include_directories(bell PUBLIC "include" ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace bell {
|
||||
|
||||
@@ -16,6 +17,7 @@ class ResponseReader {
|
||||
|
||||
virtual size_t getTotalSize() = 0;
|
||||
virtual size_t read(char *buffer, size_t size) = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
class FileResponseReader : public ResponseReader {
|
||||
@@ -34,6 +36,10 @@ class FileResponseReader : public ResponseReader {
|
||||
return fread(buffer, 1, size, file);
|
||||
}
|
||||
|
||||
void close() {
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
size_t getTotalSize() { return fileSize; }
|
||||
};
|
||||
|
||||
@@ -43,6 +49,7 @@ struct HTTPRequest {
|
||||
std::map<std::string, std::string> urlParams;
|
||||
std::map<std::string, std::string> queryParams;
|
||||
std::string body;
|
||||
std::string url;
|
||||
int handlerId;
|
||||
int connection;
|
||||
};
|
||||
|
||||
@@ -9,11 +9,13 @@ namespace bell {
|
||||
Socket() {};
|
||||
virtual ~Socket() = default;
|
||||
|
||||
virtual void open(std::string url) = 0;
|
||||
void open(const std::string &url);
|
||||
virtual void open(std::string host, uint16_t port) = 0;
|
||||
virtual size_t poll() = 0;
|
||||
virtual size_t write(uint8_t* buf, size_t len) = 0;
|
||||
virtual size_t read(uint8_t* buf, size_t len) = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <fstream>
|
||||
#include <netinet/tcp.h>
|
||||
#include <BellLogger.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
namespace bell
|
||||
{
|
||||
@@ -32,50 +33,35 @@ namespace bell
|
||||
close();
|
||||
};
|
||||
|
||||
void open(std::string url)
|
||||
void open(std::string host, uint16_t port)
|
||||
{
|
||||
// remove https or http from url
|
||||
url.erase(0, url.find("://") + 3);
|
||||
|
||||
// split by first "/" in url
|
||||
std::string hostUrl = url.substr(0, url.find('/'));
|
||||
std::string pathUrl = url.substr(url.find('/'));
|
||||
|
||||
std::string portString = "80";
|
||||
|
||||
// check if hostUrl contains ':'
|
||||
if (hostUrl.find(':') != std::string::npos)
|
||||
{
|
||||
// split by ':'
|
||||
std::string host = hostUrl.substr(0, hostUrl.find(':'));
|
||||
portString = hostUrl.substr(hostUrl.find(':') + 1);
|
||||
hostUrl = host;
|
||||
}
|
||||
|
||||
int err;
|
||||
int domain = AF_INET;
|
||||
int socketType = SOCK_STREAM;
|
||||
|
||||
addrinfo hints, *addr;
|
||||
struct addrinfo hints{}, *addr;
|
||||
//fine-tune hints according to which socket you want to open
|
||||
hints.ai_family = domain;
|
||||
hints.ai_socktype = socketType;
|
||||
hints.ai_protocol = IPPROTO_IP; // no enum : possible value can be read in /etc/protocols
|
||||
hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
|
||||
|
||||
BELL_LOG(info, "http", "%s %s", hostUrl.c_str(), portString.c_str());
|
||||
BELL_LOG(info, "http", "%s %d", host.c_str(), port);
|
||||
|
||||
if (getaddrinfo(hostUrl.c_str(), portString.c_str(), &hints, &addr) != 0)
|
||||
{
|
||||
BELL_LOG(error, "webradio", "DNS lookup error");
|
||||
char portStr[6];
|
||||
sprintf(portStr, "%u", port);
|
||||
err = getaddrinfo(host.c_str(), portStr, &hints, &addr);
|
||||
if (err != 0) {
|
||||
throw std::runtime_error("Resolve failed");
|
||||
}
|
||||
|
||||
sockFd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
|
||||
if (connect(sockFd, addr->ai_addr, addr->ai_addrlen) < 0)
|
||||
err = connect(sockFd, addr->ai_addr, addr->ai_addrlen);
|
||||
if (err < 0)
|
||||
{
|
||||
close();
|
||||
BELL_LOG(error, "http", "Could not connect to %s", url.c_str());
|
||||
BELL_LOG(error, "http", "Could not connect to %s. Error %d", host.c_str(), errno);
|
||||
throw std::runtime_error("Resolve failed");
|
||||
}
|
||||
|
||||
@@ -97,6 +83,12 @@ namespace bell
|
||||
return send(sockFd, buf, len, 0);
|
||||
}
|
||||
|
||||
size_t poll() {
|
||||
int value;
|
||||
ioctl(sockFd, FIONREAD, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (!isClosed) {
|
||||
::close(sockFd);
|
||||
@@ -107,4 +99,4 @@ namespace bell
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -58,10 +58,11 @@ public:
|
||||
TLSSocket();
|
||||
~TLSSocket() { close(); };
|
||||
|
||||
void open(std::string host);
|
||||
void open(std::string host, uint16_t port);
|
||||
|
||||
size_t read(uint8_t *buf, size_t len);
|
||||
size_t write(uint8_t *buf, size_t len);
|
||||
size_t poll();
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
@@ -65,7 +65,7 @@ void bell::HTTPServer::registerHandler(RequestType requestType,
|
||||
}
|
||||
|
||||
void bell::HTTPServer::listen() {
|
||||
BELL_LOG(info, "http", "Starting configuration server at port %d",
|
||||
BELL_LOG(info, "http", "Starting server at port %d",
|
||||
this->serverPort);
|
||||
|
||||
// setup address
|
||||
@@ -170,8 +170,8 @@ void bell::HTTPServer::readFromClient(int clientFd) {
|
||||
if (line.find("Content-Length: ") != std::string::npos) {
|
||||
conn.contentLength =
|
||||
std::stoi(line.substr(16, line.size() - 1));
|
||||
BELL_LOG(info, "http", "Content-Length: %d",
|
||||
conn.contentLength);
|
||||
//BELL_LOG(info, "http", "Content-Length: %d",
|
||||
// conn.contentLength);
|
||||
}
|
||||
if (line.size() == 0) {
|
||||
if (conn.contentLength != 0) {
|
||||
@@ -201,7 +201,7 @@ void bell::HTTPServer::writeResponseEvents(int connFd) {
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 200 OK\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Server: bell-http\r\n";
|
||||
stream << "Connection: keep-alive\r\n";
|
||||
stream << "Content-type: text/event-stream\r\n";
|
||||
stream << "Cache-Control: no-cache\r\n";
|
||||
@@ -229,7 +229,7 @@ void bell::HTTPServer::writeResponse(const HTTPResponse &response) {
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 " << response.status << " OK\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Server: bell-http\r\n";
|
||||
stream << "Connection: close\r\n";
|
||||
stream << "Content-type: " << response.contentType << "\r\n";
|
||||
|
||||
@@ -270,7 +270,6 @@ void bell::HTTPServer::writeResponse(const HTTPResponse &response) {
|
||||
} while (read > 0);
|
||||
}
|
||||
|
||||
BELL_LOG(info, "HTTP", "Closing connection");
|
||||
this->closeConnection(response.connectionFd);
|
||||
}
|
||||
|
||||
@@ -282,7 +281,7 @@ void bell::HTTPServer::redirectTo(const std::string & url, int connectionFd) {
|
||||
std::lock_guard lock(this->responseMutex);
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 301 Moved Permanently\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Server: bell-http\r\n";
|
||||
stream << "Connection: close\r\n";
|
||||
stream << "Location: " << url << "\r\n\r\n";
|
||||
auto responseStr = stream.str();
|
||||
@@ -314,11 +313,11 @@ std::map<std::string, std::string>
|
||||
bell::HTTPServer::parseQueryString(const std::string &queryString) {
|
||||
std::map<std::string, std::string> query;
|
||||
auto prefixedString = "&" + queryString;
|
||||
while (prefixedString.find("&") != std::string::npos) {
|
||||
auto keyStart = prefixedString.find("&");
|
||||
auto keyEnd = prefixedString.find("=");
|
||||
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);
|
||||
auto valueEnd = prefixedString.find('&', keyStart + 1);
|
||||
if (valueEnd == std::string::npos) {
|
||||
valueEnd = prefixedString.size();
|
||||
}
|
||||
@@ -336,12 +335,11 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
|
||||
int connectionFd) {
|
||||
std::map<std::string, std::string> pathParams;
|
||||
std::map<std::string, std::string> queryParams;
|
||||
BELL_LOG(info, "http", "URL %s", url.c_str());
|
||||
|
||||
if (url.find("OPTIONS /") != std::string::npos) {
|
||||
std::stringstream stream;
|
||||
stream << "HTTP/1.1 200 OK\r\n";
|
||||
stream << "Server: EUPHONIUM\r\n";
|
||||
stream << "Server: bell-http\r\n";
|
||||
stream << "Allow: OPTIONS, GET, HEAD, POST\r\n";
|
||||
stream << "Connection: close\r\n";
|
||||
stream << "Access-Control-Allow-Origin: *\r\n";
|
||||
@@ -377,9 +375,9 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
|
||||
continue;
|
||||
}
|
||||
|
||||
path = path.substr(0, path.find(" "));
|
||||
path = path.substr(0, path.find(' '));
|
||||
|
||||
if (path.find("?") != std::string::npos) {
|
||||
if (path.find('?') != std::string::npos) {
|
||||
auto urlEncodedSplit = splitUrl(path, '?');
|
||||
path = urlEncodedSplit[0];
|
||||
queryParams = this->parseQueryString(urlEncodedSplit[1]);
|
||||
@@ -406,19 +404,20 @@ void bell::HTTPServer::findAndHandleRoute(std::string &url, std::string &body,
|
||||
matches = false;
|
||||
}
|
||||
|
||||
if (routeSplit.back().find("*") != std::string::npos &&
|
||||
if (routeSplit.back().find('*') != std::string::npos &&
|
||||
urlSplit[1] == routeSplit[1]) {
|
||||
matches = true;
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
if (body.find("&") != std::string::npos) {
|
||||
if (body.find('&') != std::string::npos) {
|
||||
queryParams = this->parseQueryString(body);
|
||||
}
|
||||
|
||||
HTTPRequest req = {.urlParams = pathParams,
|
||||
.queryParams = queryParams,
|
||||
.body = body,
|
||||
.url = path,
|
||||
.handlerId = 0,
|
||||
.connection = connectionFd};
|
||||
|
||||
|
||||
@@ -21,25 +21,10 @@ bell::TLSSocket::TLSSocket()
|
||||
}
|
||||
}
|
||||
|
||||
void bell::TLSSocket::open(std::string url)
|
||||
void bell::TLSSocket::open(std::string hostUrl, uint16_t port)
|
||||
{
|
||||
// initialize
|
||||
int ret;
|
||||
url.erase(0, url.find("://") + 3);
|
||||
std::string hostUrl = url.substr(0, url.find('/'));
|
||||
std::string pathUrl = url.substr(url.find('/'));
|
||||
|
||||
std::string portString = "443";
|
||||
// check if hostUrl contains ':'
|
||||
if (hostUrl.find(':') != std::string::npos)
|
||||
{
|
||||
// split by ':'
|
||||
std::string host = hostUrl.substr(0, hostUrl.find(':'));
|
||||
portString = hostUrl.substr(hostUrl.find(':') + 1);
|
||||
hostUrl = host;
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_net_connect(&server_fd, hostUrl.c_str(), "443",
|
||||
if ((ret = mbedtls_net_connect(&server_fd, hostUrl.c_str(), std::to_string(port).c_str(),
|
||||
MBEDTLS_NET_PROTO_TCP)) != 0)
|
||||
{
|
||||
BELL_LOG(error, "http_tls", "failed! connect returned %d\n", ret);
|
||||
@@ -82,6 +67,10 @@ size_t bell::TLSSocket::write(uint8_t *buf, size_t len)
|
||||
return mbedtls_ssl_write(&ssl, buf, len);
|
||||
}
|
||||
|
||||
size_t bell::TLSSocket::poll() {
|
||||
return mbedtls_ssl_get_bytes_avail(&ssl);
|
||||
}
|
||||
|
||||
void bell::TLSSocket::close()
|
||||
{
|
||||
mbedtls_net_free(&server_fd);
|
||||
|
||||
@@ -11,7 +11,7 @@ bell::TLSSocket::TLSSocket() {
|
||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
}
|
||||
|
||||
void bell::TLSSocket::open(std::string url) {
|
||||
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
|
||||
@@ -23,21 +23,8 @@ void bell::TLSSocket::open(std::string url) {
|
||||
BIO_get_ssl(sbio, &ssl);
|
||||
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
url.erase(0, url.find("://") + 3);
|
||||
std::string hostUrl = url.substr(0, url.find('/'));
|
||||
std::string pathUrl = url.substr(url.find('/'));
|
||||
|
||||
std::string portString = "443";
|
||||
// check if hostUrl contains ':'
|
||||
if (hostUrl.find(':') != std::string::npos) {
|
||||
// split by ':'
|
||||
std::string host = hostUrl.substr(0, hostUrl.find(':'));
|
||||
portString = hostUrl.substr(hostUrl.find(':') + 1);
|
||||
hostUrl = host;
|
||||
}
|
||||
|
||||
BELL_LOG(info, "http_tls", "Connecting with %s", hostUrl.c_str());
|
||||
BIO_set_conn_hostname(sbio, std::string(hostUrl + ":443").c_str());
|
||||
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) {
|
||||
@@ -63,6 +50,10 @@ 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);
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
# Running
|
||||
Docker files
|
||||
------------
|
||||
|
||||
This folder contains docker files that are used in testing nanopb automatically
|
||||
on various platforms.
|
||||
|
||||
By default they take the newest master branch code from github.
|
||||
|
||||
To build tests for a single target, use for example:
|
||||
|
||||
docker build ubuntu1804
|
||||
|
||||
To build tests for all targets, use:
|
||||
|
||||
./build_all.sh
|
||||
|
||||
```sh
|
||||
$ go build -o protoc-gen-cpprefl . && protoc --plugin=protoc-gen-cpprefl=protoc-gen-cpprefl --cpprefl_out okon protos/*.proto --proto_path protos/
|
||||
```
|
||||
|
||||
Will get protos from `protos/` and output to `out/protobuf.h`
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include "FileHelper.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "protobuf/metadata.pb.h"
|
||||
|
||||
class ConfigJSON
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include "Crypto.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "protobuf/authentication.pb.h"
|
||||
|
||||
class LoginBlob
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "MercuryResponse.h"
|
||||
#include "Packet.h"
|
||||
#include "Utils.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "MercuryManager.h"
|
||||
#include "AudioChunk.h"
|
||||
#include "AudioChunkManager.h"
|
||||
@@ -19,7 +18,8 @@
|
||||
#include "platform/WrappedSemaphore.h"
|
||||
#include "TimeProvider.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include <NanoPBHelper.h>
|
||||
#include "protobuf/mercury.pb.h"
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
|
||||
@@ -56,6 +56,7 @@ extern std::map<MercuryType, std::string> MercuryTypeMap;
|
||||
class MercuryManager : public bell::Task
|
||||
{
|
||||
private:
|
||||
Header tempMercuryHeader;
|
||||
std::map<uint64_t, mercuryCallback> callbacks;
|
||||
std::mutex reconnectionMutex;
|
||||
std::mutex runningMutex;
|
||||
@@ -76,6 +77,7 @@ private:
|
||||
void runTask();
|
||||
public:
|
||||
MercuryManager(std::unique_ptr<Session> session);
|
||||
~MercuryManager();
|
||||
voidCallback reconnectedCallback;
|
||||
uint16_t audioChunkSequence;
|
||||
std::shared_ptr<TimeProvider> timeProvider;
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "ProtoHelper.h"
|
||||
#include <NanoPBHelper.h>
|
||||
#include "protobuf/mercury.pb.h"
|
||||
#include "Utils.h"
|
||||
|
||||
typedef std::vector<std::vector<uint8_t>> mercuryParts;
|
||||
@@ -18,6 +19,7 @@ private:
|
||||
std::vector<uint8_t> data;
|
||||
public:
|
||||
MercuryResponse(std::vector<uint8_t> &data);
|
||||
~MercuryResponse();
|
||||
void decodeHeader();
|
||||
Header mercuryHeader;
|
||||
uint8_t flags;
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "ProtoHelper.h"
|
||||
#include "Utils.h"
|
||||
#include "TimeProvider.h"
|
||||
#include "ConstantParameters.h"
|
||||
#include "CspotAssert.h"
|
||||
#include "TrackReference.h"
|
||||
#include "ConfigJSON.h"
|
||||
#include <NanoPBHelper.h>
|
||||
#include "protobuf/spirc.pb.h"
|
||||
|
||||
enum class PlaybackState {
|
||||
Playing,
|
||||
@@ -30,8 +31,8 @@ private:
|
||||
|
||||
void addCapability(CapabilityType typ, int intValue = -1, std::vector<std::string> stringsValue = std::vector<std::string>());
|
||||
public:
|
||||
Frame innerFrame = Frame();
|
||||
Frame remoteFrame = Frame();
|
||||
Frame innerFrame;
|
||||
Frame remoteFrame;
|
||||
|
||||
/**
|
||||
* @brief Player state represents the current state of player.
|
||||
@@ -41,6 +42,8 @@ public:
|
||||
* @param timeProvider synced time provider
|
||||
*/
|
||||
PlayerState(std::shared_ptr<TimeProvider> timeProvider);
|
||||
|
||||
~PlayerState();
|
||||
|
||||
/**
|
||||
* @brief Updates state according to current playback state.
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#include "Packet.h"
|
||||
#include "ConstantParameters.h"
|
||||
#include "Crypto.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "NanoPBHelper.h"
|
||||
#include "protobuf/authentication.pb.h"
|
||||
#include "protobuf/keyexchange.pb.h"
|
||||
|
||||
#define SPOTIFY_VERSION 0x10800000000
|
||||
#define LOGIN_REQUEST_COMMAND 0xAB
|
||||
@@ -38,6 +40,7 @@ private:
|
||||
|
||||
public:
|
||||
Session();
|
||||
~Session();
|
||||
std::shared_ptr<ShannonConnection> shanConn;
|
||||
std::shared_ptr<LoginBlob> authBlob;
|
||||
void connect(std::unique_ptr<PlainConnection> connection);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <functional>
|
||||
#include "Utils.h"
|
||||
#include "MercuryManager.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "Session.h"
|
||||
#include "PlayerState.h"
|
||||
#include "SpotifyTrack.h"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "MercuryManager.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "Utils.h"
|
||||
#include "MercuryResponse.h"
|
||||
#include <fstream>
|
||||
@@ -13,6 +12,8 @@
|
||||
#include <functional>
|
||||
#include "ChunkedAudioStream.h"
|
||||
#include "TrackReference.h"
|
||||
#include "NanoPBHelper.h"
|
||||
#include "protobuf/metadata.pb.h"
|
||||
#include <cassert>
|
||||
|
||||
struct TrackInfo {
|
||||
@@ -33,7 +34,7 @@ private:
|
||||
void episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused);
|
||||
void requestAudioKey(std::vector<uint8_t> fileId, std::vector<uint8_t> trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused);
|
||||
bool countryListContains(std::string countryList, std::string country);
|
||||
bool canPlayTrack(std::vector<Restriction> &restrictions);
|
||||
bool canPlayTrack();
|
||||
Track trackInfo;
|
||||
Episode episodeInfo;
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <vector>
|
||||
#include "Utils.h"
|
||||
#include "ProtoHelper.h"
|
||||
#include "protobuf/spirc.pb.h"
|
||||
#include <NanoPBHelper.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -62,5 +62,4 @@ message ClientResponseEncrypted {
|
||||
required LoginCredentials login_credentials = 0xa;
|
||||
required SystemInfo system_info = 0x32;
|
||||
optional string version_string = 0x46;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ message LoginCryptoHelloUnion {
|
||||
}
|
||||
|
||||
|
||||
enum Platform {
|
||||
enum Platform2 {
|
||||
PLATFORM_WIN32_X86 = 0x0;
|
||||
PLATFORM_OSX_X86 = 0x1;
|
||||
PLATFORM_LINUX_X86 = 0x2;
|
||||
@@ -58,7 +58,7 @@ enum Cryptosuite {
|
||||
|
||||
message BuildInfo {
|
||||
required Product product = 0xa;
|
||||
required Platform platform = 0x1e;
|
||||
required Platform2 platform = 0x1e;
|
||||
required uint64 version = 0x28;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,16 +33,16 @@ bool ConfigJSON::load()
|
||||
auto bitrateObject = cJSON_GetObjectItemCaseSensitive(root, "bitrate");
|
||||
switch((uint16_t)cJSON_GetNumberValue(bitrateObject)){
|
||||
case 320:
|
||||
this->format = AudioFormat::OGG_VORBIS_320;
|
||||
this->format = AudioFormat_OGG_VORBIS_320;
|
||||
break;
|
||||
case 160:
|
||||
this->format = AudioFormat::OGG_VORBIS_160;
|
||||
this->format = AudioFormat_OGG_VORBIS_160;
|
||||
break;
|
||||
case 96:
|
||||
this->format = AudioFormat::OGG_VORBIS_96;
|
||||
this->format = AudioFormat_OGG_VORBIS_96;
|
||||
break;
|
||||
default:
|
||||
this->format = AudioFormat::OGG_VORBIS_320;
|
||||
this->format = AudioFormat_OGG_VORBIS_320;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ bool ConfigJSON::load()
|
||||
// Set default values
|
||||
this->volume = 32767;
|
||||
this->deviceName = defaultDeviceName;
|
||||
this->format = AudioFormat::OGG_VORBIS_160;
|
||||
this->format = AudioFormat_OGG_VORBIS_160;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -76,13 +76,13 @@ bool ConfigJSON::save()
|
||||
obj["volume"] = this->volume;
|
||||
obj["deviceName"] = this->deviceName;
|
||||
switch(this->format){
|
||||
case AudioFormat::OGG_VORBIS_320:
|
||||
case AudioFormat_OGG_VORBIS_320:
|
||||
obj["bitrate"] = 320;
|
||||
break;
|
||||
case AudioFormat::OGG_VORBIS_160:
|
||||
case AudioFormat_OGG_VORBIS_160:
|
||||
obj["bitrate"] = 160;
|
||||
break;
|
||||
case AudioFormat::OGG_VORBIS_96:
|
||||
case AudioFormat_OGG_VORBIS_96:
|
||||
obj["bitrate"] = 96;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -103,7 +103,7 @@ void LoginBlob::loadUserPass(const std::string &username, const std::string &pas
|
||||
{
|
||||
this->username = username;
|
||||
this->authData = std::vector<uint8_t>(password.begin(), password.end());
|
||||
this->authType = static_cast<uint32_t>(AuthenticationType::AUTHENTICATION_USER_PASS);
|
||||
this->authType = static_cast<uint32_t>(AuthenticationType_AUTHENTICATION_USER_PASS);
|
||||
}
|
||||
|
||||
void LoginBlob::loadJson(const std::string &json)
|
||||
|
||||
@@ -11,6 +11,7 @@ std::map<MercuryType, std::string> MercuryTypeMap({
|
||||
|
||||
MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("mercuryManager", 6 * 1024, +1, 1)
|
||||
{
|
||||
tempMercuryHeader = Header_init_default;
|
||||
this->timeProvider = std::make_shared<TimeProvider>();
|
||||
this->callbacks = std::map<uint64_t, mercuryCallback>();
|
||||
this->subscriptions = std::map<std::string, mercuryCallback>();
|
||||
@@ -27,6 +28,11 @@ MercuryManager::MercuryManager(std::unique_ptr<Session> session): bell::Task("me
|
||||
};
|
||||
}
|
||||
|
||||
MercuryManager::~MercuryManager()
|
||||
{
|
||||
pbFree(Header_fields, &tempMercuryHeader);
|
||||
}
|
||||
|
||||
bool MercuryManager::timeoutHandler()
|
||||
{
|
||||
if (!isRunning) return true;
|
||||
@@ -257,9 +263,10 @@ void MercuryManager::updateQueue() {
|
||||
{
|
||||
auto response = std::make_unique<MercuryResponse>(packet->data);
|
||||
|
||||
if (this->subscriptions.count(response->mercuryHeader.uri.value()) > 0)
|
||||
auto uri = std::string(response->mercuryHeader.uri);
|
||||
if (this->subscriptions.count(uri) > 0)
|
||||
{
|
||||
this->subscriptions[response->mercuryHeader.uri.value()](std::move(response));
|
||||
this->subscriptions[uri](std::move(response));
|
||||
//this->subscriptions.erase(std::string(response->mercuryHeader.uri));
|
||||
}
|
||||
break;
|
||||
@@ -288,9 +295,8 @@ uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCal
|
||||
// Construct mercury header
|
||||
|
||||
CSPOT_LOG(debug, "executing MercuryType %s", MercuryTypeMap[method].c_str());
|
||||
Header mercuryHeader;
|
||||
mercuryHeader.uri = uri;
|
||||
mercuryHeader.method = MercuryTypeMap[method];
|
||||
tempMercuryHeader.uri = (char *)(uri.c_str());
|
||||
tempMercuryHeader.method = (char *)(MercuryTypeMap[method].c_str());
|
||||
|
||||
// GET and SEND are actually the same. Therefore the override
|
||||
// The difference between them is only in header's method
|
||||
@@ -299,7 +305,7 @@ uint64_t MercuryManager::execute(MercuryType method, std::string uri, mercuryCal
|
||||
method = MercuryType::SEND;
|
||||
}
|
||||
|
||||
auto headerBytes = encodePb(mercuryHeader);
|
||||
auto headerBytes = pbEncode(Header_fields, &tempMercuryHeader);
|
||||
|
||||
// Register a subscription when given method is called
|
||||
if (method == MercuryType::SUB)
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
MercuryResponse::MercuryResponse(std::vector<uint8_t> &data)
|
||||
{
|
||||
// this->mercuryHeader = std::make_unique<Header>();
|
||||
this->mercuryHeader = Header_init_default;
|
||||
this->parts = mercuryParts(0);
|
||||
this->parseResponse(data);
|
||||
}
|
||||
|
||||
MercuryResponse::~MercuryResponse() {
|
||||
pbFree(Header_fields, &mercuryHeader);
|
||||
}
|
||||
|
||||
void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
|
||||
{
|
||||
auto sequenceLength = ntohs(extract<uint16_t>(data, 0));
|
||||
@@ -29,5 +34,5 @@ void MercuryResponse::parseResponse(std::vector<uint8_t> &data)
|
||||
pos += 2 + partSize;
|
||||
}
|
||||
|
||||
this->mercuryHeader = decodePb<Header>(headerBytes);
|
||||
pbDecode(this->mercuryHeader, Header_fields, headerBytes);
|
||||
}
|
||||
@@ -5,35 +5,56 @@
|
||||
PlayerState::PlayerState(std::shared_ptr<TimeProvider> timeProvider)
|
||||
{
|
||||
this->timeProvider = timeProvider;
|
||||
innerFrame = {};
|
||||
remoteFrame = {};
|
||||
|
||||
// Prepare default state
|
||||
innerFrame.state.emplace();
|
||||
innerFrame.state->position_ms = 0;
|
||||
innerFrame.state->status = PlayStatus::kPlayStatusStop;
|
||||
innerFrame.state->position_measured_at = 0;
|
||||
innerFrame.state->shuffle = false;
|
||||
innerFrame.state->repeat = false;
|
||||
innerFrame.state.has_position_ms = true;
|
||||
innerFrame.state.position_ms = 0;
|
||||
|
||||
innerFrame.device_state.emplace();
|
||||
innerFrame.device_state->sw_version = swVersion;
|
||||
innerFrame.device_state->is_active = false;
|
||||
innerFrame.device_state->can_play = true;
|
||||
innerFrame.device_state->volume = configMan->volume;
|
||||
innerFrame.device_state->name = configMan->deviceName;
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusStop;
|
||||
innerFrame.state.has_status = true;
|
||||
|
||||
innerFrame.state.position_measured_at = 0;
|
||||
innerFrame.state.has_position_measured_at = true;
|
||||
|
||||
innerFrame.state.shuffle = false;
|
||||
innerFrame.state.has_shuffle = true;
|
||||
|
||||
innerFrame.state.repeat = false;
|
||||
innerFrame.state.has_repeat = true;
|
||||
|
||||
innerFrame.device_state.sw_version = (char*) swVersion;
|
||||
|
||||
innerFrame.device_state.is_active = false;
|
||||
innerFrame.device_state.has_is_active = true;
|
||||
|
||||
innerFrame.device_state.can_play = true;
|
||||
innerFrame.device_state.has_can_play = true;
|
||||
|
||||
innerFrame.device_state.volume = configMan->volume;
|
||||
innerFrame.device_state.has_volume = true;
|
||||
|
||||
innerFrame.device_state.name = (char*) configMan->deviceName.c_str();
|
||||
|
||||
// Prepare player's capabilities
|
||||
innerFrame.device_state->capabilities = std::vector<Capability>();
|
||||
addCapability(CapabilityType::kCanBePlayer, 1);
|
||||
addCapability(CapabilityType::kDeviceType, 4);
|
||||
addCapability(CapabilityType::kGaiaEqConnectId, 1);
|
||||
addCapability(CapabilityType::kSupportsLogout, 0);
|
||||
addCapability(CapabilityType::kIsObservable, 1);
|
||||
addCapability(CapabilityType::kVolumeSteps, 64);
|
||||
addCapability(CapabilityType::kSupportedContexts, -1,
|
||||
addCapability(CapabilityType_kCanBePlayer, 1);
|
||||
addCapability(CapabilityType_kDeviceType, 4);
|
||||
addCapability(CapabilityType_kGaiaEqConnectId, 1);
|
||||
addCapability(CapabilityType_kSupportsLogout, 0);
|
||||
addCapability(CapabilityType_kIsObservable, 1);
|
||||
addCapability(CapabilityType_kVolumeSteps, 64);
|
||||
addCapability(CapabilityType_kSupportedContexts, -1,
|
||||
std::vector<std::string>({"album", "playlist", "search", "inbox",
|
||||
"toplist", "starred", "publishedstarred", "track"}));
|
||||
addCapability(CapabilityType::kSupportedTypes, -1,
|
||||
addCapability(CapabilityType_kSupportedTypes, -1,
|
||||
std::vector<std::string>({"audio/local", "audio/track", "audio/episode", "local", "track"}));
|
||||
innerFrame.device_state.capabilities_count = 8;
|
||||
}
|
||||
|
||||
PlayerState::~PlayerState() {
|
||||
pbFree(Frame_fields, &innerFrame);
|
||||
pbFree(Frame_fields, &remoteFrame);
|
||||
}
|
||||
|
||||
void PlayerState::setPlaybackState(const PlaybackState state)
|
||||
@@ -42,38 +63,40 @@ void PlayerState::setPlaybackState(const PlaybackState state)
|
||||
{
|
||||
case PlaybackState::Loading:
|
||||
// Prepare the playback at position 0
|
||||
innerFrame.state->status = PlayStatus::kPlayStatusPause;
|
||||
innerFrame.state->position_ms = 0;
|
||||
innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPause;
|
||||
innerFrame.state.position_ms = 0;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
break;
|
||||
case PlaybackState::Playing:
|
||||
innerFrame.state->status = PlayStatus::kPlayStatusPlay;
|
||||
innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPlay;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
break;
|
||||
case PlaybackState::Stopped:
|
||||
break;
|
||||
case PlaybackState::Paused:
|
||||
// Update state and recalculate current song position
|
||||
innerFrame.state->status = PlayStatus::kPlayStatusPause;
|
||||
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state->position_measured_at.value();
|
||||
this->updatePositionMs(innerFrame.state->position_ms.value() + diff);
|
||||
innerFrame.state.status = PlayStatus_kPlayStatusPause;
|
||||
uint32_t diff = timeProvider->getSyncedTimestamp() - innerFrame.state.position_measured_at;
|
||||
this->updatePositionMs(innerFrame.state.position_ms + diff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerState::isActive()
|
||||
{
|
||||
return innerFrame.device_state->is_active.value();
|
||||
return innerFrame.device_state.is_active;
|
||||
}
|
||||
|
||||
bool PlayerState::nextTrack()
|
||||
{
|
||||
innerFrame.state->playing_track_index.value()++;
|
||||
if (innerFrame.state.repeat) return true;
|
||||
|
||||
if (innerFrame.state->playing_track_index >= innerFrame.state->track.size())
|
||||
innerFrame.state.playing_track_index++;
|
||||
|
||||
if (innerFrame.state.playing_track_index >= innerFrame.state.track_count)
|
||||
{
|
||||
innerFrame.state->playing_track_index = 0;
|
||||
if (!innerFrame.state->repeat)
|
||||
innerFrame.state.playing_track_index = 0;
|
||||
if (!innerFrame.state.repeat)
|
||||
{
|
||||
setPlaybackState(PlaybackState::Paused);
|
||||
return false;
|
||||
@@ -85,44 +108,46 @@ bool PlayerState::nextTrack()
|
||||
|
||||
void PlayerState::prevTrack()
|
||||
{
|
||||
if (innerFrame.state->playing_track_index > 0)
|
||||
if (innerFrame.state.playing_track_index > 0)
|
||||
{
|
||||
innerFrame.state->playing_track_index.value()--;
|
||||
innerFrame.state.playing_track_index--;
|
||||
}
|
||||
else if (innerFrame.state->repeat)
|
||||
else if (innerFrame.state.repeat)
|
||||
{
|
||||
innerFrame.state->playing_track_index = innerFrame.state->track.size() - 1;
|
||||
innerFrame.state.playing_track_index = innerFrame.state.track_count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerState::setActive(bool isActive)
|
||||
{
|
||||
innerFrame.device_state->is_active = isActive;
|
||||
innerFrame.device_state.is_active = isActive;
|
||||
if (isActive)
|
||||
{
|
||||
innerFrame.device_state->became_active_at = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.device_state.became_active_at = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.device_state.has_became_active_at = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerState::updatePositionMs(uint32_t position)
|
||||
{
|
||||
innerFrame.state->position_ms = position;
|
||||
innerFrame.state->position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.state.position_ms = position;
|
||||
innerFrame.state.position_measured_at = timeProvider->getSyncedTimestamp();
|
||||
}
|
||||
void PlayerState::updateTracks()
|
||||
{
|
||||
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state->track.size());
|
||||
CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
|
||||
// innerFrame.state->context_uri = remoteFrame.state->context_uri == nullptr ? nullptr : strdup(otherFrame->state->context_uri);
|
||||
std::copy(std::begin(remoteFrame.state.track), std::end(remoteFrame.state.track), std::begin(innerFrame.state.track));
|
||||
innerFrame.state.track_count = remoteFrame.state.track_count;
|
||||
innerFrame.state.has_playing_track_index = true;
|
||||
innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
|
||||
|
||||
innerFrame.state->track = remoteFrame.state->track;
|
||||
innerFrame.state->playing_track_index = remoteFrame.state->playing_track_index;
|
||||
|
||||
if (remoteFrame.state->repeat.value())
|
||||
if (remoteFrame.state.repeat)
|
||||
{
|
||||
setRepeat(true);
|
||||
}
|
||||
|
||||
if (remoteFrame.state->shuffle.value())
|
||||
if (remoteFrame.state.shuffle)
|
||||
{
|
||||
setShuffle(true);
|
||||
}
|
||||
@@ -130,70 +155,87 @@ void PlayerState::updateTracks()
|
||||
|
||||
void PlayerState::setVolume(uint32_t volume)
|
||||
{
|
||||
innerFrame.device_state->volume = volume;
|
||||
innerFrame.device_state.volume = volume;
|
||||
configMan->volume = volume;
|
||||
configMan->save();
|
||||
}
|
||||
|
||||
void PlayerState::setShuffle(bool shuffle)
|
||||
{
|
||||
innerFrame.state->shuffle = shuffle;
|
||||
innerFrame.state.shuffle = shuffle;
|
||||
if (shuffle)
|
||||
{
|
||||
// Put current song at the begining
|
||||
auto tmp = innerFrame.state->track.at(0);
|
||||
innerFrame.state->track.at(0) = innerFrame.state->track.at(innerFrame.state->playing_track_index.value());
|
||||
innerFrame.state->track.at(innerFrame.state->playing_track_index.value()) = tmp;
|
||||
auto tmp = innerFrame.state.track[0];
|
||||
innerFrame.state.track[0] = innerFrame.state.track[innerFrame.state.playing_track_index];
|
||||
innerFrame.state.track[innerFrame.state.playing_track_index] = tmp;
|
||||
|
||||
// Shuffle current tracks
|
||||
for (int x = 1; x < innerFrame.state->track.size() - 1; x++)
|
||||
for (int x = 1; x < innerFrame.state.track_count - 1; x++)
|
||||
{
|
||||
auto j = x + (std::rand() % (innerFrame.state->track.size() - x));
|
||||
tmp = innerFrame.state->track.at(j);
|
||||
innerFrame.state->track.at(j) = innerFrame.state->track.at(x);
|
||||
innerFrame.state->track.at(x) = tmp;
|
||||
auto j = x + (std::rand() % (innerFrame.state.track_count - x));
|
||||
tmp = innerFrame.state.track[j];
|
||||
innerFrame.state.track[j] = innerFrame.state.track[x];
|
||||
innerFrame.state.track[x] = tmp;
|
||||
}
|
||||
innerFrame.state->playing_track_index = 0;
|
||||
innerFrame.state.playing_track_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerState::setRepeat(bool repeat)
|
||||
{
|
||||
innerFrame.state->repeat = repeat;
|
||||
innerFrame.state.repeat = repeat;
|
||||
}
|
||||
|
||||
std::shared_ptr<TrackReference> PlayerState::getCurrentTrack()
|
||||
{
|
||||
// Wrap current track in a class
|
||||
return std::make_shared<TrackReference>(&innerFrame.state->track.at(innerFrame.state->playing_track_index.value()));
|
||||
return std::make_shared<TrackReference>(&innerFrame.state.track[innerFrame.state.playing_track_index]);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> PlayerState::encodeCurrentFrame(MessageType typ)
|
||||
{
|
||||
// Prepare current frame info
|
||||
innerFrame.version = 1;
|
||||
innerFrame.ident = deviceId;
|
||||
innerFrame.ident = (char *) deviceId;
|
||||
innerFrame.seq_nr = this->seqNum;
|
||||
innerFrame.protocol_version = protocolVersion;
|
||||
innerFrame.protocol_version = (char*) protocolVersion;
|
||||
innerFrame.typ = typ;
|
||||
innerFrame.state_update_id = timeProvider->getSyncedTimestamp();
|
||||
innerFrame.has_version = true;
|
||||
innerFrame.has_seq_nr = true;
|
||||
innerFrame.recipient_count = 0;
|
||||
innerFrame.has_state = true;
|
||||
innerFrame.has_device_state = true;
|
||||
innerFrame.has_typ = true;
|
||||
innerFrame.has_state_update_id = true;
|
||||
|
||||
this->seqNum += 1;
|
||||
auto fram = encodePb(innerFrame);
|
||||
return fram;
|
||||
return pbEncode(Frame_fields, &innerFrame);
|
||||
}
|
||||
|
||||
// Wraps messy nanopb setters. @TODO: find a better way to handle this
|
||||
void PlayerState::addCapability(CapabilityType typ, int intValue, std::vector<std::string> stringValue)
|
||||
{
|
||||
auto capability = Capability();
|
||||
capability.typ = typ;
|
||||
innerFrame.device_state.capabilities[capabilityIndex].has_typ = true;
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].typ = typ;
|
||||
|
||||
if (intValue != -1)
|
||||
{
|
||||
capability.intValue = std::vector<int64_t>({intValue});
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].intValue[0] = intValue;
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count = 0;
|
||||
}
|
||||
|
||||
capability.stringValue = stringValue;
|
||||
innerFrame.device_state->capabilities.push_back(capability);
|
||||
for (int x = 0; x < stringValue.size(); x++)
|
||||
{
|
||||
stringValue[x].copy(this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x], stringValue[x].size());
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue[x][stringValue[x].size()] = '\0';
|
||||
}
|
||||
|
||||
this->innerFrame.device_state.capabilities[capabilityIndex].stringValue_count = stringValue.size();
|
||||
this->capabilityIndex += 1;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,24 @@ using random_bytes_engine = std::independent_bits_engine<std::default_random_eng
|
||||
|
||||
Session::Session()
|
||||
{
|
||||
this->clientHello = ClientHello_init_default;
|
||||
this->apResponse = APResponseMessage_init_default;
|
||||
this->authRequest = ClientResponseEncrypted_init_default;
|
||||
this->clientResPlaintext = ClientResponsePlaintext_init_default;
|
||||
|
||||
// Generates the public and priv key
|
||||
this->crypto = std::make_unique<Crypto>();
|
||||
this->shanConn = std::make_shared<ShannonConnection>();
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
pbFree(ClientHello_fields, &clientHello);
|
||||
pbFree(APResponseMessage_fields, &apResponse);
|
||||
pbFree(ClientResponseEncrypted_fields, &authRequest);
|
||||
pbFree(ClientResponsePlaintext_fields, &clientResPlaintext);
|
||||
}
|
||||
|
||||
void Session::connect(std::unique_ptr<PlainConnection> connection)
|
||||
{
|
||||
this->conn = std::move(connection);
|
||||
@@ -37,16 +50,16 @@ std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
|
||||
authBlob = blob;
|
||||
|
||||
// prepare authentication request proto
|
||||
authRequest.login_credentials.username = blob->username;
|
||||
authRequest.login_credentials.auth_data = blob->authData;
|
||||
authRequest.login_credentials.typ = static_cast<AuthenticationType>(blob->authType);
|
||||
authRequest.system_info.cpu_family = CpuFamily::CPU_UNKNOWN;
|
||||
authRequest.system_info.os = Os::OS_UNKNOWN;
|
||||
authRequest.system_info.system_information_string = std::string(informationString);
|
||||
authRequest.system_info.device_id = std::string(deviceId);
|
||||
authRequest.version_string = std::string(versionString);
|
||||
authRequest.login_credentials.username = (char *)(blob->username.c_str());
|
||||
authRequest.login_credentials.auth_data = vectorToPbArray(blob->authData);
|
||||
authRequest.login_credentials.typ = (AuthenticationType) blob->authType;
|
||||
authRequest.system_info.cpu_family = CpuFamily_CPU_UNKNOWN;
|
||||
authRequest.system_info.os = Os_OS_UNKNOWN;
|
||||
authRequest.system_info.system_information_string = (char *)informationString;
|
||||
authRequest.system_info.device_id = (char *)deviceId;
|
||||
authRequest.version_string = (char *)versionString;
|
||||
|
||||
auto data = encodePb(authRequest);
|
||||
auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest);
|
||||
|
||||
// Send login request
|
||||
this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data);
|
||||
@@ -82,11 +95,11 @@ void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
|
||||
CSPOT_LOG(debug, "Received AP hello response");
|
||||
// Decode the response
|
||||
auto skipSize = std::vector<uint8_t>(data.begin() + 4, data.end());
|
||||
apResponse = decodePb<APResponseMessage>(skipSize);
|
||||
pbDecode(apResponse, APResponseMessage_fields, skipSize);
|
||||
|
||||
auto kkEy = apResponse.challenge->login_crypto_challenge.diffie_hellman->gs;
|
||||
auto diffieKey = std::vector<uint8_t>(apResponse.challenge.login_crypto_challenge.diffie_hellman.gs, apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96);
|
||||
// Compute the diffie hellman shared key based on the response
|
||||
auto sharedKey = this->crypto->dhCalculateShared(kkEy);
|
||||
auto sharedKey = this->crypto->dhCalculateShared(diffieKey);
|
||||
|
||||
// Init client packet + Init server packets are required for the hmac challenge
|
||||
data.insert(data.begin(), helloPacket.begin(), helloPacket.end());
|
||||
@@ -106,11 +119,14 @@ void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
|
||||
auto lastVec = std::vector<uint8_t>(resultData.begin(), resultData.begin() + 0x14);
|
||||
|
||||
// Digest generated!
|
||||
clientResPlaintext.login_crypto_response = {};
|
||||
clientResPlaintext.login_crypto_response.diffie_hellman.emplace();
|
||||
clientResPlaintext.login_crypto_response.diffie_hellman->hmac = crypto->sha1HMAC(lastVec, data);
|
||||
auto digest = crypto->sha1HMAC(lastVec, data);
|
||||
clientResPlaintext.login_crypto_response.has_diffie_hellman = true;
|
||||
|
||||
auto resultPacket = encodePb(clientResPlaintext);
|
||||
std::copy(digest.begin(),
|
||||
digest.end(),
|
||||
clientResPlaintext.login_crypto_response.diffie_hellman.hmac);
|
||||
|
||||
auto resultPacket = pbEncode(ClientResponsePlaintext_fields, &clientResPlaintext);
|
||||
|
||||
auto emptyPrefix = std::vector<uint8_t>(0);
|
||||
|
||||
@@ -136,20 +152,27 @@ std::vector<uint8_t> Session::sendClientHelloRequest()
|
||||
this->crypto->dhInit();
|
||||
|
||||
// Copy the public key into diffiehellman hello packet
|
||||
clientHello.login_crypto_hello.diffie_hellman.emplace();
|
||||
clientHello.feature_set.emplace();
|
||||
clientHello.login_crypto_hello.diffie_hellman->gc = this->crypto->publicKey;
|
||||
clientHello.login_crypto_hello.diffie_hellman->server_keys_known = 1;
|
||||
clientHello.build_info.product = Product::PRODUCT_PARTNER;
|
||||
clientHello.build_info.platform = Platform::PLATFORM_LINUX_X86;
|
||||
clientHello.build_info.version = 112800721;
|
||||
clientHello.feature_set->autoupdate2 = true;
|
||||
clientHello.cryptosuites_supported = std::vector<Cryptosuite>({Cryptosuite::CRYPTO_SUITE_SHANNON});
|
||||
clientHello.padding = std::vector<uint8_t>({0x1E});
|
||||
std::copy(this->crypto->publicKey.begin(),
|
||||
this->crypto->publicKey.end(),
|
||||
clientHello.login_crypto_hello.diffie_hellman.gc);
|
||||
|
||||
clientHello.login_crypto_hello.diffie_hellman.server_keys_known = 1;
|
||||
clientHello.build_info.product = Product_PRODUCT_PARTNER;
|
||||
clientHello.build_info.platform = Platform2_PLATFORM_LINUX_X86;
|
||||
clientHello.build_info.version = SPOTIFY_VERSION;
|
||||
clientHello.feature_set.autoupdate2 = true;
|
||||
clientHello.cryptosuites_supported[0] = Cryptosuite_CRYPTO_SUITE_SHANNON;
|
||||
clientHello.padding[0] = 0x1E;
|
||||
|
||||
clientHello.has_feature_set = true;
|
||||
clientHello.login_crypto_hello.has_diffie_hellman = true;
|
||||
clientHello.has_padding = true;
|
||||
clientHello.has_feature_set = true;
|
||||
|
||||
// Generate the random nonce
|
||||
clientHello.client_nonce = crypto->generateVectorWithRandomData(16);
|
||||
auto vecData = encodePb(clientHello);
|
||||
auto nonce = crypto->generateVectorWithRandomData(16);
|
||||
std::copy(nonce.begin(), nonce.end(), clientHello.client_nonce);
|
||||
auto vecData = pbEncode(ClientHello_fields, &clientHello);
|
||||
auto prefix = std::vector<uint8_t>({0x00, 0x04});
|
||||
return this->conn->sendPrefixPacket(prefix, vecData);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// #define NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
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.
|
||||
@@ -440,4 +442,4 @@ void Shannon::finish(std::vector<uint8_t> &bufVec)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ SpircController::SpircController(std::shared_ptr<MercuryManager> manager,
|
||||
void SpircController::subscribe() {
|
||||
mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
|
||||
// this->trackInformationCallback(std::move(res));
|
||||
sendCmd(MessageType::kMessageTypeHello);
|
||||
sendCmd(MessageType_kMessageTypeHello);
|
||||
CSPOT_LOG(debug, "Sent kMessageTypeHello!");
|
||||
};
|
||||
mercuryCallback subLambda = [=](std::unique_ptr<MercuryResponse> res) {
|
||||
@@ -60,7 +60,7 @@ void SpircController::disconnect(void) {
|
||||
}
|
||||
|
||||
void SpircController::playToggle() {
|
||||
if (state->innerFrame.state->status.value() == PlayStatus::kPlayStatusPause) {
|
||||
if (state->innerFrame.state.status == PlayStatus_kPlayStatusPause) {
|
||||
setPause(false);
|
||||
} else {
|
||||
setPause(true);
|
||||
@@ -68,8 +68,8 @@ void SpircController::playToggle() {
|
||||
}
|
||||
|
||||
void SpircController::adjustVolume(int by) {
|
||||
if (state->innerFrame.device_state->volume.has_value()) {
|
||||
int volume = state->innerFrame.device_state->volume.value() + by;
|
||||
if (state->innerFrame.device_state.has_volume) {
|
||||
int volume = state->innerFrame.device_state.volume + by;
|
||||
if (volume < 0) volume = 0;
|
||||
else if (volume > MAX_VOLUME) volume = MAX_VOLUME;
|
||||
setVolume(volume);
|
||||
@@ -103,45 +103,45 @@ void SpircController::prevSong() {
|
||||
}
|
||||
|
||||
void SpircController::handleFrame(std::vector<uint8_t> &data) {
|
||||
state->remoteFrame = decodePb<Frame>(data);
|
||||
pbDecode(state->remoteFrame, Frame_fields, data);
|
||||
|
||||
switch (state->remoteFrame.typ.value()) {
|
||||
case MessageType::kMessageTypeNotify: {
|
||||
switch (state->remoteFrame.typ) {
|
||||
case MessageType_kMessageTypeNotify: {
|
||||
CSPOT_LOG(debug, "Notify frame");
|
||||
// Pause the playback if another player took control
|
||||
if (state->isActive() &&
|
||||
state->remoteFrame.device_state->is_active.value()) {
|
||||
state->remoteFrame.device_state.is_active) {
|
||||
disconnect();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MessageType::kMessageTypeSeek: {
|
||||
case MessageType_kMessageTypeSeek: {
|
||||
CSPOT_LOG(debug, "Seek command");
|
||||
sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position.value());
|
||||
state->updatePositionMs(state->remoteFrame.position.value());
|
||||
this->player->seekMs(state->remoteFrame.position.value());
|
||||
sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position);
|
||||
state->updatePositionMs(state->remoteFrame.position);
|
||||
this->player->seekMs(state->remoteFrame.position);
|
||||
notify();
|
||||
break;
|
||||
}
|
||||
case MessageType::kMessageTypeVolume:
|
||||
sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume.value());
|
||||
setVolume(state->remoteFrame.volume.value());
|
||||
case MessageType_kMessageTypeVolume:
|
||||
sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume);
|
||||
setVolume(state->remoteFrame.volume);
|
||||
break;
|
||||
case MessageType::kMessageTypePause:
|
||||
case MessageType_kMessageTypePause:
|
||||
setPause(true);
|
||||
break;
|
||||
case MessageType::kMessageTypePlay:
|
||||
case MessageType_kMessageTypePlay:
|
||||
setPause(false);
|
||||
break;
|
||||
case MessageType::kMessageTypeNext:
|
||||
case MessageType_kMessageTypeNext:
|
||||
sendEvent(CSpotEventType::NEXT);
|
||||
nextSong();
|
||||
break;
|
||||
case MessageType::kMessageTypePrev:
|
||||
case MessageType_kMessageTypePrev:
|
||||
sendEvent(CSpotEventType::PREV);
|
||||
prevSong();
|
||||
break;
|
||||
case MessageType::kMessageTypeLoad: {
|
||||
case MessageType_kMessageTypeLoad: {
|
||||
CSPOT_LOG(debug, "Load frame!");
|
||||
|
||||
state->setActive(true);
|
||||
@@ -154,25 +154,25 @@ void SpircController::handleFrame(std::vector<uint8_t> &data) {
|
||||
|
||||
// bool isPaused = (state->remoteFrame.state->status.value() ==
|
||||
// PlayStatus::kPlayStatusPlay) ? false : true;
|
||||
loadTrack(state->remoteFrame.state->position_ms.value(), false);
|
||||
state->updatePositionMs(state->remoteFrame.state->position_ms.value());
|
||||
loadTrack(state->remoteFrame.state.position_ms, false);
|
||||
state->updatePositionMs(state->remoteFrame.state.position_ms);
|
||||
|
||||
this->notify();
|
||||
break;
|
||||
}
|
||||
case MessageType::kMessageTypeReplace: {
|
||||
case MessageType_kMessageTypeReplace: {
|
||||
CSPOT_LOG(debug, "Got replace frame!");
|
||||
break;
|
||||
}
|
||||
case MessageType::kMessageTypeShuffle: {
|
||||
case MessageType_kMessageTypeShuffle: {
|
||||
CSPOT_LOG(debug, "Got shuffle frame");
|
||||
state->setShuffle(state->remoteFrame.state->shuffle.value());
|
||||
state->setShuffle(state->remoteFrame.state.shuffle);
|
||||
this->notify();
|
||||
break;
|
||||
}
|
||||
case MessageType::kMessageTypeRepeat: {
|
||||
case MessageType_kMessageTypeRepeat: {
|
||||
CSPOT_LOG(debug, "Got repeat frame");
|
||||
state->setRepeat(state->remoteFrame.state->repeat.value());
|
||||
state->setRepeat(state->remoteFrame.state.repeat);
|
||||
this->notify();
|
||||
break;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ void SpircController::loadTrack(uint32_t position_ms, bool isPaused) {
|
||||
}
|
||||
|
||||
void SpircController::notify() {
|
||||
this->sendCmd(MessageType::kMessageTypeNotify);
|
||||
this->sendCmd(MessageType_kMessageTypeNotify);
|
||||
}
|
||||
|
||||
void SpircController::sendEvent(CSpotEventType eventType, std::variant<TrackInfo, int, bool> data) {
|
||||
@@ -218,7 +218,6 @@ void SpircController::setEventHandler(cspotEventHandler callback) {
|
||||
info.imageUrl = track.imageUrl;
|
||||
info.name = track.name;
|
||||
info.duration = track.duration;
|
||||
|
||||
this->sendEvent(CSpotEventType::TRACK_INFO, info);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ SpotifyTrack::SpotifyTrack(std::shared_ptr<MercuryManager> manager, std::shared_
|
||||
{
|
||||
this->manager = manager;
|
||||
this->fileId = std::vector<uint8_t>();
|
||||
episodeInfo = Episode_init_default;
|
||||
trackInfo = Track_init_default;
|
||||
|
||||
mercuryCallback trackResponseLambda = [=](std::unique_ptr<MercuryResponse> res) {
|
||||
this->trackInformationCallback(std::move(res), position_ms, isPaused);
|
||||
@@ -33,6 +35,8 @@ SpotifyTrack::~SpotifyTrack()
|
||||
{
|
||||
this->manager->unregisterMercuryCallback(this->reqSeqNum);
|
||||
this->manager->freeAudioKeyCallback();
|
||||
pbFree(Track_fields, &this->trackInfo);
|
||||
pbFree(Episode_fields, &this->episodeInfo);
|
||||
}
|
||||
|
||||
bool SpotifyTrack::countryListContains(std::string countryList, std::string country)
|
||||
@@ -47,18 +51,18 @@ bool SpotifyTrack::countryListContains(std::string countryList, std::string coun
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SpotifyTrack::canPlayTrack(std::vector<Restriction>& restrictions)
|
||||
bool SpotifyTrack::canPlayTrack()
|
||||
{
|
||||
for (int x = 0; x < restrictions.size(); x++)
|
||||
for (int x = 0; x < trackInfo.restriction_count; x++)
|
||||
{
|
||||
if (restrictions[x].countries_allowed.has_value())
|
||||
if (trackInfo.restriction[x].countries_allowed != nullptr)
|
||||
{
|
||||
return countryListContains(restrictions[x].countries_allowed.value(), manager->countryCode);
|
||||
return countryListContains(std::string(trackInfo.restriction[x].countries_allowed), manager->countryCode);
|
||||
}
|
||||
|
||||
if (restrictions[x].countries_forbidden.has_value())
|
||||
if (trackInfo.restriction[x].countries_forbidden != nullptr)
|
||||
{
|
||||
return !countryListContains(restrictions[x].countries_forbidden.value(), manager->countryCode);
|
||||
return !countryListContains(std::string(trackInfo.restriction[x].countries_forbidden), manager->countryCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,48 +75,49 @@ void SpotifyTrack::trackInformationCallback(std::unique_ptr<MercuryResponse> res
|
||||
return;
|
||||
CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
|
||||
|
||||
trackInfo = decodePb<Track>(response->parts[0]);
|
||||
pbDecode(trackInfo, Track_fields, response->parts[0]);
|
||||
|
||||
CSPOT_LOG(info, "Track name: %s", trackInfo.name.value().c_str());
|
||||
CSPOT_LOG(info, "Track duration: %d", trackInfo.duration.value());
|
||||
|
||||
CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction.size());
|
||||
CSPOT_LOG(info, "Track name: %s", trackInfo.name);
|
||||
CSPOT_LOG(info, "Track duration: %d", trackInfo.duration);
|
||||
CSPOT_LOG(debug, "trackInfo.restriction.size() = %d", trackInfo.restriction_count);
|
||||
int altIndex = 0;
|
||||
while (!canPlayTrack(trackInfo.restriction))
|
||||
while (!canPlayTrack())
|
||||
{
|
||||
trackInfo.restriction = trackInfo.alternative[altIndex].restriction;
|
||||
trackInfo.restriction_count = trackInfo.alternative[altIndex].restriction_count;
|
||||
trackInfo.gid = trackInfo.alternative[altIndex].gid;
|
||||
trackInfo.file = trackInfo.alternative[altIndex].file;
|
||||
altIndex++;
|
||||
CSPOT_LOG(info, "Trying alternative %d", altIndex);
|
||||
}
|
||||
auto trackId = trackInfo.gid.value();
|
||||
auto trackId = pbArrayToVector(trackInfo.gid);
|
||||
this->fileId = std::vector<uint8_t>();
|
||||
|
||||
for (int x = 0; x < trackInfo.file.size(); x++)
|
||||
for (int x = 0; x < trackInfo.file_count; x++)
|
||||
{
|
||||
if (trackInfo.file[x].format == configMan->format)
|
||||
{
|
||||
this->fileId = trackInfo.file[x].file_id.value();
|
||||
this->fileId = pbArrayToVector(trackInfo.file[x].file_id);
|
||||
break; // If file found stop searching
|
||||
}
|
||||
}
|
||||
|
||||
if (trackInfoReceived != nullptr)
|
||||
{
|
||||
auto imageId = pbArrayToVector(trackInfo.album.cover_group.image[0].file_id);
|
||||
TrackInfo simpleTrackInfo = {
|
||||
.name = trackInfo.name.value(),
|
||||
.album = trackInfo.album.value().name.value(),
|
||||
.artist = trackInfo.artist[0].name.value(),
|
||||
.imageUrl = "https://i.scdn.co/image/" + bytesToHexString(trackInfo.album.value().cover_group.value().image[0].file_id.value()),
|
||||
.duration = trackInfo.duration.value(),
|
||||
.name = std::string(trackInfo.name),
|
||||
.album = std::string(trackInfo.album.name),
|
||||
.artist = std::string(trackInfo.artist[0].name),
|
||||
.imageUrl = "https://i.scdn.co/image/" + bytesToHexString(imageId),
|
||||
.duration = trackInfo.duration,
|
||||
|
||||
};
|
||||
|
||||
trackInfoReceived(simpleTrackInfo);
|
||||
}
|
||||
|
||||
this->requestAudioKey(this->fileId, trackId, trackInfo.duration.value(), position_ms, isPaused);
|
||||
this->requestAudioKey(this->fileId, trackId, trackInfo.duration, position_ms, isPaused);
|
||||
}
|
||||
|
||||
void SpotifyTrack::episodeInformationCallback(std::unique_ptr<MercuryResponse> response, uint32_t position_ms, bool isPaused)
|
||||
@@ -121,23 +126,23 @@ void SpotifyTrack::episodeInformationCallback(std::unique_ptr<MercuryResponse> r
|
||||
return;
|
||||
CSPOT_LOG(debug, "Got to episode");
|
||||
CSPOT_ASSERT(response->parts.size() > 0, "response->parts.size() must be greater than 0");
|
||||
episodeInfo = decodePb<Episode>(response->parts[0]);
|
||||
pbDecode(episodeInfo, Episode_fields, response->parts[0]);
|
||||
|
||||
CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name.value().c_str());
|
||||
CSPOT_LOG(info, "--- Episode name: %s", episodeInfo.name);
|
||||
|
||||
this->fileId = std::vector<uint8_t>();
|
||||
|
||||
// TODO: option to set file quality
|
||||
for (int x = 0; x < episodeInfo.audio.size(); x++)
|
||||
for (int x = 0; x < episodeInfo.audio_count; x++)
|
||||
{
|
||||
if (episodeInfo.audio[x].format == AudioFormat::OGG_VORBIS_96)
|
||||
if (episodeInfo.audio[x].format == AudioFormat_OGG_VORBIS_96)
|
||||
{
|
||||
this->fileId = episodeInfo.audio[x].file_id.value();
|
||||
this->fileId = pbArrayToVector(episodeInfo.audio[x].file_id);
|
||||
break; // If file found stop searching
|
||||
}
|
||||
}
|
||||
|
||||
this->requestAudioKey(episodeInfo.gid.value(), this->fileId, episodeInfo.duration.value(), position_ms, isPaused);
|
||||
this->requestAudioKey(pbArrayToVector(episodeInfo.gid), this->fileId, episodeInfo.duration, position_ms, isPaused);
|
||||
}
|
||||
|
||||
void SpotifyTrack::requestAudioKey(std::vector<uint8_t> fileId, std::vector<uint8_t> trackId, int32_t trackDuration, uint32_t position_ms, bool isPaused)
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
TrackReference::TrackReference(TrackRef *ref)
|
||||
{
|
||||
if (ref->gid.has_value())
|
||||
if (ref->gid != nullptr)
|
||||
{
|
||||
gid = ref->gid.value();
|
||||
gid = pbArrayToVector(ref->gid);
|
||||
}
|
||||
else if (ref->uri.has_value())
|
||||
else if (ref->uri != nullptr)
|
||||
{
|
||||
auto uri = ref->uri.value();
|
||||
auto uri = std::string(ref->uri);
|
||||
auto idString = uri.substr(uri.find_last_of(":") + 1, uri.size());
|
||||
CSPOT_LOG(debug, "idString = %s", idString.c_str());
|
||||
gid = base62Decode(idString);
|
||||
@@ -19,6 +19,7 @@ TrackReference::TrackReference(TrackRef *ref)
|
||||
|
||||
TrackReference::~TrackReference()
|
||||
{
|
||||
//pbFree(TrackRef_fields, &ref);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> TrackReference::base62Decode(std::string uri)
|
||||
|
||||
Reference in New Issue
Block a user