make it fit in allocated space

This commit is contained in:
philippe44
2023-03-27 17:09:27 -07:00
parent 7dfdd7b9e5
commit fc78b36c1f
24 changed files with 421 additions and 230 deletions

View File

@@ -1,5 +1,5 @@
# this must be set *before* idf_component_register # this must be set *before* idf_component_register
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
idf_component_register( idf_component_register(
SRC_DIRS . SRC_DIRS .
@@ -14,8 +14,12 @@ add_definitions(-Wno-unused-variable -Wno-unused-const-variable -Wchar-subscript
set(BELL_DISABLE_CODECS ON) set(BELL_DISABLE_CODECS ON)
set(BELL_DISABLE_SINKS ON) set(BELL_DISABLE_SINKS ON)
set(BELL_DISABLE_FMT ON)
set(BELL_DISABLE_REGEX ON)
set(BELL_ONLY_CJSON ON)
set(CSPOT_TARGET_ESP32 ON) set(CSPOT_TARGET_ESP32 ON)
# becase CMake is so broken, the cache set below overrides a normal "set" for the first build
# because CMake is so broken, the cache set below overrides a normal "set" for the first build
set(BELL_EXTERNAL_VORBIS "idf::codecs" CACHE STRING "provide own codecs") set(BELL_EXTERNAL_VORBIS "idf::codecs" CACHE STRING "provide own codecs")
set(BELL_EXTERNAL_CJSON "idf::json" CACHE STRING "provide own CJSON") set(BELL_EXTERNAL_CJSON "idf::json" CACHE STRING "provide own CJSON")

View File

@@ -29,40 +29,7 @@
#include "platform_config.h" #include "platform_config.h"
#include "tools.h" #include "tools.h"
//#include "time.h" static class cspotPlayer *player;
/*
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_http_server.h"
#include <ConstantParameters.h>
#include <Session.h>
#include <SpircController.h>
#include <MercuryManager.h>
#include <ZeroconfAuthenticator.h>
#include <ApResolve.h>
#include <HTTPServer.h>
#include "ConfigJSON.h"
#include "Logger.h"
#include "platform_config.h"
#include "tools.h"
#include "cspot_private.h"
#include "cspot_sink.h"
*/
static const char *TAG = "cspot";
class cspotPlayer *player;
/**************************************************************************************** /****************************************************************************************
* Chunk manager class (task) * Chunk manager class (task)
@@ -72,27 +39,33 @@ class chunkManager : public bell::Task {
public: public:
std::atomic<bool> isRunning = true; std::atomic<bool> isRunning = true;
std::atomic<bool> isPaused = true; std::atomic<bool> isPaused = true;
chunkManager(std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer, std::function<void()> trackHandler, std::function<void(const uint8_t*, size_t)> audioHandler); chunkManager(std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer, std::function<void()> trackHandler,
std::function<void(const uint8_t*, size_t)> dataHandler);
void teardown(); void teardown();
private: private:
std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer; std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer;
std::function<void()> trackHandler; std::function<void()> trackHandler;
std::function<void(const uint8_t*, size_t)> audioHandler; std::function<void(const uint8_t*, size_t)> dataHandler;
std::mutex runningMutex; std::mutex runningMutex;
void runTask() override; void runTask() override;
}; };
chunkManager::chunkManager(std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer, chunkManager::chunkManager(std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer,
std::function<void()> trackHandler, std::function<void(const uint8_t*, size_t)> audioHandler) std::function<void()> trackHandler, std::function<void(const uint8_t*, size_t)> dataHandler)
: bell::Task("player", 4 * 1024, 0, 0) { : bell::Task("chunker", 4 * 1024, 0, 0) {
this->centralAudioBuffer = centralAudioBuffer; this->centralAudioBuffer = centralAudioBuffer;
this->trackHandler = trackHandler; this->trackHandler = trackHandler;
this->audioHandler = audioHandler; this->dataHandler = dataHandler;
startTask(); startTask();
} }
void chunkManager::teardown() {
isRunning = false;
std::scoped_lock lock(runningMutex);
}
void chunkManager::runTask() { void chunkManager::runTask() {
std::scoped_lock lock(runningMutex); std::scoped_lock lock(runningMutex);
size_t lastHash = 0; size_t lastHash = 0;
@@ -118,15 +91,10 @@ void chunkManager::runTask() {
trackHandler(); trackHandler();
} }
audioHandler(chunk->pcmData, chunk->pcmSize); dataHandler(chunk->pcmData, chunk->pcmSize);
} }
} }
void chunkManager::teardown() {
isRunning = false;
std::scoped_lock lock(runningMutex);
}
/**************************************************************************************** /****************************************************************************************
* Player's main class & task * Player's main class & task
*/ */
@@ -134,17 +102,14 @@ void chunkManager::teardown() {
class cspotPlayer : public bell::Task { class cspotPlayer : public bell::Task {
private: private:
std::string name; std::string name;
bool playback = false;
bell::WrappedSemaphore clientConnected; bell::WrappedSemaphore clientConnected;
std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer; std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer;
TimerHandle_t trackTimer;
int startOffset, volume = 0, bitrate = 160; int startOffset, volume = 0, bitrate = 160;
httpd_handle_t serverHandle; httpd_handle_t serverHandle;
int serverPort; int serverPort;
cspot_cmd_cb_t cmdHandler; cspot_cmd_cb_t cmdHandler;
cspot_data_cb_t dataHandler; cspot_data_cb_t dataHandler;
std::shared_ptr<cspot::LoginBlob> blob; std::shared_ptr<cspot::LoginBlob> blob;
std::unique_ptr<cspot::SpircHandler> spirc; std::unique_ptr<cspot::SpircHandler> spirc;
@@ -156,12 +121,13 @@ private:
void runTask(); void runTask();
public: public:
std::atomic<bool> trackNotify = false; typedef enum {TRACK_INIT, TRACK_NOTIFY, TRACK_STREAM, TRACK_END} TrackStatus;
std::atomic<TrackStatus> trackStatus = TRACK_INIT;
cspotPlayer(const char*, httpd_handle_t, int, cspot_cmd_cb_t, cspot_data_cb_t); cspotPlayer(const char*, httpd_handle_t, int, cspot_cmd_cb_t, cspot_data_cb_t);
~cspotPlayer();
esp_err_t handleGET(httpd_req_t *request); esp_err_t handleGET(httpd_req_t *request);
esp_err_t handlePOST(httpd_req_t *request); esp_err_t handlePOST(httpd_req_t *request);
void command(cspot_event_t event);
}; };
cspotPlayer::cspotPlayer(const char* name, httpd_handle_t server, int port, cspot_cmd_cb_t cmdHandler, cspot_data_cb_t dataHandler) : cspotPlayer::cspotPlayer(const char* name, httpd_handle_t server, int port, cspot_cmd_cb_t cmdHandler, cspot_data_cb_t dataHandler) :
@@ -179,9 +145,6 @@ cspotPlayer::cspotPlayer(const char* name, httpd_handle_t server, int port, cspo
if (bitrate != 96 && bitrate != 160 && bitrate != 320) bitrate = 160; if (bitrate != 96 && bitrate != 160 && bitrate != 320) bitrate = 160;
} }
cspotPlayer::~cspotPlayer() {
}
extern "C" { extern "C" {
static esp_err_t handleGET(httpd_req_t *request) { static esp_err_t handleGET(httpd_req_t *request) {
return player->handleGET(request); return player->handleGET(request);
@@ -190,10 +153,6 @@ extern "C" {
static esp_err_t handlePOST(httpd_req_t *request) { static esp_err_t handlePOST(httpd_req_t *request) {
return player->handlePOST(request); return player->handlePOST(request);
} }
static void trackTimerHandler(TimerHandle_t xTimer) {
player->trackNotify = true;
}
} }
esp_err_t cspotPlayer::handleGET(httpd_req_t *request) { esp_err_t cspotPlayer::handleGET(httpd_req_t *request) {
@@ -258,18 +217,14 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
centralAudioBuffer->clearBuffer(); centralAudioBuffer->clearBuffer();
// we are not playing anymore // we are not playing anymore
xTimerStop(trackTimer, portMAX_DELAY); trackStatus = TRACK_INIT;
trackNotify = false;
playback = false;
// memorize position for when track's beginning will be detected // memorize position for when track's beginning will be detected
startOffset = std::get<int>(event->data); startOffset = std::get<int>(event->data);
cmdHandler(CSPOT_START, 44100);
CSPOT_LOG(info, "start track <%s>", spirc->getTrackPlayer()->getCurrentTrackInfo().name.c_str());
// Spotify servers do not send volume at connection // Spotify servers do not send volume at connection
spirc->setRemoteVolume(volume); spirc->setRemoteVolume(volume);
cmdHandler(CSPOT_START, 44100);
CSPOT_LOG(info, "restart");
break; break;
} }
case cspot::SpircHandler::EventType::PLAY_PAUSE: { case cspot::SpircHandler::EventType::PLAY_PAUSE: {
@@ -280,7 +235,7 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
} }
case cspot::SpircHandler::EventType::TRACK_INFO: { case cspot::SpircHandler::EventType::TRACK_INFO: {
auto trackInfo = std::get<cspot::CDNTrackStream::TrackInfo>(event->data); auto trackInfo = std::get<cspot::CDNTrackStream::TrackInfo>(event->data);
cmdHandler(CSPOT_TRACK, trackInfo.duration, startOffset, trackInfo.artist.c_str(), cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str()); trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
spirc->updatePositionMs(startOffset); spirc->updatePositionMs(startOffset);
startOffset = 0; startOffset = 0;
@@ -296,7 +251,6 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
} }
case cspot::SpircHandler::EventType::DISC: case cspot::SpircHandler::EventType::DISC:
centralAudioBuffer->clearBuffer(); centralAudioBuffer->clearBuffer();
xTimerStop(trackTimer, portMAX_DELAY);
cmdHandler(CSPOT_DISC); cmdHandler(CSPOT_DISC);
chunker->teardown(); chunker->teardown();
break; break;
@@ -306,6 +260,7 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
break; break;
} }
case cspot::SpircHandler::EventType::DEPLETED: case cspot::SpircHandler::EventType::DEPLETED:
trackStatus = TRACK_END;
CSPOT_LOG(info, "playlist ended, no track left to play"); CSPOT_LOG(info, "playlist ended, no track left to play");
break; break;
case cspot::SpircHandler::EventType::VOLUME: case cspot::SpircHandler::EventType::VOLUME:
@@ -318,18 +273,38 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
} }
void cspotPlayer::trackHandler(void) { void cspotPlayer::trackHandler(void) {
if (playback) { // this is just informative
uint32_t remains; auto trackInfo = spirc->getTrackPlayer()->getCurrentTrackInfo();
auto trackInfo = spirc->getTrackPlayer()->getCurrentTrackInfo(); uint32_t remains;
// if this is not first track, estimate when the current one will finish cmdHandler(CSPOT_QUERY_REMAINING, &remains);
cmdHandler(CSPOT_REMAINING, &remains); CSPOT_LOG(info, "next track <%s> will play in %d ms", trackInfo.name.c_str(), remains);
if (remains > 100) xTimerChangePeriod(trackTimer, pdMS_TO_TICKS(remains), portMAX_DELAY);
else trackNotify = true; // inform sink of track beginning
CSPOT_LOG(info, "next track <%s> in cspot buffers, remaining %d ms", trackInfo.name.c_str(), remains); trackStatus = TRACK_NOTIFY;
} else { cmdHandler(CSPOT_TRACK_MARK);
trackNotify = true; }
playback = true;
} void cspotPlayer::command(cspot_event_t event) {
if (!spirc) return;
// switch...case consume a ton of extra .rodata
if (event == CSPOT_PREV) spirc->previousSong();
else if (event == CSPOT_NEXT) spirc->nextSong();
else if (event == CSPOT_TOGGLE) spirc->setPause(!chunker->isPaused);
else if (event == CSPOT_STOP || event == CSPOT_PAUSE) spirc->setPause(true);
else if (event == CSPOT_PLAY) spirc->setPause(false);
else if (event == CSPOT_DISC) spirc->disconnect();
else if (event == CSPOT_VOLUME_UP) {
volume += (UINT16_MAX / 50);
volume = std::min(volume, UINT16_MAX);
cmdHandler(CSPOT_VOLUME, volume);
spirc->setRemoteVolume(volume);
} else if (event == CSPOT_VOLUME_DOWN) {
volume -= (UINT16_MAX / 50);
volume = std::max(volume, 0);
cmdHandler(CSPOT_VOLUME, volume);
spirc->setRemoteVolume(volume);
}
} }
void cspotPlayer::runTask() { void cspotPlayer::runTask() {
@@ -372,7 +347,6 @@ void cspotPlayer::runTask() {
// Auth successful // Auth successful
if (token.size() > 0) { if (token.size() > 0) {
trackTimer = xTimerCreate("trackTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, trackTimerHandler);
spirc = std::make_unique<cspot::SpircHandler>(ctx); spirc = std::make_unique<cspot::SpircHandler>(ctx);
// set call back to calculate a hash on trackId // set call back to calculate a hash on trackId
@@ -399,20 +373,38 @@ void cspotPlayer::runTask() {
return dataHandler(data, bytes); return dataHandler(data, bytes);
}); });
// set volume at connection
cmdHandler(CSPOT_VOLUME, volume);
// exit when player has stopped (received a DISC) // exit when player has stopped (received a DISC)
while (chunker->isRunning) { while (chunker->isRunning) {
ctx->session->handlePacket(); ctx->session->handlePacket();
// inform Spotify that next track has started (don't need to be super accurate) // low-accuracy polling events
if (trackNotify) { if (trackStatus == TRACK_NOTIFY) {
CSPOT_LOG(info, "next track's audio has reached DAC"); // inform Spotify that next track has started (don't need to be super accurate)
spirc->notifyAudioReachedPlayback(); uint32_t started;
trackNotify = false; cmdHandler(CSPOT_QUERY_STARTED, &started);
if (started) {
CSPOT_LOG(info, "next track's audio has reached DAC");
spirc->notifyAudioReachedPlayback();
trackStatus = TRACK_STREAM;
}
} else if (trackStatus == TRACK_END) {
// wait for end of last track
uint32_t remains;
cmdHandler(CSPOT_QUERY_REMAINING, &remains);
if (!remains) {
CSPOT_LOG(info, "last track finished");
trackStatus = TRACK_INIT;
cmdHandler(CSPOT_STOP);
spirc->setPause(true);
}
} }
} }
xTimerDelete(trackTimer, portMAX_DELAY);
spirc->disconnect(); spirc->disconnect();
spirc.reset();
CSPOT_LOG(info, "disconnecting player %s", name.c_str()); CSPOT_LOG(info, "disconnecting player %s", name.c_str());
} }
@@ -423,7 +415,7 @@ void cspotPlayer::runTask() {
token.clear(); token.clear();
// update volume when we disconnect // update volume when we disconnect
cJSON *item, *config = config_alloc_get_cjson("cspot_config"); cJSON *config = config_alloc_get_cjson("cspot_config");
cJSON_DeleteItemFromObject(config, "volume"); cJSON_DeleteItemFromObject(config, "volume");
cJSON_AddNumberToObject(config, "volume", volume); cJSON_AddNumberToObject(config, "volume", volume);
config_set_cjson_str_and_free("cspot_config", config); config_set_cjson_str_and_free("cspot_config", config);
@@ -446,42 +438,6 @@ struct cspot_s* cspot_create(const char *name, httpd_handle_t server, int port,
*/ */
bool cspot_cmd(struct cspot_s* ctx, cspot_event_t event, void *param) { bool cspot_cmd(struct cspot_s* ctx, cspot_event_t event, void *param) {
// we might have no controller left player->command(event);
/*
if (!spircController.use_count()) return false;
switch(event) {
case CSPOT_PREV:
spircController->prevSong();
break;
case CSPOT_NEXT:
spircController->nextSong();
break;
case CSPOT_TOGGLE:
spircController->playToggle();
break;
case CSPOT_PAUSE:
spircController->setPause(true);
break;
case CSPOT_PLAY:
spircController->setPause(false);
break;
case CSPOT_DISC:
spircController->disconnect();
break;
case CSPOT_STOP:
spircController->stopPlayer();
break;
case CSPOT_VOLUME_UP:
spircController->adjustVolume(MAX_VOLUME / 100 + 1);
break;
case CSPOT_VOLUME_DOWN:
spircController->adjustVolume(-(MAX_VOLUME / 100 + 1));
break;
default:
break;
}
*/
return true; return true;
} }

View File

@@ -17,9 +17,17 @@ option(BELL_SINK_ALSA "Enable ALSA audio sink" OFF)
option(BELL_SINK_PORTAUDIO "Enable PortAudio sink" OFF) option(BELL_SINK_PORTAUDIO "Enable PortAudio sink" OFF)
# cJSON wrapper # cJSON wrapper
option(BELL_DISABLE_CJSON "Disable cJSON and JSONObject completely" OFF) option(BELL_ONLY_CJSON "Use only cJSON, not Nlohmann")
set(BELL_EXTERNAL_CJSON "" CACHE STRING "External cJSON library target name, optional") set(BELL_EXTERNAL_CJSON "" CACHE STRING "External cJSON library target name, optional")
# vorbis
set(BELL_EXTERNAL_VORBIS "" CACHE STRING "External Vorbis library target name, optional")
option(BELL_VORBIS_FLOAT "Use floating point Vorbis API" OFF)
# fmt & regex
option(BELL_DISABLE_FMT "Don't use std::fmt (saves space)" OFF)
option(BELL_DISABLE_REGEX "Don't use std::regex (saves space)" OFF)
# disable json tests # disable json tests
set(JSON_BuildTests OFF CACHE INTERNAL "") set(JSON_BuildTests OFF CACHE INTERNAL "")
@@ -46,13 +54,16 @@ if(NOT BELL_DISABLE_CODECS)
endif() endif()
message(STATUS " Disable built-in audio sinks: ${BELL_DISABLE_SINKS}") message(STATUS " Disable built-in audio sinks: ${BELL_DISABLE_SINKS}")
message(STATUS " Use Vorbis float version: ${BELL_VORBIS_FLOAT}")
if(NOT BELL_DISABLE_SINKS) if(NOT BELL_DISABLE_SINKS)
message(STATUS " - ALSA sink: ${BELL_SINK_ALSA}") message(STATUS " - ALSA sink: ${BELL_SINK_ALSA}")
message(STATUS " - PortAudio sink: ${BELL_SINK_PORTAUDIO}") message(STATUS " - PortAudio sink: ${BELL_SINK_PORTAUDIO}")
endif() endif()
message(STATUS " Disable cJSON and JSONObject: ${BELL_DISABLE_CJSON}") message(STATUS " Use cJSON only: ${BELL_ONLY_CJSON}")
message(STATUS " Disable Fmt: ${BELL_DISABLE_FMT}")
message(STATUS " Disable Regex: ${BELL_DISABLE_REGEX}")
# Include nanoPB library # Include nanoPB library
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/external/nanopb/extra") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/external/nanopb/extra")
@@ -212,7 +223,7 @@ else()
list(REMOVE_ITEM SOURCES "${IO_DIR}/EncodedAudioStream.cpp") list(REMOVE_ITEM SOURCES "${IO_DIR}/EncodedAudioStream.cpp")
endif() endif()
if(BELL_EXTERNAL_VORBIS) if(NOT BELL_EXTERNAL_VORBIS STREQUAL "")
message(STATUS "Using external Vorbis codec ${BELL_EXTERNAL_VORBIS}") message(STATUS "Using external Vorbis codec ${BELL_EXTERNAL_VORBIS}")
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_VORBIS}) list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_VORBIS})
else() else()
@@ -254,21 +265,19 @@ if(NOT BELL_DISABLE_SINKS)
list(APPEND SOURCES ${SINK_SOURCES}) list(APPEND SOURCES ${SINK_SOURCES})
endif() endif()
if(BELL_DISABLE_CJSON) if(NOT BELL_ONLY_CJSON)
list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/main/io/JSONObject.cpp")
else()
add_subdirectory(external/nlohmann_json) add_subdirectory(external/nlohmann_json)
list(APPEND EXTRA_LIBS nlohmann_json::nlohmann_json) list(APPEND EXTRA_LIBS nlohmann_json::nlohmann_json)
if(BELL_EXTERNAL_CJSON)
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
else()
list(APPEND SOURCES "external/cJSON/cJSON.c")
list(APPEND EXTRA_INCLUDES "external/cJSON")
endif()
endif() endif()
if (BELL_DISABLE_FMT) if(BELL_EXTERNAL_CJSON)
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
else() else()
list(APPEND SOURCES "external/cJSON/cJSON.c")
list(APPEND EXTRA_INCLUDES "external/cJSON")
endif()
if (NOT BELL_DISABLE_FMT)
list(APPEND EXTRA_INCLUDES "external/fmt/include") list(APPEND EXTRA_INCLUDES "external/fmt/include")
endif() endif()
@@ -306,6 +315,18 @@ if(BELL_VORBIS_FLOAT)
target_compile_definitions(bell PUBLIC BELL_VORBIS_FLOAT) target_compile_definitions(bell PUBLIC BELL_VORBIS_FLOAT)
endif() endif()
if(BELL_DISABLE_FMT)
target_compile_definitions(bell PUBLIC BELL_DISABLE_FMT)
endif()
if(BELL_DISABLE_REGEX)
target_compile_definitions(bell PUBLIC BELL_DISABLE_REGEX)
endif()
if(BELL_ONLY_CJSON)
target_compile_definitions(bell PUBLIC BELL_ONLY_CJSON)
endif()
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "SunOS") if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "SunOS")
target_compile_definitions(bell PUBLIC PB_NO_STATIC_ASSERT) target_compile_definitions(bell PUBLIC PB_NO_STATIC_ASSERT)
endif() endif()

