PlaybackState.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #include "PlaybackState.h"
  2. #include <memory>
  3. #include "CSpotContext.h"
  4. #include "Logger.h"
  5. using namespace cspot;
  6. PlaybackState::PlaybackState(std::shared_ptr<cspot::Context> ctx) {
  7. this->ctx = ctx;
  8. innerFrame = {};
  9. remoteFrame = {};
  10. // Prepare default state
  11. innerFrame.state.has_position_ms = true;
  12. innerFrame.state.position_ms = 0;
  13. innerFrame.state.status = PlayStatus_kPlayStatusStop;
  14. innerFrame.state.has_status = true;
  15. innerFrame.state.position_measured_at = 0;
  16. innerFrame.state.has_position_measured_at = true;
  17. innerFrame.state.shuffle = false;
  18. innerFrame.state.has_shuffle = true;
  19. innerFrame.state.repeat = false;
  20. innerFrame.state.has_repeat = true;
  21. innerFrame.device_state.sw_version = strdup(swVersion);
  22. innerFrame.device_state.is_active = false;
  23. innerFrame.device_state.has_is_active = true;
  24. innerFrame.device_state.can_play = true;
  25. innerFrame.device_state.has_can_play = true;
  26. innerFrame.device_state.volume = ctx->config.volume;
  27. innerFrame.device_state.has_volume = true;
  28. innerFrame.device_state.name = strdup(ctx->config.deviceName.c_str());
  29. innerFrame.state.track_count = 0;
  30. // Prepare player's capabilities
  31. addCapability(CapabilityType_kCanBePlayer, 1);
  32. addCapability(CapabilityType_kDeviceType, 4);
  33. addCapability(CapabilityType_kGaiaEqConnectId, 1);
  34. addCapability(CapabilityType_kSupportsLogout, 0);
  35. addCapability(CapabilityType_kIsObservable, 1);
  36. addCapability(CapabilityType_kVolumeSteps, 64);
  37. addCapability(CapabilityType_kSupportedContexts, -1,
  38. std::vector<std::string>({"album", "playlist", "search",
  39. "inbox", "toplist", "starred",
  40. "publishedstarred", "track"}));
  41. addCapability(CapabilityType_kSupportedTypes, -1,
  42. std::vector<std::string>({"audio/local", "audio/track",
  43. "audio/episode", "local", "track"}));
  44. innerFrame.device_state.capabilities_count = 8;
  45. }
  46. PlaybackState::~PlaybackState() {
  47. pb_release(Frame_fields, &innerFrame);
  48. pb_release(Frame_fields, &remoteFrame);
  49. }
  50. void PlaybackState::setPlaybackState(const PlaybackState::State state) {
  51. switch (state) {
  52. case State::Loading:
  53. // Prepare the playback at position 0
  54. innerFrame.state.status = PlayStatus_kPlayStatusPause;
  55. innerFrame.state.position_ms = 0;
  56. innerFrame.state.position_measured_at =
  57. ctx->timeProvider->getSyncedTimestamp();
  58. break;
  59. case State::Playing:
  60. innerFrame.state.status = PlayStatus_kPlayStatusPlay;
  61. innerFrame.state.position_measured_at =
  62. ctx->timeProvider->getSyncedTimestamp();
  63. break;
  64. case State::Stopped:
  65. break;
  66. case State::Paused:
  67. // Update state and recalculate current song position
  68. innerFrame.state.status = PlayStatus_kPlayStatusPause;
  69. uint32_t diff = ctx->timeProvider->getSyncedTimestamp() -
  70. innerFrame.state.position_measured_at;
  71. this->updatePositionMs(innerFrame.state.position_ms + diff);
  72. break;
  73. }
  74. }
  75. bool PlaybackState::isActive() {
  76. return innerFrame.device_state.is_active;
  77. }
  78. bool PlaybackState::nextTrack() {
  79. innerFrame.state.playing_track_index++;
  80. if (innerFrame.state.playing_track_index >= innerFrame.state.track_count) {
  81. innerFrame.state.playing_track_index = 0;
  82. if (!innerFrame.state.repeat) {
  83. setPlaybackState(State::Paused);
  84. return false;
  85. }
  86. }
  87. return true;
  88. }
  89. void PlaybackState::prevTrack() {
  90. if (innerFrame.state.playing_track_index > 0) {
  91. innerFrame.state.playing_track_index--;
  92. } else if (innerFrame.state.repeat) {
  93. innerFrame.state.playing_track_index = innerFrame.state.track_count - 1;
  94. }
  95. }
  96. void PlaybackState::setActive(bool isActive) {
  97. innerFrame.device_state.is_active = isActive;
  98. if (isActive) {
  99. innerFrame.device_state.became_active_at =
  100. ctx->timeProvider->getSyncedTimestamp();
  101. innerFrame.device_state.has_became_active_at = true;
  102. }
  103. }
  104. void PlaybackState::updatePositionMs(uint32_t position) {
  105. innerFrame.state.position_ms = position;
  106. innerFrame.state.position_measured_at =
  107. ctx->timeProvider->getSyncedTimestamp();
  108. }
  109. #define FREE(ptr) \
  110. { \
  111. free(ptr); \
  112. ptr = NULL; \
  113. }
  114. #define STRDUP(dst, src) \
  115. if (src != NULL) { \
  116. dst = strdup(src); \
  117. } else { \
  118. FREE(dst); \
  119. } // strdup null pointer safe
  120. void PlaybackState::updateTracks() {
  121. CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
  122. CSPOT_LOG(info, "---- Inner track count %d", innerFrame.state.track_count);
  123. CSPOT_LOG(info, "--- Context URI %s", remoteFrame.state.context_uri);
  124. // free unused tracks
  125. if (innerFrame.state.track_count > remoteFrame.state.track_count) {
  126. for (uint16_t i = remoteFrame.state.track_count;
  127. i < innerFrame.state.track_count; ++i) {
  128. FREE(innerFrame.state.track[i].gid);
  129. FREE(innerFrame.state.track[i].uri);
  130. FREE(innerFrame.state.track[i].context);
  131. }
  132. }
  133. // reallocate memory for new tracks
  134. innerFrame.state.track = (TrackRef*)realloc(
  135. innerFrame.state.track, sizeof(TrackRef) * remoteFrame.state.track_count);
  136. for (uint16_t i = 0; i < remoteFrame.state.track_count; ++i) {
  137. if (i >= innerFrame.state.track_count) {
  138. innerFrame.state.track[i].gid = NULL;
  139. innerFrame.state.track[i].uri = NULL;
  140. innerFrame.state.track[i].context = NULL;
  141. }
  142. if (remoteFrame.state.track[i].gid != NULL) {
  143. uint16_t gid_size = remoteFrame.state.track[i].gid->size;
  144. innerFrame.state.track[i].gid = (pb_bytes_array_t*)realloc(
  145. innerFrame.state.track[i].gid, PB_BYTES_ARRAY_T_ALLOCSIZE(gid_size));
  146. memcpy(innerFrame.state.track[i].gid->bytes,
  147. remoteFrame.state.track[i].gid->bytes, gid_size);
  148. innerFrame.state.track[i].gid->size = gid_size;
  149. }
  150. innerFrame.state.track[i].has_queued =
  151. remoteFrame.state.track[i].has_queued;
  152. innerFrame.state.track[i].queued = remoteFrame.state.track[i].queued;
  153. STRDUP(innerFrame.state.track[i].uri, remoteFrame.state.track[i].uri);
  154. STRDUP(innerFrame.state.track[i].context,
  155. remoteFrame.state.track[i].context);
  156. }
  157. innerFrame.state.context_uri = (char*)realloc(
  158. innerFrame.state.context_uri, strlen(remoteFrame.state.context_uri) + 1);
  159. strcpy(innerFrame.state.context_uri, remoteFrame.state.context_uri);
  160. innerFrame.state.track_count = remoteFrame.state.track_count;
  161. innerFrame.state.has_playing_track_index = true;
  162. innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
  163. if (remoteFrame.state.repeat) {
  164. setRepeat(true);
  165. }
  166. if (remoteFrame.state.shuffle) {
  167. setShuffle(true);
  168. }
  169. }
  170. void PlaybackState::setVolume(uint32_t volume) {
  171. innerFrame.device_state.volume = volume;
  172. ctx->config.volume = volume;
  173. }
  174. void PlaybackState::setShuffle(bool shuffle) {
  175. innerFrame.state.shuffle = shuffle;
  176. if (shuffle) {
  177. // Put current song at the begining
  178. std::swap(innerFrame.state.track[0],
  179. innerFrame.state.track[innerFrame.state.playing_track_index]);
  180. // Shuffle current tracks
  181. for (int x = 1; x < innerFrame.state.track_count - 1; x++) {
  182. auto j = x + (std::rand() % (innerFrame.state.track_count - x));
  183. std::swap(innerFrame.state.track[j], innerFrame.state.track[x]);
  184. }
  185. innerFrame.state.playing_track_index = 0;
  186. }
  187. }
  188. void PlaybackState::setRepeat(bool repeat) {
  189. innerFrame.state.repeat = repeat;
  190. }
  191. TrackRef* PlaybackState::getCurrentTrackRef() {
  192. if (innerFrame.state.playing_track_index >= innerFrame.state.track_count) {
  193. return nullptr;
  194. }
  195. return &innerFrame.state.track[innerFrame.state.playing_track_index];
  196. }
  197. TrackRef* PlaybackState::getNextTrackRef() {
  198. if ((innerFrame.state.playing_track_index + 1) >= innerFrame.state.track_count) {
  199. if (innerFrame.state.repeat) {
  200. return &innerFrame.state.track[0];
  201. }
  202. return nullptr;
  203. }
  204. return &innerFrame.state.track[innerFrame.state.playing_track_index + 1];
  205. }
  206. std::vector<uint8_t> PlaybackState::encodeCurrentFrame(MessageType typ) {
  207. free(innerFrame.ident);
  208. free(innerFrame.protocol_version);
  209. // Prepare current frame info
  210. innerFrame.version = 1;
  211. innerFrame.ident = strdup(ctx->config.deviceId.c_str());
  212. innerFrame.seq_nr = this->seqNum;
  213. innerFrame.protocol_version = strdup(protocolVersion);
  214. innerFrame.typ = typ;
  215. innerFrame.state_update_id = ctx->timeProvider->getSyncedTimestamp();
  216. innerFrame.has_version = true;
  217. innerFrame.has_seq_nr = true;
  218. innerFrame.recipient_count = 0;
  219. innerFrame.has_state = true;
  220. innerFrame.has_device_state = true;
  221. innerFrame.has_typ = true;
  222. innerFrame.has_state_update_id = true;
  223. this->seqNum += 1;
  224. return pbEncode(Frame_fields, &innerFrame);
  225. }
  226. // Wraps messy nanopb setters. @TODO: find a better way to handle this
  227. void PlaybackState::addCapability(CapabilityType typ, int intValue,
  228. std::vector<std::string> stringValue) {
  229. innerFrame.device_state.capabilities[capabilityIndex].has_typ = true;
  230. this->innerFrame.device_state.capabilities[capabilityIndex].typ = typ;
  231. if (intValue != -1) {
  232. this->innerFrame.device_state.capabilities[capabilityIndex].intValue[0] =
  233. intValue;
  234. this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count =
  235. 1;
  236. } else {
  237. this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count =
  238. 0;
  239. }
  240. for (int x = 0; x < stringValue.size(); x++) {
  241. pbPutString(stringValue[x],
  242. this->innerFrame.device_state.capabilities[capabilityIndex]
  243. .stringValue[x]);
  244. }
  245. this->innerFrame.device_state.capabilities[capabilityIndex]
  246. .stringValue_count = stringValue.size();
  247. this->capabilityIndex += 1;
  248. }