mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-10 21:47:04 +03:00
new cspot/bell
This commit is contained in:
@@ -3,29 +3,41 @@
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <atomic>
|
||||
|
||||
namespace bell {
|
||||
class WrappedSemaphore;
|
||||
};
|
||||
namespace cspot {
|
||||
struct Context;
|
||||
|
||||
class AccessKeyFetcher {
|
||||
public:
|
||||
AccessKeyFetcher(std::shared_ptr<cspot::Context> ctx);
|
||||
~AccessKeyFetcher();
|
||||
|
||||
typedef std::function<void(std::string)> Callback;
|
||||
/**
|
||||
* @brief Checks if key is expired
|
||||
* @returns true when currently held access key is not valid
|
||||
*/
|
||||
bool isExpired();
|
||||
|
||||
void getAccessKey(Callback callback);
|
||||
/**
|
||||
* @brief Fetches a new access key
|
||||
* @remark In case the key is expired, this function blocks until a refresh is done.
|
||||
* @returns access key
|
||||
*/
|
||||
std::string getAccessKey();
|
||||
|
||||
/**
|
||||
* @brief Forces a refresh of the access key
|
||||
*/
|
||||
void updateAccessKey();
|
||||
|
||||
private:
|
||||
const std::string CLIENT_ID =
|
||||
"65b708073fc0480ea92a077233ca87bd"; // Spotify web client's client id
|
||||
const std::string SCOPES =
|
||||
"streaming,user-library-read,user-library-modify,user-top-read,user-read-"
|
||||
"recently-played"; // Required access scopes
|
||||
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
std::shared_ptr<bell::WrappedSemaphore> updateSemaphore;
|
||||
|
||||
bool isExpired();
|
||||
std::atomic<bool> keyPending = false;
|
||||
std::string accessKey;
|
||||
long long int expiresAt;
|
||||
};
|
||||
|
||||
@@ -8,17 +8,16 @@
|
||||
|
||||
namespace cspot {
|
||||
class ApResolve {
|
||||
private:
|
||||
std::string apOverride;
|
||||
|
||||
public:
|
||||
ApResolve(std::string apOverride);
|
||||
|
||||
/**
|
||||
* @brief Connects to spotify's servers and returns first valid ap address
|
||||
*
|
||||
* @return std::string Address in form of url:port
|
||||
*/
|
||||
* @brief Connects to spotify's servers and returns first valid ap address
|
||||
* @returns std::string Address in form of url:port
|
||||
*/
|
||||
std::string fetchFirstApAddress();
|
||||
|
||||
private:
|
||||
std::string apOverride;
|
||||
};
|
||||
} // namespace cspot
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Crypto.h" // for Crypto
|
||||
#include "protobuf/authentication.pb.h" // for ClientResponseEncrypted
|
||||
@@ -16,20 +15,45 @@ class AuthChallenges {
|
||||
AuthChallenges();
|
||||
~AuthChallenges();
|
||||
|
||||
std::vector<uint8_t> shanSendKey = {};
|
||||
std::vector<uint8_t> shanRecvKey = {};
|
||||
|
||||
/**
|
||||
* @brief Prepares a spotify authentication packet
|
||||
* @param authBlob authentication blob bytes
|
||||
* @param authType value representing spotify's authentication type
|
||||
* @param deviceId device id to use during auth.
|
||||
* @param username spotify's username
|
||||
*
|
||||
* @returns vector containing bytes of the authentication packet
|
||||
*/
|
||||
std::vector<uint8_t> prepareAuthPacket(std::vector<uint8_t>& authBlob,
|
||||
int authType,
|
||||
const std::string& deviceId,
|
||||
const std::string& username);
|
||||
|
||||
/**
|
||||
* @brief Solves the ApHello packet, and returns a packet with response
|
||||
*
|
||||
* @param helloPacket hello packet bytes received from the server
|
||||
* @param data authentication data received from the server
|
||||
*
|
||||
* @returns vector containing response packet
|
||||
*/
|
||||
std::vector<uint8_t> solveApHello(std::vector<uint8_t>& helloPacket,
|
||||
std::vector<uint8_t>& data);
|
||||
|
||||
/**
|
||||
* @brief Prepares an client hello packet, used for initial auth with spotify
|
||||
*
|
||||
* @returns vector containing the packet's data
|
||||
*/
|
||||
std::vector<uint8_t> prepareClientHello();
|
||||
|
||||
std::vector<uint8_t> shanSendKey = {};
|
||||
std::vector<uint8_t> shanRecvKey = {};
|
||||
|
||||
private:
|
||||
const long long SPOTIFY_VERSION = 0x10800000000;
|
||||
|
||||
// Protobuf structures
|
||||
ClientResponseEncrypted authRequest;
|
||||
ClientResponsePlaintext clientResPlaintext;
|
||||
ClientHello clientHello;
|
||||
@@ -37,4 +61,4 @@ class AuthChallenges {
|
||||
|
||||
std::unique_ptr<Crypto> crypto;
|
||||
};
|
||||
} // namespace cspot
|
||||
} // namespace cspot
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "LoginBlob.h"
|
||||
#include "MercurySession.h"
|
||||
#include "TimeProvider.h"
|
||||
#include "protobuf/metadata.pb.h"
|
||||
#include "LoginBlob.h"
|
||||
|
||||
namespace cspot {
|
||||
struct Context {
|
||||
@@ -25,7 +25,8 @@ struct Context {
|
||||
std::shared_ptr<TimeProvider> timeProvider;
|
||||
std::shared_ptr<cspot::MercurySession> session;
|
||||
|
||||
static std::shared_ptr<Context> createFromBlob(std::shared_ptr<LoginBlob> blob) {
|
||||
static std::shared_ptr<Context> createFromBlob(
|
||||
std::shared_ptr<LoginBlob> blob) {
|
||||
auto ctx = std::make_shared<Context>();
|
||||
ctx->timeProvider = std::make_shared<TimeProvider>();
|
||||
|
||||
@@ -37,6 +38,5 @@ struct Context {
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace cspot
|
||||
} // namespace cspot
|
||||
|
||||
@@ -7,11 +7,11 @@ extern char deviceId[];
|
||||
|
||||
namespace cspot {
|
||||
// Hardcoded information sent to spotify servers
|
||||
const char * const informationString = "cspot-player";
|
||||
const char * const brandName = "cspot";
|
||||
const char * const versionString = "cspot-1.1";
|
||||
const char * const protocolVersion = "2.7.1";
|
||||
const char * const defaultDeviceName = "CSpot";
|
||||
const char * const swVersion = "1.0.0";
|
||||
const char* const informationString = "cspot-player";
|
||||
const char* const brandName = "cspot";
|
||||
const char* const versionString = "cspot-1.1";
|
||||
const char* const protocolVersion = "2.7.1";
|
||||
const char* const defaultDeviceName = "CSpot";
|
||||
const char* const swVersion = "1.0.0";
|
||||
|
||||
}
|
||||
} // namespace cspot
|
||||
@@ -3,14 +3,13 @@
|
||||
#include <stdio.h>
|
||||
#include <cassert>
|
||||
|
||||
#define CSPOT_ASSERT(CONDITION, MESSAGE) \
|
||||
do \
|
||||
{ \
|
||||
if (!(CONDITION)) \
|
||||
{ \
|
||||
printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, __LINE__, #CONDITION, MESSAGE); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define CSPOT_ASSERT(CONDITION, MESSAGE) \
|
||||
do { \
|
||||
if (!(CONDITION)) { \
|
||||
printf("At %s in %s:%d\n Assertion %s failed: %s", __func__, __FILE__, \
|
||||
__LINE__, #CONDITION, MESSAGE); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include <BellLogger.h>
|
||||
|
||||
#define CSPOT_LOG(type, ...) \
|
||||
do \
|
||||
{ \
|
||||
bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define CSPOT_LOG(type, ...) \
|
||||
do { \
|
||||
bell::bellGlobalLogger->type(__FILE__, __LINE__, "cspot", __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // for uint8_t, uint32_t
|
||||
#include <map> // for map
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t, uint32_t
|
||||
#include <map> // for map
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Crypto.h" // for CryptoMbedTLS, Crypto
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic> // for atomic
|
||||
#include <cstdint> // for uint8_t, uint64_t, uint32_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <string> // for string
|
||||
#include <unordered_map> // for unordered_map
|
||||
#include <vector> // for vector
|
||||
#include <atomic> // for atomic
|
||||
#include <cstdint> // for uint8_t, uint64_t, uint32_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <string> // for string
|
||||
#include <unordered_map> // for unordered_map
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "BellTask.h" // for Task
|
||||
#include "Packet.h" // for Packet
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "Session.h" // for Session
|
||||
#include "protobuf/mercury.pb.h" // for Header
|
||||
|
||||
namespace cspot {
|
||||
namespace cspot {
|
||||
class TimeProvider;
|
||||
|
||||
class MercurySession : public bell::Task, public cspot::Session {
|
||||
@@ -33,7 +33,8 @@ class MercurySession : public bell::Task, public cspot::Session {
|
||||
};
|
||||
|
||||
typedef std::function<void(Response&)> ResponseCallback;
|
||||
typedef std::function<void(bool, const std::vector<uint8_t>&)> AudioKeyCallback;
|
||||
typedef std::function<void(bool, const std::vector<uint8_t>&)>
|
||||
AudioKeyCallback;
|
||||
typedef std::function<void()> ConnectionEstabilishedCallback;
|
||||
|
||||
enum class RequestType : uint8_t {
|
||||
@@ -82,7 +83,11 @@ class MercurySession : public bell::Task, public cspot::Session {
|
||||
return this->executeSubscription(type, uri, callback, nullptr, parts);
|
||||
}
|
||||
|
||||
void requestAudioKey(const std::vector<uint8_t>& trackId,
|
||||
void unregister(uint64_t sequenceId);
|
||||
|
||||
void unregisterAudioKey(uint32_t sequenceId);
|
||||
|
||||
uint32_t requestAudioKey(const std::vector<uint8_t>& trackId,
|
||||
const std::vector<uint8_t>& fileId,
|
||||
AudioKeyCallback audioCallback);
|
||||
|
||||
@@ -108,7 +113,7 @@ class MercurySession : public bell::Task, public cspot::Session {
|
||||
|
||||
std::unordered_map<uint64_t, ResponseCallback> callbacks;
|
||||
std::unordered_map<std::string, ResponseCallback> subscriptions;
|
||||
AudioKeyCallback audioKeyCallback;
|
||||
std::unordered_map<uint32_t, AudioKeyCallback> audioKeyCallbacks;
|
||||
|
||||
uint64_t sequenceId = 1;
|
||||
uint32_t audioKeySequence = 1;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "win32shim.h"
|
||||
#else
|
||||
#include <unistd.h> // for size_t
|
||||
#include <unistd.h> // for size_t
|
||||
#endif
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <functional> // for function
|
||||
@@ -37,8 +37,8 @@ class PlainConnection {
|
||||
void readBlock(const uint8_t* dst, size_t size);
|
||||
size_t writeBlock(const std::vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
int apSock;
|
||||
private:
|
||||
int apSock;
|
||||
};
|
||||
} // namespace cspot
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // for uint8_t, uint32_t
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
#include <stdint.h> // for uint8_t, uint32_t
|
||||
#include <memory> // for shared_ptr
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "TrackReference.h"
|
||||
#include "protobuf/spirc.pb.h" // for Frame, TrackRef, CapabilityType, Mess...
|
||||
|
||||
namespace cspot {
|
||||
@@ -13,8 +14,10 @@ struct Context;
|
||||
class PlaybackState {
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
|
||||
uint32_t seqNum = 0;
|
||||
uint8_t capabilityIndex = 0;
|
||||
|
||||
std::vector<uint8_t> frameData;
|
||||
|
||||
void addCapability(
|
||||
@@ -24,6 +27,9 @@ class PlaybackState {
|
||||
public:
|
||||
Frame innerFrame;
|
||||
Frame remoteFrame;
|
||||
|
||||
std::vector<TrackReference> remoteTracks;
|
||||
|
||||
enum class State { Playing, Stopped, Loading, Paused };
|
||||
|
||||
/**
|
||||
@@ -74,56 +80,9 @@ class PlaybackState {
|
||||
void setVolume(uint32_t volume);
|
||||
|
||||
/**
|
||||
* @brief Enables queue shuffling.
|
||||
*
|
||||
* Sets shuffle parameter on local frame, and in case shuffling is enabled,
|
||||
* it will randomize the entire local queue.
|
||||
*
|
||||
* @param shuffle whenever should shuffle
|
||||
* @brief Updates local track queue from remote data.
|
||||
*/
|
||||
void setShuffle(bool shuffle);
|
||||
|
||||
/**
|
||||
* @brief Enables repeat
|
||||
*
|
||||
* @param repeat should repeat param
|
||||
*/
|
||||
void setRepeat(bool repeat);
|
||||
|
||||
/**
|
||||
* @brief Updates local track queue from remote data.
|
||||
*/
|
||||
void updateTracks();
|
||||
|
||||
/**
|
||||
* @brief Changes playback to next queued track.
|
||||
*
|
||||
* Will go back to first track if current track is last track in queue.
|
||||
* In that case, it will pause if repeat is disabled.
|
||||
*/
|
||||
bool nextTrack();
|
||||
|
||||
/**
|
||||
* @brief Changes playback to previous queued track.
|
||||
*
|
||||
* Will stop if current track is the first track in queue and repeat is disabled.
|
||||
* If repeat is enabled, it will loop back to the last track in queue.
|
||||
*/
|
||||
void prevTrack();
|
||||
|
||||
/**
|
||||
* @brief Gets the current track reference.
|
||||
*
|
||||
* @return std::shared_ptr<TrackReference> pointer to track reference
|
||||
*/
|
||||
TrackRef* getCurrentTrackRef();
|
||||
|
||||
/**
|
||||
* @brief Gets reference to next track in queue, or nullptr if there is no next track.
|
||||
*
|
||||
* @return std::shared_ptr<TrackReference> pointer to track reference
|
||||
*/
|
||||
TrackRef* getNextTrackRef();
|
||||
void syncWithRemote();
|
||||
|
||||
/**
|
||||
* @brief Encodes current frame into binary data via protobuf.
|
||||
@@ -132,5 +91,7 @@ class PlaybackState {
|
||||
* @return std::vector<uint8_t> binary frame data
|
||||
*/
|
||||
std::vector<uint8_t> encodeCurrentFrame(MessageType typ);
|
||||
|
||||
bool decodeRemoteFrame(std::vector<uint8_t>& data);
|
||||
};
|
||||
} // namespace cspot
|
||||
} // namespace cspot
|
||||
|
||||
@@ -4,41 +4,40 @@
|
||||
#include <cstdint> // for uint32_t, uint8_t
|
||||
#include <vector> // for vector
|
||||
|
||||
class Shannon
|
||||
{
|
||||
public:
|
||||
static constexpr unsigned int N = 16;
|
||||
class Shannon {
|
||||
public:
|
||||
static constexpr unsigned int N = 16;
|
||||
|
||||
void key(const std::vector<uint8_t> &key); /* set key */
|
||||
void nonce(const std::vector<uint8_t> &nonce); /* set Init Vector */
|
||||
void stream(std::vector<uint8_t> &buf); /* stream cipher */
|
||||
void maconly(std::vector<uint8_t> &buf); /* accumulate MAC */
|
||||
void encrypt(std::vector<uint8_t> &buf); /* encrypt + MAC */
|
||||
void decrypt(std::vector<uint8_t> &buf); /* finalize + MAC */
|
||||
void finish(std::vector<uint8_t> &buf); /* finalise MAC */
|
||||
void key(const std::vector<uint8_t>& key); /* set key */
|
||||
void nonce(const std::vector<uint8_t>& nonce); /* set Init Vector */
|
||||
void stream(std::vector<uint8_t>& buf); /* stream cipher */
|
||||
void maconly(std::vector<uint8_t>& buf); /* accumulate MAC */
|
||||
void encrypt(std::vector<uint8_t>& buf); /* encrypt + MAC */
|
||||
void decrypt(std::vector<uint8_t>& buf); /* finalize + MAC */
|
||||
void finish(std::vector<uint8_t>& buf); /* finalise MAC */
|
||||
|
||||
private:
|
||||
static constexpr unsigned int FOLD = Shannon::N;
|
||||
static constexpr unsigned int INITKONST = 0x6996c53a;
|
||||
static constexpr unsigned int KEYP = 13;
|
||||
uint32_t R[Shannon::N];
|
||||
uint32_t CRC[Shannon::N];
|
||||
uint32_t initR[Shannon::N];
|
||||
uint32_t konst;
|
||||
uint32_t sbuf;
|
||||
uint32_t mbuf;
|
||||
int nbuf;
|
||||
static uint32_t sbox1(uint32_t w);
|
||||
static uint32_t sbox2(uint32_t w);
|
||||
void cycle();
|
||||
void crcfunc(uint32_t i);
|
||||
void macfunc(uint32_t i);
|
||||
void initState();
|
||||
void saveState();
|
||||
void reloadState();
|
||||
void genkonst();
|
||||
void diffuse();
|
||||
void loadKey(const std::vector<uint8_t> &key);
|
||||
private:
|
||||
static constexpr unsigned int FOLD = Shannon::N;
|
||||
static constexpr unsigned int INITKONST = 0x6996c53a;
|
||||
static constexpr unsigned int KEYP = 13;
|
||||
uint32_t R[Shannon::N];
|
||||
uint32_t CRC[Shannon::N];
|
||||
uint32_t initR[Shannon::N];
|
||||
uint32_t konst;
|
||||
uint32_t sbuf;
|
||||
uint32_t mbuf;
|
||||
int nbuf;
|
||||
static uint32_t sbox1(uint32_t w);
|
||||
static uint32_t sbox2(uint32_t w);
|
||||
void cycle();
|
||||
void crcfunc(uint32_t i);
|
||||
void macfunc(uint32_t i);
|
||||
void initState();
|
||||
void saveState();
|
||||
void reloadState();
|
||||
void genkonst();
|
||||
void diffuse();
|
||||
void loadKey(const std::vector<uint8_t>& key);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef SHANNONCONNECTION_H
|
||||
#define SHANNONCONNECTION_H
|
||||
|
||||
#include <cstdint> // for uint8_t, uint32_t
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t, uint32_t
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "Packet.h" // for Packet
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // for uint32_t, uint8_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <string> // for string
|
||||
#include <variant> // for variant
|
||||
#include <vector> // for vector
|
||||
#include <stdint.h> // for uint32_t, uint8_t
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <string> // for string
|
||||
#include <variant> // for variant
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::Track...
|
||||
#include "PlaybackState.h" // for PlaybackState
|
||||
#include "CDNAudioFile.h" // for CDNTrackStream, CDNTrackStream::Track...
|
||||
#include "TrackQueue.h"
|
||||
#include "protobuf/spirc.pb.h" // for MessageType
|
||||
|
||||
namespace cspot {
|
||||
@@ -31,7 +31,8 @@ class SpircHandler {
|
||||
FLUSH,
|
||||
PLAYBACK_START
|
||||
};
|
||||
typedef std::variant<CDNTrackStream::TrackInfo, int, bool> EventData;
|
||||
|
||||
typedef std::variant<TrackInfo, int, bool> EventData;
|
||||
|
||||
struct Event {
|
||||
EventType eventType;
|
||||
@@ -47,36 +48,34 @@ class SpircHandler {
|
||||
|
||||
void setPause(bool pause);
|
||||
|
||||
void nextSong();
|
||||
void previousSong();
|
||||
|
||||
void nextSong();
|
||||
|
||||
void notifyAudioReachedPlayback();
|
||||
void updatePositionMs(uint32_t position);
|
||||
void setRemoteVolume(int volume);
|
||||
void loadTrackFromURI(const std::string& uri);
|
||||
std::shared_ptr<cspot::TrackQueue> getTrackQueue() { return trackQueue; }
|
||||
|
||||
void disconnect();
|
||||
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
std::shared_ptr<cspot::TrackPlayer> trackPlayer;
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue;
|
||||
|
||||
EventHandler eventHandler = nullptr;
|
||||
|
||||
cspot::PlaybackState playbackState;
|
||||
CDNTrackStream::TrackInfo currentTrackInfo;
|
||||
|
||||
bool isTrackFresh = true;
|
||||
bool isRequestedFromLoad = false;
|
||||
bool isNextTrackPreloaded = false;
|
||||
uint32_t nextTrackPosition = 0;
|
||||
std::shared_ptr<cspot::PlaybackState> playbackState;
|
||||
|
||||
void sendCmd(MessageType typ);
|
||||
|
||||
void sendEvent(EventType type);
|
||||
void sendEvent(EventType type, EventData data);
|
||||
|
||||
void skipSong(TrackQueue::SkipDirection dir);
|
||||
void handleFrame(std::vector<uint8_t>& data);
|
||||
void notify();
|
||||
};
|
||||
} // namespace cspot
|
||||
} // namespace cspot
|
||||
|
||||
@@ -1,47 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic> // for atomic
|
||||
#include <cstdint> // for uint8_t, int64_t
|
||||
#include <ctime> // for size_t, time
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <string_view> // for string_view
|
||||
#include <vector> // for vector
|
||||
#include <atomic> // for atomic
|
||||
#include <cstdint> // for uint8_t, int64_t
|
||||
#include <ctime> // for size_t, time
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, unique_ptr
|
||||
#include <mutex> // for mutex
|
||||
#include <string_view> // for string_view
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "BellTask.h" // for Task
|
||||
#include "CDNTrackStream.h" // for CDNTrackStream, CDNTrackStream::TrackInfo
|
||||
#include "BellTask.h" // for Task
|
||||
#include "CDNAudioFile.h"
|
||||
#include "TrackQueue.h"
|
||||
|
||||
namespace bell {
|
||||
class WrappedSemaphore;
|
||||
} // namespace bell
|
||||
|
||||
#ifdef BELL_VORBIS_FLOAT
|
||||
#include "vorbis/vorbisfile.h"
|
||||
#else
|
||||
#include "ivorbisfile.h" // for OggVorbis_File, ov_callbacks
|
||||
#include "ivorbisfile.h" // for OggVorbis_File, ov_callbacks
|
||||
#endif
|
||||
|
||||
namespace cspot {
|
||||
class TrackProvider;
|
||||
class TrackQueue;
|
||||
struct Context;
|
||||
struct TrackReference;
|
||||
|
||||
class TrackPlayer : bell::Task {
|
||||
public:
|
||||
typedef std::function<void()> TrackLoadedCallback;
|
||||
typedef std::function<size_t(uint8_t*, size_t, std::string_view, size_t)> DataCallback;
|
||||
// Callback types
|
||||
typedef std::function<void(std::shared_ptr<QueuedTrack>)> TrackLoadedCallback;
|
||||
typedef std::function<size_t(uint8_t*, size_t, std::string_view)> DataCallback;
|
||||
typedef std::function<void()> EOFCallback;
|
||||
typedef std::function<bool()> isAiringCallback;
|
||||
|
||||
TrackPlayer(std::shared_ptr<cspot::Context> ctx, isAiringCallback, EOFCallback, TrackLoadedCallback);
|
||||
TrackPlayer(std::shared_ptr<cspot::Context> ctx,
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue,
|
||||
EOFCallback eofCallback, TrackLoadedCallback loadedCallback);
|
||||
~TrackPlayer();
|
||||
|
||||
void loadTrackFromRef(TrackReference& ref, size_t playbackMs, bool startAutomatically);
|
||||
|
||||
void loadTrackFromRef(TrackReference& ref, size_t playbackMs,
|
||||
bool startAutomatically);
|
||||
void setDataCallback(DataCallback callback);
|
||||
|
||||
CDNTrackStream::TrackInfo getCurrentTrackInfo();
|
||||
|
||||
// CDNTrackStream::TrackInfo getCurrentTrackInfo();
|
||||
void seekMs(size_t ms);
|
||||
void stopTrack();
|
||||
void resetState();
|
||||
|
||||
// Vorbis codec callbacks
|
||||
size_t _vorbisRead(void* ptr, size_t size, size_t nmemb);
|
||||
@@ -49,35 +56,39 @@ class TrackPlayer : bell::Task {
|
||||
int _vorbisSeek(int64_t offset, int whence);
|
||||
long _vorbisTell();
|
||||
|
||||
void destroy();
|
||||
void stop();
|
||||
void start();
|
||||
|
||||
private:
|
||||
std::shared_ptr<cspot::Context> ctx;
|
||||
std::shared_ptr<cspot::TrackProvider> trackProvider;
|
||||
std::shared_ptr<cspot::CDNTrackStream> currentTrackStream;
|
||||
size_t sequence = std::time(nullptr);
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue;
|
||||
std::shared_ptr<cspot::CDNAudioFile> currentTrackStream;
|
||||
|
||||
std::unique_ptr<bell::WrappedSemaphore> playbackSemaphore;
|
||||
|
||||
TrackLoadedCallback trackLoaded;
|
||||
DataCallback dataCallback = nullptr;
|
||||
EOFCallback eofCallback;
|
||||
isAiringCallback isAiring;
|
||||
|
||||
// Playback control
|
||||
std::atomic<bool> currentSongPlaying;
|
||||
std::mutex playbackMutex;
|
||||
std::mutex seekMutex;
|
||||
|
||||
std::mutex dataOutMutex;
|
||||
|
||||
// Vorbis related
|
||||
OggVorbis_File vorbisFile;
|
||||
ov_callbacks vorbisCallbacks;
|
||||
int currentSection;
|
||||
|
||||
std::vector<uint8_t> pcmBuffer = std::vector<uint8_t>(1024);
|
||||
|
||||
size_t playbackPosition = 0;
|
||||
bool autoStart = false;
|
||||
std::atomic<bool> isRunning = true;
|
||||
|
||||
std::atomic<bool> isRunning = false;
|
||||
std::atomic<bool> pendingReset = false;
|
||||
std::atomic<bool> inFuture = false;
|
||||
std::atomic<size_t> pendingSeekPositionMs = 0;
|
||||
|
||||
std::mutex runningMutex;
|
||||
|
||||
void runTask() override;
|
||||
|
||||
@@ -1,51 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <pb_encode.h>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include "NanoPBHelper.h"
|
||||
#include "Utils.h"
|
||||
#include "pb_decode.h"
|
||||
#include "protobuf/spirc.pb.h"
|
||||
namespace cspot {
|
||||
|
||||
namespace cspot {
|
||||
struct TrackReference {
|
||||
static constexpr auto base62Alphabet =
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
TrackReference();
|
||||
|
||||
// Resolved track GID
|
||||
std::vector<uint8_t> gid;
|
||||
std::string uri, context;
|
||||
std::optional<bool> queued;
|
||||
|
||||
// Type identifier
|
||||
enum class Type { TRACK, EPISODE };
|
||||
|
||||
Type type;
|
||||
|
||||
static TrackReference fromTrackRef(TrackRef* ref) {
|
||||
TrackReference trackRef;
|
||||
if (ref->gid != nullptr) {
|
||||
// For tracks, the GID is already in the protobuf
|
||||
trackRef.gid = pbArrayToVector(ref->gid);
|
||||
trackRef.type = Type::TRACK;
|
||||
} else {
|
||||
// Episode GID is being fetched via base62 encoded URI
|
||||
auto uri = std::string(ref->uri);
|
||||
auto idString = uri.substr(uri.find_last_of(":") + 1, uri.size());
|
||||
trackRef.gid = {0};
|
||||
void decodeURI();
|
||||
|
||||
std::string_view alphabet(base62Alphabet);
|
||||
for (int x = 0; x < idString.size(); x++) {
|
||||
size_t d = alphabet.find(idString[x]);
|
||||
trackRef.gid = bigNumMultiply(trackRef.gid, 62);
|
||||
trackRef.gid = bigNumAdd(trackRef.gid, d);
|
||||
}
|
||||
}
|
||||
bool operator==(const TrackReference& other) const;
|
||||
|
||||
return trackRef;
|
||||
}
|
||||
// Encodes list of track references into a pb structure, used by nanopb
|
||||
static bool pbEncodeTrackList(pb_ostream_t* stream, const pb_field_t* field,
|
||||
void* const* arg);
|
||||
|
||||
static TrackReference fromGID(std::vector<uint8_t> gid, bool isEpisode) {
|
||||
TrackReference trackRef;
|
||||
trackRef.gid = gid;
|
||||
trackRef.type = isEpisode ? Type::EPISODE : Type::TRACK;
|
||||
return trackRef;
|
||||
}
|
||||
static bool pbDecodeTrackList(pb_istream_t* stream, const pb_field_t* field,
|
||||
void** arg);
|
||||
};
|
||||
} // namespace cspot
|
||||
} // namespace cspot
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include <cstdio> // for snprintf, size_t
|
||||
#include <vector> // for vector
|
||||
#include <cstdio> // for snprintf, size_t
|
||||
#include <vector> // for vector
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
@@ -55,7 +55,6 @@ std::vector<uint8_t> bigNumAdd(std::vector<uint8_t> num, int n);
|
||||
|
||||
unsigned char h2int(char c);
|
||||
|
||||
|
||||
std::string urlDecode(std::string str);
|
||||
|
||||
/**
|
||||
@@ -64,7 +63,7 @@ std::string urlDecode(std::string str);
|
||||
* @param s string containing hex data
|
||||
* @return std::vector<uint8_t> vector containing binary data
|
||||
*/
|
||||
std::vector<uint8_t> stringHexToBytes(const std::string &s);
|
||||
std::vector<uint8_t> stringHexToBytes(const std::string& s);
|
||||
|
||||
/**
|
||||
* @brief Converts provided bytes into a human readable hex string
|
||||
@@ -72,7 +71,7 @@ std::vector<uint8_t> stringHexToBytes(const std::string &s);
|
||||
* @param bytes vector containing binary data
|
||||
* @return std::string string containing hex representation of inputted data
|
||||
*/
|
||||
std::string bytesToHexString(const std::vector<uint8_t> &bytes);
|
||||
std::string bytesToHexString(const std::vector<uint8_t>& bytes);
|
||||
|
||||
/**
|
||||
* @brief Extracts given type from binary data
|
||||
@@ -83,8 +82,7 @@ std::string bytesToHexString(const std::vector<uint8_t> &bytes);
|
||||
* @return T extracted type
|
||||
*/
|
||||
template <typename T>
|
||||
T extract(const std::vector<unsigned char> &v, int pos)
|
||||
{
|
||||
T extract(const std::vector<unsigned char>& v, int pos) {
|
||||
T value;
|
||||
memcpy(&value, &v[pos], sizeof(T));
|
||||
return value;
|
||||
@@ -98,22 +96,25 @@ T extract(const std::vector<unsigned char> &v, int pos)
|
||||
* @return std::vector<uint8_t> resulting vector containing binary data
|
||||
*/
|
||||
template <typename T>
|
||||
std::vector<uint8_t> pack(T data)
|
||||
{
|
||||
std::vector<std::uint8_t> rawData( (std::uint8_t*)&data, (std::uint8_t*)&(data) + sizeof(T));
|
||||
std::vector<uint8_t> pack(T data) {
|
||||
std::vector<std::uint8_t> rawData((std::uint8_t*)&data,
|
||||
(std::uint8_t*)&(data) + sizeof(T));
|
||||
|
||||
return rawData;
|
||||
return rawData;
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::string string_format( const std::string& format, Args ... args )
|
||||
{
|
||||
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
|
||||
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
|
||||
auto size = static_cast<size_t>( size_s );
|
||||
std::unique_ptr<char[]> buf( new char[ size ] );
|
||||
std::snprintf( buf.get(), size, format.c_str(), args ... );
|
||||
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
|
||||
template <typename... Args>
|
||||
std::string string_format(const std::string& format, Args... args) {
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) +
|
||||
1; // Extra space for '\0'
|
||||
if (size_s <= 0) {
|
||||
throw std::runtime_error("Error during formatting.");
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(),
|
||||
buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user