View File

@@ -1 +1,38 @@
#include "URLParser.h" #include "URLParser.h"
namespace bell {
#ifdef BELL_DISABLE_REGEX
void URLParser::parse(const char* url, std::vector<std::string>& match) {
match[0] = url;
char scratch[512];
// get schema [http(s)]://
if (sscanf(url, "%[^:]:/", scratch) > 0) match[1] = scratch;
// get host http(s)://[host]
if (sscanf(url, "htt%*[^:]://%512[^/#]", scratch) > 0) match[2] = scratch;
// get the path
url = strstr(url, match[2].c_str());
if (!url || *url == '\0') return;
url += match[2].size();
if (sscanf(url, "/%512[^?]", scratch) > 0) match[3] = scratch;
// get the query
if (match[3].size()) url += match[3].size() + 1;
if (sscanf(url, "?%512[^#]", scratch) > 0) match[4] = scratch;
// get the hash
if (match[4].size()) url += match[4].size() + 1;
if (sscanf(url, "#%512s", scratch) > 0) match[5] = scratch;
// fix the acquired items
match[3] = "/" + match[3];
if (match[4].size()) match[4] = "?" + match[4];
}
#else
const std::regex URLParser::urlParseRegex = std::regex(
"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(\\?(?:[^#]*))?(#(?:.*))?");
#endif
}

