2
0

SpircHandler.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #include "SpircHandler.h"
  2. #include <cstdint> // for uint8_t
  3. #include <memory> // for shared_ptr, make_unique, unique_ptr
  4. #include <type_traits> // for remove_extent_t
  5. #include <utility> // for move
  6. #include "BellLogger.h" // for AbstractLogger
  7. #include "CSpotContext.h" // for Context::ConfigState, Context (ptr only)
  8. #include "Logger.h" // for CSPOT_LOG
  9. #include "MercurySession.h" // for MercurySession, MercurySession::Response
  10. #include "NanoPBHelper.h" // for pbDecode
  11. #include "Packet.h" // for cspot
  12. #include "PlaybackState.h" // for PlaybackState, PlaybackState::State
  13. #include "TrackPlayer.h" // for TrackPlayer
  14. #include "TrackQueue.h"
  15. #include "TrackReference.h" // for TrackReference
  16. #include "Utils.h" // for stringHexToBytes
  17. #include "pb_decode.h" // for pb_release
  18. #include "protobuf/spirc.pb.h" // for Frame, State, Frame_fields, MessageTy...
  19. using namespace cspot;
  20. SpircHandler::SpircHandler(std::shared_ptr<cspot::Context> ctx) {
  21. this->playbackState = std::make_shared<PlaybackState>(ctx);
  22. this->trackQueue = std::make_shared<cspot::TrackQueue>(ctx, playbackState);
  23. auto EOFCallback = [this]() {
  24. if (trackQueue->isFinished()) {
  25. sendEvent(EventType::DEPLETED);
  26. }
  27. };
  28. auto trackLoadedCallback = [this](std::shared_ptr<QueuedTrack> track,
  29. bool paused = false) {
  30. playbackState->setPlaybackState(paused ? PlaybackState::State::Paused
  31. : PlaybackState::State::Playing);
  32. playbackState->updatePositionMs(track->requestedPosition);
  33. this->notify();
  34. // Send playback start event, pause/unpause per request
  35. sendEvent(EventType::PLAYBACK_START, (int)track->requestedPosition);
  36. sendEvent(EventType::PLAY_PAUSE, paused);
  37. };
  38. this->ctx = ctx;
  39. this->trackPlayer = std::make_shared<TrackPlayer>(
  40. ctx, trackQueue, EOFCallback, trackLoadedCallback);
  41. // Subscribe to mercury on session ready
  42. ctx->session->setConnectedHandler([this]() { this->subscribeToMercury(); });
  43. }
  44. void SpircHandler::subscribeToMercury() {
  45. auto responseLambda = [this](MercurySession::Response& res) {
  46. if (res.fail)
  47. return;
  48. sendCmd(MessageType_kMessageTypeHello);
  49. CSPOT_LOG(debug, "Sent kMessageTypeHello!");
  50. // Assign country code
  51. this->ctx->config.countryCode = this->ctx->session->getCountryCode();
  52. };
  53. auto subscriptionLambda = [this](MercurySession::Response& res) {
  54. if (res.fail)
  55. return;
  56. CSPOT_LOG(debug, "Received subscription response");
  57. this->handleFrame(res.parts[0]);
  58. };
  59. ctx->session->executeSubscription(
  60. MercurySession::RequestType::SUB,
  61. "hm://remote/user/" + ctx->config.username + "/", responseLambda,
  62. subscriptionLambda);
  63. }
  64. void SpircHandler::loadTrackFromURI(const std::string& uri) {}
  65. void SpircHandler::notifyAudioEnded() {
  66. playbackState->updatePositionMs(0);
  67. notify();
  68. trackPlayer->resetState(true);
  69. }
  70. void SpircHandler::notifyAudioReachedPlayback() {
  71. int offset = 0;
  72. // get HEAD track
  73. auto currentTrack = trackQueue->consumeTrack(nullptr, offset);
  74. // Do not execute when meta is already updated
  75. if (trackQueue->notifyPending) {
  76. trackQueue->notifyPending = false;
  77. playbackState->updatePositionMs(currentTrack->requestedPosition);
  78. // Reset position in queued track
  79. currentTrack->requestedPosition = 0;
  80. } else {
  81. trackQueue->skipTrack(TrackQueue::SkipDirection::NEXT, false);
  82. playbackState->updatePositionMs(0);
  83. // we moved to next track, re-acquire currentTrack again
  84. currentTrack = trackQueue->consumeTrack(nullptr, offset);
  85. }
  86. this->notify();
  87. sendEvent(EventType::TRACK_INFO, currentTrack->trackInfo);
  88. }
  89. void SpircHandler::updatePositionMs(uint32_t position) {
  90. playbackState->updatePositionMs(position);
  91. notify();
  92. }
  93. void SpircHandler::disconnect() {
  94. this->trackQueue->stopTask();
  95. this->trackPlayer->stop();
  96. this->ctx->session->disconnect();
  97. }
  98. void SpircHandler::handleFrame(std::vector<uint8_t>& data) {
  99. // Decode received spirc frame
  100. playbackState->decodeRemoteFrame(data);
  101. switch (playbackState->remoteFrame.typ) {
  102. case MessageType_kMessageTypeNotify: {
  103. CSPOT_LOG(debug, "Notify frame");
  104. // Pause the playback if another player took control
  105. if (playbackState->isActive() &&
  106. playbackState->remoteFrame.device_state.is_active) {
  107. CSPOT_LOG(debug, "Another player took control, pausing playback");
  108. playbackState->setActive(false);
  109. this->trackPlayer->stop();
  110. sendEvent(EventType::DISC);
  111. }
  112. break;
  113. }
  114. case MessageType_kMessageTypeSeek: {
  115. this->trackPlayer->seekMs(playbackState->remoteFrame.position);
  116. playbackState->updatePositionMs(playbackState->remoteFrame.position);
  117. notify();
  118. sendEvent(EventType::SEEK, (int)playbackState->remoteFrame.position);
  119. break;
  120. }
  121. case MessageType_kMessageTypeVolume:
  122. playbackState->setVolume(playbackState->remoteFrame.volume);
  123. this->notify();
  124. sendEvent(EventType::VOLUME, (int)playbackState->remoteFrame.volume);
  125. break;
  126. case MessageType_kMessageTypePause:
  127. setPause(true);
  128. break;
  129. case MessageType_kMessageTypePlay:
  130. setPause(false);
  131. break;
  132. case MessageType_kMessageTypeNext:
  133. if (nextSong()) {
  134. sendEvent(EventType::NEXT);
  135. }
  136. break;
  137. case MessageType_kMessageTypePrev:
  138. if (previousSong()) {
  139. sendEvent(EventType::PREV);
  140. }
  141. break;
  142. case MessageType_kMessageTypeLoad: {
  143. this->trackPlayer->start();
  144. CSPOT_LOG(debug, "Load frame %d!", playbackState->remoteTracks.size());
  145. if (playbackState->remoteTracks.size() == 0) {
  146. CSPOT_LOG(info, "No tracks in frame, stopping playback");
  147. break;
  148. }
  149. playbackState->setActive(true);
  150. playbackState->updatePositionMs(playbackState->remoteFrame.position);
  151. playbackState->setPlaybackState(PlaybackState::State::Playing);
  152. playbackState->syncWithRemote();
  153. // Update track list in case we have a new one
  154. trackQueue->updateTracks(playbackState->remoteFrame.state.position_ms,
  155. true);
  156. this->notify();
  157. // Stop the current track, if any
  158. trackPlayer->resetState();
  159. break;
  160. }
  161. case MessageType_kMessageTypeReplace: {
  162. CSPOT_LOG(debug, "Got replace frame %d", playbackState->remoteTracks.size());
  163. playbackState->syncWithRemote();
  164. // 1st track is the current one, but update the position
  165. bool cleared = trackQueue->updateTracks(
  166. playbackState->remoteFrame.state.position_ms +
  167. ctx->timeProvider->getSyncedTimestamp() -
  168. playbackState->innerFrame.state.position_measured_at);
  169. this->notify();
  170. // need to re-load all if streaming track is completed
  171. if (cleared) {
  172. sendEvent(EventType::FLUSH);
  173. trackPlayer->resetState();
  174. }
  175. break;
  176. }
  177. case MessageType_kMessageTypeShuffle: {
  178. CSPOT_LOG(debug, "Got shuffle frame");
  179. this->notify();
  180. break;
  181. }
  182. case MessageType_kMessageTypeRepeat: {
  183. CSPOT_LOG(debug, "Got repeat frame");
  184. this->notify();
  185. break;
  186. }
  187. default:
  188. break;
  189. }
  190. }
  191. void SpircHandler::setRemoteVolume(int volume) {
  192. playbackState->setVolume(volume);
  193. notify();
  194. }
  195. void SpircHandler::notify() {
  196. this->sendCmd(MessageType_kMessageTypeNotify);
  197. }
  198. bool SpircHandler::skipSong(TrackQueue::SkipDirection dir) {
  199. bool skipped = trackQueue->skipTrack(dir);
  200. // Reset track state
  201. trackPlayer->resetState(!skipped);
  202. // send NEXT or PREV event only when successful
  203. return skipped;
  204. }
  205. bool SpircHandler::nextSong() {
  206. return skipSong(TrackQueue::SkipDirection::NEXT);
  207. }
  208. bool SpircHandler::previousSong() {
  209. return skipSong(TrackQueue::SkipDirection::PREV);
  210. }
  211. std::shared_ptr<TrackPlayer> SpircHandler::getTrackPlayer() {
  212. return this->trackPlayer;
  213. }
  214. void SpircHandler::sendCmd(MessageType typ) {
  215. // Serialize current player state
  216. auto encodedFrame = playbackState->encodeCurrentFrame(typ);
  217. auto responseLambda = [=](MercurySession::Response& res) {
  218. };
  219. auto parts = MercurySession::DataParts({encodedFrame});
  220. ctx->session->execute(MercurySession::RequestType::SEND,
  221. "hm://remote/user/" + ctx->config.username + "/",
  222. responseLambda, parts);
  223. }
  224. void SpircHandler::setEventHandler(EventHandler handler) {
  225. this->eventHandler = handler;
  226. }
  227. void SpircHandler::setPause(bool isPaused) {
  228. if (isPaused) {
  229. CSPOT_LOG(debug, "External pause command");
  230. playbackState->setPlaybackState(PlaybackState::State::Paused);
  231. } else {
  232. CSPOT_LOG(debug, "External play command");
  233. playbackState->setPlaybackState(PlaybackState::State::Playing);
  234. }
  235. notify();
  236. sendEvent(EventType::PLAY_PAUSE, isPaused);
  237. }
  238. void SpircHandler::sendEvent(EventType type) {
  239. auto event = std::make_unique<Event>();
  240. event->eventType = type;
  241. event->data = {};
  242. eventHandler(std::move(event));
  243. }
  244. void SpircHandler::sendEvent(EventType type, EventData data) {
  245. auto event = std::make_unique<Event>();
  246. event->eventType = type;
  247. event->data = data;
  248. eventHandler(std::move(event));
  249. }