View File

@@ -15,7 +15,9 @@
#include "ByteStream.h" #include "ByteStream.h"
#include "SocketStream.h" #include "SocketStream.h"
#include "URLParser.h" #include "URLParser.h"
#ifndef BELL_DISABLE_FMT
#include "fmt/core.h" #include "fmt/core.h"
#endif
#include "picohttpparser.h" #include "picohttpparser.h"
namespace bell { namespace bell {
@@ -29,11 +31,19 @@ class HTTPClient {
// Helper over ValueHeader, formatting a HTTP bytes range // Helper over ValueHeader, formatting a HTTP bytes range
struct RangeHeader { struct RangeHeader {
static ValueHeader range(int32_t from, int32_t to) { static ValueHeader range(int32_t from, int32_t to) {
#ifndef BELL_DISABLE_FMT
return ValueHeader{"Range", fmt::format("bytes={}-{}", from, to)}; return ValueHeader{"Range", fmt::format("bytes={}-{}", from, to)};
#else
return ValueHeader{"Range", "bytes=" + std::to_string(from) + "-" + std::to_string(to) };
#endif
} }
static ValueHeader last(int32_t nbytes) { static ValueHeader last(int32_t nbytes) {
#ifndef BELL_DISABLE_FMT
return ValueHeader{"Range", fmt::format("bytes=-{}", nbytes)}; return ValueHeader{"Range", fmt::format("bytes=-{}", nbytes)};
#else
return ValueHeader{"Range", "bytes=-" + std::to_string(nbytes)};
#endif
} }
}; };

View File

@@ -58,16 +58,23 @@ class URLParser {
std::string schema = "http"; std::string schema = "http";
std::string path; std::string path;
std::regex urlParseRegex = std::regex( #ifdef BELL_DISABLE_REGEX
"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(\\?(?:[^#]*))?(#(?:.*))?"); void parse(const char* url, std::vector<std::string>& match);
#else
static const std::regex urlParseRegex;
#endif
static URLParser parse(const std::string& url) { static URLParser parse(const std::string& url) {
URLParser parser; URLParser parser;
// apply parser.urlParseRegex to url // apply parser.urlParseRegex to url
#ifdef BELL_DISABLE_REGEX
std::vector<std::string> match(6);
parser.parse(url.c_str(), match);
#else
std::cmatch match; std::cmatch match;
std::regex_match(url.c_str(), match, parser.urlParseRegex); std::regex_match(url.c_str(), match, parser.urlParseRegex);
#endif
if (match.size() < 3) { if (match.size() < 3) {
throw std::invalid_argument("Invalid URL"); throw std::invalid_argument("Invalid URL");

View File

@@ -2,12 +2,14 @@
#include <map> #include <map>
#include <string> #include <string>
#include <memory>
namespace bell { namespace bell {
class MDNSService { class MDNSService {
public: public:
static void* registerService( virtual ~MDNSService() { }
static std::unique_ptr<MDNSService> registerService(
const std::string &serviceName, const std::string &serviceName,
const std::string &serviceType, const std::string &serviceType,
const std::string &serviceProto, const std::string &serviceProto,
@@ -15,7 +17,7 @@ class MDNSService {
int servicePort, int servicePort,
const std::map<std::string, std::string> txtData const std::map<std::string, std::string> txtData
); );
static void unregisterService(void* service); virtual void unregisterService() = 0;
}; };
} // namespace bell } // namespace bell

View File

@@ -4,11 +4,20 @@
using namespace bell; using namespace bell;
class implMDNSService : public MDNSService {
private:
DNSServiceRef* service;
public:
implMDNSService(DNSServiceRef* service) : service(service) { }
void unregisterService() { DNSServiceRefDeallocate(*service); }
};
/** /**
* MacOS implementation of MDNSService. * MacOS implementation of MDNSService.
* @see https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister * @see https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister
**/ **/
void* MDNSService::registerService( std::unique_ptr<MDNSService> MDNSService::registerService(
const std::string& serviceName, const std::string& serviceName,
const std::string& serviceType, const std::string& serviceType,
const std::string& serviceProto, const std::string& serviceProto,
@@ -37,9 +46,5 @@ void* MDNSService::registerService(
NULL /* context */ NULL /* context */
); );
TXTRecordDeallocate(&txtRecord); TXTRecordDeallocate(&txtRecord);
return ref; return std::make_unique<implMDNSService>(ref);
}
void MDNSService::unregisterService(void* ref) {
DNSServiceRefDeallocate((DNSServiceRef)ref);
} }

View File

@@ -5,11 +5,22 @@
using namespace bell; using namespace bell;
class implMDNSService : public MDNSService {
private:
const std::string type;
const std::string proto;
void unregisterService() { mdns_service_remove(type.c_str(), proto.c_str()); }
public:
implMDNSService(std::string type, std::string proto) : type(type), proto(proto) { };
};
/** /**
* ESP32 implementation of MDNSService * ESP32 implementation of MDNSService
* @see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mdns.html * @see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mdns.html
**/ **/
void* MDNSService::registerService(
std::unique_ptr<MDNSService> MDNSService::registerService(
const std::string& serviceName, const std::string& serviceName,
const std::string& serviceType, const std::string& serviceType,
const std::string& serviceProto, const std::string& serviceProto,
@@ -35,5 +46,5 @@ void* MDNSService::registerService(
txtItems.size() /* num_items */ txtItems.size() /* num_items */
); );
return NULL; return std::make_unique<implMDNSService>(serviceType, serviceProto);
} }

View File

@@ -21,19 +21,55 @@
using namespace bell; using namespace bell;
#ifndef BELL_DISABLE_AVAHI #ifndef BELL_DISABLE_AVAHI
static AvahiClient *avahiClient = NULL;
static AvahiSimplePoll *avahiPoll = NULL;
static void groupHandler(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { } static void groupHandler(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { }
#endif #endif
static in_addr_t host = INADDR_ANY; class implMDNSService : public MDNSService {
static struct mdnsd *mdnsServer; private:
#ifndef BELL_DISABLE_AVAHI
AvahiEntryGroup *avahiGroup;
#endif
struct mdns_service* service;
public:
#ifndef BELL_DISABLE_AVAHI
static AvahiClient *avahiClient;
static AvahiSimplePoll *avahiPoll;
#endif
static struct mdnsd* mdnsServer;
static in_addr_t host;
implMDNSService(struct mdns_service* service) : service(service) { };
#ifndef BELL_DISABLE_AVAHI
implMDNSService(AvahiEntryGroup *avahiGroup) : avahiGroup(avahiGroup) { };
#endif
void unregisterService();
};
struct mdnsd* implMDNSService::mdnsServer = NULL;
in_addr_t implMDNSService::host = INADDR_ANY;
#ifndef BELL_DISABLE_AVAHI
AvahiClient* implMDNSService::avahiClient = NULL;
AvahiSimplePoll* implMDNSService::avahiPoll = NULL;
#endif
/** /**
* Linux implementation of MDNSService using avahi. * Linux implementation of MDNSService using avahi.
* @see https://www.avahi.org/doxygen/html/ * @see https://www.avahi.org/doxygen/html/
**/ **/
void* MDNSService::registerService(
void implMDNSService::unregisterService() {
#ifndef BELL_DISABLE_AVAHI
if (avahiGroup) {
avahi_entry_group_free(avahiGroup);
} else
#endif
{
mdns_service_remove(implMDNSService::mdnsServer, service);
}
}
std::unique_ptr<MDNSService> MDNSService::registerService(
const std::string& serviceName, const std::string& serviceName,
const std::string& serviceType, const std::string& serviceType,
const std::string& serviceProto, const std::string& serviceProto,
@@ -43,19 +79,19 @@ void* MDNSService::registerService(
) { ) {
#ifndef BELL_DISABLE_AVAHI #ifndef BELL_DISABLE_AVAHI
// try avahi first if available // try avahi first if available
if (!avahiPoll) { if (!implMDNSService::avahiPoll) {
avahiPoll = avahi_simple_poll_new(); implMDNSService::avahiPoll = avahi_simple_poll_new();
} }
if (avahiPoll && !avahiClient) { if (implMDNSService::avahiPoll && !implMDNSService::avahiClient) {
avahiClient = avahi_client_new(avahi_simple_poll_get(avahiPoll), implMDNSService::avahiClient = avahi_client_new(avahi_simple_poll_get(implMDNSService::avahiPoll),
AvahiClientFlags(0), NULL, NULL, NULL); AvahiClientFlags(0), NULL, NULL, NULL);
} }
AvahiEntryGroup *avahiGroup; AvahiEntryGroup *avahiGroup;
if (avahiClient && if (implMDNSService::avahiClient &&
(avahiGroup = avahi_entry_group_new(avahiClient, groupHandler, NULL)) == NULL) { (avahiGroup = avahi_entry_group_new(implMDNSService::avahiClient, groupHandler, NULL)) == NULL) {
BELL_LOG(error, "MDNS", "cannot create service %s", serviceName.c_str()); BELL_LOG(error, "MDNS", "cannot create service %s", serviceName.c_str());
} }
@@ -80,7 +116,7 @@ void* MDNSService::registerService(
avahi_entry_group_free(avahiGroup); avahi_entry_group_free(avahiGroup);
} else { } else {
BELL_LOG(info, "MDNS", "using avahi for %s", serviceName.c_str()); BELL_LOG(info, "MDNS", "using avahi for %s", serviceName.c_str());
return avahiGroup; return std::make_unique<implMDNSService>(avahiGroup);
} }
} }
#endif #endif
@@ -92,39 +128,39 @@ void* MDNSService::registerService(
if (serviceHost.size()) { if (serviceHost.size()) {
struct hostent *h = gethostbyname(serviceHost.c_str()); struct hostent *h = gethostbyname(serviceHost.c_str());
if (h) { if (h) {
memcpy(&host, h->h_addr_list[0], 4); memcpy(&implMDNSService::host, h->h_addr_list[0], 4);
} }
} }
// try go guess ifaddr if we have nothing as listening to INADDR_ANY usually does not work // try go guess ifaddr if we have nothing as listening to INADDR_ANY usually does not work
if (host == INADDR_ANY && getifaddrs(&ifaddr) != -1) { if (implMDNSService::host == INADDR_ANY && getifaddrs(&ifaddr) != -1) {
for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET || if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET ||
!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST) || !(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST) ||
(ifa->ifa_flags & IFF_LOOPBACK)) continue; (ifa->ifa_flags & IFF_LOOPBACK)) continue;
host = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr; implMDNSService::host = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr;
break; break;
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
} }
if (!mdnsServer) { if (!implMDNSService::mdnsServer) {
char hostname[256]; char hostname[256];
struct in_addr addr; struct in_addr addr;
// it's the same, but who knows.. // it's the same, but who knows..
addr.s_addr = host; addr.s_addr = implMDNSService::host;
gethostname(hostname, sizeof(hostname)); gethostname(hostname, sizeof(hostname));
mdnsServer = mdnsd_start(addr, false); implMDNSService::mdnsServer = mdnsd_start(addr, false);
if (mdnsServer) { if (implMDNSService::mdnsServer) {
mdnsd_set_hostname(mdnsServer, hostname, addr); mdnsd_set_hostname(implMDNSService::mdnsServer, hostname, addr);
} }
} }
if (mdnsServer) { if (implMDNSService::mdnsServer) {
std::vector<const char*> txt; std::vector<const char*> txt;
std::vector<std::unique_ptr<std::string>> txtStr; std::vector<std::unique_ptr<std::string>> txtStr;
@@ -137,25 +173,17 @@ void* MDNSService::registerService(
txt.push_back(NULL); txt.push_back(NULL);
std::string type(serviceType + "." + serviceProto + ".local"); std::string type(serviceType + "." + serviceProto + ".local");
BELL_LOG(info, "MDNS", "using build-in mDNS for %s", serviceName.c_str()); BELL_LOG(info, "MDNS", "using built-in mDNS for %s", serviceName.c_str());
struct mdns_service* mdnsService = mdnsd_register_svc(mdnsServer, serviceName.c_str(), struct mdns_service* mdnsService = mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
type.c_str(), servicePort, NULL, txt.data()); type.c_str(), servicePort, NULL, txt.data());
if (mdnsService) { if (mdnsService) {
return mdnsService; auto service = mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
type.c_str(), servicePort, NULL, txt.data());
return std::make_unique<implMDNSService>(service);
} }
} }
BELL_LOG(error, "MDNS", "cannot start any mDNS listener for %s", serviceName.c_str()); BELL_LOG(error, "MDNS", "cannot start any mDNS listener for %s", serviceName.c_str());
return NULL; return NULL;
} }
void MDNSService::unregisterService(void* service) {
#ifndef BELL_DISABLE_AVAHI
if (avahiClient) {
avahi_entry_group_free((AvahiEntryGroup*)service);
} else
#endif
{
mdns_service_remove(mdnsServer, (mdns_service*)service);
}
}

View File

@@ -17,13 +17,23 @@
using namespace bell; using namespace bell;
static struct mdnsd *mdnsService; class implMDNSService : public MDNSService {
private:
struct mdns_service* service;
void unregisterService(void) { mdns_service_remove(implMDNSService::mdnsServer, service); };
public:
static struct mdnsd* mdnsServer;
implMDNSService(struct mdns_service* service) : service(service) { };
};
/** /**
* Win32 implementation of MDNSService * Win32 implementation of MDNSService
**/ **/
void* MDNSService::registerService( struct mdnsd* implMDNSService::mdnsServer = NULL;
std::unique_ptr<MDNSService> MDNSService::registerService(
const std::string& serviceName, const std::string& serviceName,
const std::string& serviceType, const std::string& serviceType,
const std::string& serviceProto, const std::string& serviceProto,
@@ -31,7 +41,7 @@ void* MDNSService::registerService(
int servicePort, int servicePort,
const std::map<std::string, std::string> txtData const std::map<std::string, std::string> txtData
) { ) {
if (!mdnsService) { if (!implMDNSService::mdnsServer) {
char hostname[128]; char hostname[128];
gethostname(hostname, sizeof(hostname)); gethostname(hostname, sizeof(hostname));
@@ -49,14 +59,14 @@ void* MDNSService::registerService(
if (adapter->FirstGatewayAddress && unicast->Address.lpSockaddr->sa_family == AF_INET) { if (adapter->FirstGatewayAddress && unicast->Address.lpSockaddr->sa_family == AF_INET) {
host = (struct sockaddr_in*)unicast->Address.lpSockaddr; host = (struct sockaddr_in*)unicast->Address.lpSockaddr;
BELL_LOG(info, "mdns", "mDNS on interface %s", inet_ntoa(host->sin_addr)); BELL_LOG(info, "mdns", "mDNS on interface %s", inet_ntoa(host->sin_addr));
mdnsService = mdnsd_start(host->sin_addr, false); implMDNSService::mdnsServer = mdnsd_start(host->sin_addr, false);
break; break;
} }
} }
} }
assert(mdnsService); assert(implMDNSService::mdnsServer);
mdnsd_set_hostname(mdnsService, hostname, host->sin_addr); mdnsd_set_hostname(implMDNSService::mdnsServer, hostname, host->sin_addr);
free(adapters); free(adapters);
} }
@@ -71,9 +81,9 @@ void* MDNSService::registerService(
txt.push_back(NULL); txt.push_back(NULL);
std::string type(serviceType + "." + serviceProto + ".local"); std::string type(serviceType + "." + serviceProto + ".local");
return mdnsd_register_svc(mdnsService, serviceName.c_str(), type.c_str(), servicePort, NULL, txt.data());
}
void MDNSService::unregisterService(void* service) { auto service = mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
mdns_service_remove(mdnsService, (mdns_service*)service); type.c_str(), servicePort, NULL, txt.data());
return std::make_unique<implMDNSService>(service);
} }

View File

@@ -11,7 +11,6 @@
#include <vector> #include <vector>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
//#include "esp_mac.h"
#include "esp_system.h" #include "esp_system.h"
#endif #endif

View File

@@ -22,7 +22,7 @@ extern "C" {
#define DH_KEY_SIZE 96 #define DH_KEY_SIZE 96
static unsigned char DHPrime[] = { const static unsigned char DHPrime[] = {
/* Well-known Group 1, 768-bit prime */ /* Well-known Group 1, 768-bit prime */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9,
0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6,

View File

@@ -4,7 +4,11 @@
#include <string> #include <string>
#include "HTTPClient.h" #include "HTTPClient.h"
#ifdef BELL_ONLY_CJSON
#include "cJSON.h"
#else
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#endif
namespace cspot { namespace cspot {
class ApResolve { class ApResolve {

View File

@@ -10,7 +10,6 @@
#include "CSpotContext.h" #include "CSpotContext.h"
#include "AccessKeyFetcher.h" #include "AccessKeyFetcher.h"
namespace cspot { namespace cspot {
class CDNTrackStream { class CDNTrackStream {

View File

@@ -3,7 +3,9 @@
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <memory> #include <memory>
#ifndef BELL_ONLY_CJSON
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#endif
#include <vector> #include <vector>
#include "ConstantParameters.h" #include "ConstantParameters.h"

View File

@@ -41,13 +41,24 @@ void AccessKeyFetcher::getAccessKey(AccessKeyFetcher::Callback callback) {
if (res.fail) return; if (res.fail) return;
char* accessKeyJson = (char*)res.parts[0].data(); char* accessKeyJson = (char*)res.parts[0].data();
auto accessJSON = std::string(accessKeyJson, strrchr(accessKeyJson, '}') - accessKeyJson + 1); auto accessJSON = std::string(accessKeyJson, strrchr(accessKeyJson, '}') - accessKeyJson + 1);
#ifdef BELL_ONLY_CJSON
cJSON* jsonBody = cJSON_Parse(accessJSON.c_str());
this->accessKey = cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring;
int expiresIn = cJSON_GetObjectItem(jsonBody, "expiresIn")->valueint;
#else
auto jsonBody = nlohmann::json::parse(accessJSON); auto jsonBody = nlohmann::json::parse(accessJSON);
this->accessKey = jsonBody["accessToken"]; this->accessKey = jsonBody["accessToken"];
int expiresIn = jsonBody["expiresIn"]; int expiresIn = jsonBody["expiresIn"];
#endif
expiresIn = expiresIn / 2; // Refresh token before it expires expiresIn = expiresIn / 2; // Refresh token before it expires
this->expiresAt = this->expiresAt =
timeProvider->getSyncedTimestamp() + (expiresIn * 1000); timeProvider->getSyncedTimestamp() + (expiresIn * 1000);
#ifdef BELL_ONLY_CJSON
callback(cJSON_GetObjectItem(jsonBody, "accessToken")->valuestring);
cJSON_Delete(jsonBody);
#else
callback(jsonBody["accessToken"]); callback(jsonBody["accessToken"]);
#endif
}); });
} }

View File

@@ -18,6 +18,13 @@ std::string ApResolve::fetchFirstApAddress()
std::string_view responseStr = request->body(); std::string_view responseStr = request->body();
// parse json with nlohmann // parse json with nlohmann
#if BELL_ONLY_CJSON
cJSON* json = cJSON_Parse(responseStr.data());
auto ap_string = std::string(cJSON_GetArrayItem(cJSON_GetObjectItem(json, "ap_list"), 0)->valuestring);
cJSON_Delete(json);
return ap_string;
#else
auto json = nlohmann::json::parse(responseStr); auto json = nlohmann::json::parse(responseStr);
return json["ap_list"][0]; return json["ap_list"][0];
#endif
} }

View File

@@ -38,8 +38,14 @@ void CDNTrackStream::fetchFile(const std::vector<uint8_t>& trackId,
std::string_view result = req->body(); std::string_view result = req->body();
#ifdef BELL_ONLY_CJSON
cJSON* jsonResult = cJSON_Parse(result.data());
std::string cdnUrl = cJSON_GetArrayItem(cJSON_GetObjectItem(jsonResult, "cdnurl"), 0)->valuestring;
cJSON_Delete(jsonResult);
#else
auto jsonResult = nlohmann::json::parse(result); auto jsonResult = nlohmann::json::parse(result);
std::string cdnUrl = jsonResult["cdnurl"][0]; std::string cdnUrl = jsonResult["cdnurl"][0];
#endif
if (this->status != Status::FAILED) { if (this->status != Status::FAILED) {
this->cdnUrl = cdnUrl; this->cdnUrl = cdnUrl;

View File

@@ -1,6 +1,9 @@
#include "LoginBlob.h" #include "LoginBlob.h"
#include "ConstantParameters.h" #include "ConstantParameters.h"
#include "Logger.h" #include "Logger.h"
#ifdef BELL_ONLY_CJSON
#include "cJSON.h"
#endif
using namespace cspot; using namespace cspot;
@@ -125,21 +128,46 @@ void LoginBlob::loadUserPass(const std::string& username,
} }
void LoginBlob::loadJson(const std::string& json) { void LoginBlob::loadJson(const std::string& json) {
#ifdef BELL_ONLY_CJSON
cJSON* root = cJSON_Parse(json.c_str());
cJSON* item = cJSON_GetObjectItem(root, "authType");
this->authType = item->valueint;
item = cJSON_GetObjectItem(root, "username");
this->username = item->valuestring;
item = cJSON_GetObjectItem(root, "authData");
std::string authDataObject = item->valuestring;
cJSON_Delete(root);
#else
auto root = nlohmann::json::parse(json); auto root = nlohmann::json::parse(json);
this->authType = root["authType"]; this->authType = root["authType"];
this->username = root["username"]; this->username = root["username"];
std::string authDataObject = root["authData"]; std::string authDataObject = root["authData"];
this->authData = crypto->base64Decode(authDataObject); this->authData = crypto->base64Decode(authDataObject);
#endif
} }
std::string LoginBlob::toJson() { std::string LoginBlob::toJson() {
#ifdef BELL_ONLY_CJSON
cJSON* json_obj = cJSON_CreateObject();
cJSON_AddStringToObject(json_obj, "authData", crypto->base64Encode(authData).c_str());
cJSON_AddNumberToObject(json_obj, "authType", this->authType);
cJSON_AddStringToObject(json_obj, "username", this->username.c_str());
char *str = cJSON_PrintUnformatted(json_obj);
cJSON_Delete(json_obj);
std::string json_objStr(str);
free(str);
return json_objStr;
#else
nlohmann::json obj; nlohmann::json obj;
obj["authData"] = crypto->base64Encode(authData); obj["authData"] = crypto->base64Encode(authData);
obj["authType"] = this->authType; obj["authType"] = this->authType;
obj["username"] = this->username; obj["username"] = this->username;
return obj.dump(); return obj.dump();
#endif
} }
void LoginBlob::loadZeroconfQuery( void LoginBlob::loadZeroconfQuery(
@@ -164,7 +192,35 @@ std::string LoginBlob::buildZeroconfInfo() {
// Encode publicKey into base64 // Encode publicKey into base64
auto encodedKey = crypto->base64Encode(crypto->publicKey); auto encodedKey = crypto->base64Encode(crypto->publicKey);
#ifdef BELL_ONLY_CJSON
cJSON* json_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(json_obj, "status", 101);
cJSON_AddStringToObject(json_obj, "statusString", "OK");
cJSON_AddStringToObject(json_obj, "version", cspot::protocolVersion);
cJSON_AddStringToObject(json_obj, "libraryVersion", cspot::swVersion);
cJSON_AddStringToObject(json_obj, "accountReq", "PREMIUM");
cJSON_AddStringToObject(json_obj, "brandDisplayName", cspot::brandName);
cJSON_AddStringToObject(json_obj, "modelDisplayName", name.c_str());
cJSON_AddStringToObject(json_obj, "voiceSupport", "NO");
cJSON_AddStringToObject(json_obj, "availability", this->username.c_str());
cJSON_AddNumberToObject(json_obj, "productID", 0);
cJSON_AddStringToObject(json_obj, "tokenType", "default");
cJSON_AddStringToObject(json_obj, "groupStatus", "NONE");
cJSON_AddStringToObject(json_obj, "resolverVersion", "0");
cJSON_AddStringToObject(json_obj, "scope", "streaming,client-authorization-universal");
cJSON_AddStringToObject(json_obj, "activeUser", "");
cJSON_AddStringToObject(json_obj, "deviceID", deviceId.c_str());
cJSON_AddStringToObject(json_obj, "remoteName", name.c_str());
cJSON_AddStringToObject(json_obj, "publicKey", encodedKey.c_str());
cJSON_AddStringToObject(json_obj, "deviceType", "deviceType");
char *str = cJSON_PrintUnformatted(json_obj);
cJSON_Delete(json_obj);
std::string json_objStr(str);
free(str);
return json_objStr;
#else
nlohmann::json obj; nlohmann::json obj;
obj["status"] = 101; obj["status"] = 101;
obj["statusString"] = "OK"; obj["statusString"] = "OK";
@@ -188,6 +244,7 @@ std::string LoginBlob::buildZeroconfInfo() {
obj["deviceType"] = "SPEAKER"; obj["deviceType"] = "SPEAKER";
return obj.dump(); return obj.dump();
#endif
} }
std::string LoginBlob::getDeviceId() { std::string LoginBlob::getDeviceId() {

View File

@@ -150,7 +150,7 @@ void TrackPlayer::runTask() {
dataCallback(pcmBuffer.data() + (ret - toWrite), toWrite, dataCallback(pcmBuffer.data() + (ret - toWrite), toWrite,
this->currentTrackStream->trackInfo.trackId, this->sequence); this->currentTrackStream->trackInfo.trackId, this->sequence);
if (written == 0) { if (written == 0) {
BELL_SLEEP_MS(10); BELL_SLEEP_MS(50);
} }
toWrite -= written; toWrite -= written;
} }

View File

@@ -135,7 +135,7 @@ static bool cmd_handler(cspot_event_t event, ...) {
case CSPOT_SEEK: case CSPOT_SEEK:
displayer_timer(DISPLAYER_ELAPSED, va_arg(args, int), -1); displayer_timer(DISPLAYER_ELAPSED, va_arg(args, int), -1);
break; break;
case CSPOT_TRACK: { case CSPOT_TRACK_INFO: {
uint32_t duration = va_arg(args, int); uint32_t duration = va_arg(args, int);
uint32_t offset = va_arg(args, int); uint32_t offset = va_arg(args, int);
char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*); char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*);

View File

@@ -16,8 +16,11 @@ extern "C"
#endif #endif
// STOP means remove playlist, FLUSH means flush audio buffer, DISC means bye-bye // STOP means remove playlist, FLUSH means flush audio buffer, DISC means bye-bye
typedef enum { CSPOT_START, CSPOT_DISC, CSPOT_FLUSH, CSPOT_STOP, CSPOT_PLAY, CSPOT_PAUSE, CSPOT_SEEK, CSPOT_TRACK, typedef enum { CSPOT_START, CSPOT_DISC, CSPOT_FLUSH, CSPOT_STOP, CSPOT_PLAY, CSPOT_PAUSE, CSPOT_SEEK,
CSPOT_VOLUME, CSPOT_VOLUME_UP, CSPOT_VOLUME_DOWN, CSPOT_NEXT, CSPOT_PREV, CSPOT_TOGGLE, CSPOT_REMAINING, CSPOT_NEXT, CSPOT_PREV, CSPOT_TOGGLE,
CSPOT_TRACK_INFO, CSPOT_TRACK_MARK,
CSPOT_VOLUME, CSPOT_VOLUME_UP, CSPOT_VOLUME_DOWN,
CSPOT_QUERY_STARTED, CSPOT_QUERY_REMAINING,
} cspot_event_t; } cspot_event_t;
typedef bool (*cspot_cmd_cb_t)(cspot_event_t event, ...); typedef bool (*cspot_cmd_cb_t)(cspot_event_t event, ...);

View File

@@ -351,6 +351,8 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t); output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
output.external = DECODE_CSPOT; output.external = DECODE_CSPOT;
output.frames_played = 0; output.frames_played = 0;
// in 1/10 of seconds
output.threshold = 25;
output.state = OUTPUT_STOPPED; output.state = OUTPUT_STOPPED;
sink_state = SINK_ABORT; sink_state = SINK_ABORT;
_buf_flush(outputbuf); _buf_flush(outputbuf);
@@ -386,10 +388,20 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
output.stop_time = gettime_ms(); output.stop_time = gettime_ms();
LOG_INFO("CSpot pause"); LOG_INFO("CSpot pause");
break; break;
case CSPOT_REMAINING: { case CSPOT_TRACK_MARK:
output.track_start = outputbuf->writep;
break;
case CSPOT_QUERY_REMAINING: {
uint32_t *remaining = va_arg(args, uint32_t*); uint32_t *remaining = va_arg(args, uint32_t*);
*remaining = (_buf_used(outputbuf) * 1000) / (output.current_sample_rate * BYTES_PER_FRAME); *remaining = (_buf_used(outputbuf) * 1000) / (output.current_sample_rate * BYTES_PER_FRAME);
break; break;
}
case CSPOT_QUERY_STARTED: {
uint32_t *started = va_arg(args, uint32_t*);
*started = output.track_started;
// this is a read_and_clear event
output.track_started = false;
break;
} }
case CSPOT_VOLUME: { case CSPOT_VOLUME: {
u32_t volume = va_arg(args, u32_t); u32_t volume = va_arg(args, u32_t);