PlaybackState.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #include "PlaybackState.h"
  2. #include <string.h> // for strdup, memcpy, strcpy, strlen
  3. #include <cstdint> // for uint8_t
  4. #include <cstdlib> // for free, NULL, realloc, rand
  5. #include <cstring>
  6. #include <memory> // for shared_ptr
  7. #include <type_traits> // for remove_extent_t
  8. #include <utility> // for swap
  9. #include "BellLogger.h" // for AbstractLogger
  10. #include "CSpotContext.h" // for Context::ConfigState, Context (ptr o...
  11. #include "ConstantParameters.h" // for protocolVersion, swVersion
  12. #include "Logger.h" // for CSPOT_LOG
  13. #include "NanoPBHelper.h" // for pbEncode, pbPutString
  14. #include "Packet.h" // for cspot
  15. #include "pb.h" // for pb_bytes_array_t, PB_BYTES_ARRAY_T_A...
  16. #include "pb_decode.h" // for pb_release
  17. #include "protobuf/spirc.pb.h"
  18. using namespace cspot;
  19. PlaybackState::PlaybackState(std::shared_ptr<cspot::Context> ctx) {
  20. this->ctx = ctx;
  21. innerFrame = {};
  22. remoteFrame = {};
  23. // Prepare callbacks for decoding of remote frame track data
  24. remoteFrame.state.track.funcs.decode = &TrackReference::pbDecodeTrackList;
  25. remoteFrame.state.track.arg = &remoteTracks;
  26. innerFrame.ident = strdup(ctx->config.deviceId.c_str());
  27. innerFrame.protocol_version = strdup(protocolVersion);
  28. // Prepare default state
  29. innerFrame.state.has_position_ms = true;
  30. innerFrame.state.position_ms = 0;
  31. innerFrame.state.status = PlayStatus_kPlayStatusStop;
  32. innerFrame.state.has_status = true;
  33. innerFrame.state.position_measured_at = 0;
  34. innerFrame.state.has_position_measured_at = true;
  35. innerFrame.state.shuffle = false;
  36. innerFrame.state.has_shuffle = true;
  37. innerFrame.state.repeat = false;
  38. innerFrame.state.has_repeat = true;
  39. innerFrame.device_state.sw_version = strdup(swVersion);
  40. innerFrame.device_state.is_active = false;
  41. innerFrame.device_state.has_is_active = true;
  42. innerFrame.device_state.can_play = true;
  43. innerFrame.device_state.has_can_play = true;
  44. innerFrame.device_state.volume = ctx->config.volume;
  45. innerFrame.device_state.has_volume = true;
  46. innerFrame.device_state.name = strdup(ctx->config.deviceName.c_str());
  47. // Prepare player's capabilities
  48. addCapability(CapabilityType_kCanBePlayer, 1);
  49. addCapability(CapabilityType_kDeviceType, 4);
  50. addCapability(CapabilityType_kGaiaEqConnectId, 1);
  51. addCapability(CapabilityType_kSupportsLogout, 0);
  52. addCapability(CapabilityType_kSupportsPlaylistV2, 1);
  53. addCapability(CapabilityType_kIsObservable, 1);
  54. addCapability(CapabilityType_kVolumeSteps, 64);
  55. addCapability(CapabilityType_kSupportedContexts, -1,
  56. std::vector<std::string>({"album", "playlist", "search",
  57. "inbox", "toplist", "starred",
  58. "publishedstarred", "track"}));
  59. addCapability(CapabilityType_kSupportedTypes, -1,
  60. std::vector<std::string>(
  61. {"audio/track", "audio/episode", "audio/episode+track"}));
  62. innerFrame.device_state.capabilities_count = 8;
  63. }
  64. PlaybackState::~PlaybackState() {
  65. pb_release(Frame_fields, &innerFrame);
  66. pb_release(Frame_fields, &remoteFrame);
  67. }
  68. void PlaybackState::setPlaybackState(const PlaybackState::State state) {
  69. switch (state) {
  70. case State::Loading:
  71. // Prepare the playback at position 0
  72. innerFrame.state.status = PlayStatus_kPlayStatusPause;
  73. innerFrame.state.position_ms = 0;
  74. innerFrame.state.position_measured_at =
  75. ctx->timeProvider->getSyncedTimestamp();
  76. break;
  77. case State::Playing:
  78. innerFrame.state.status = PlayStatus_kPlayStatusPlay;
  79. innerFrame.state.position_measured_at =
  80. ctx->timeProvider->getSyncedTimestamp();
  81. break;
  82. case State::Stopped:
  83. break;
  84. case State::Paused:
  85. // Update state and recalculate current song position
  86. innerFrame.state.status = PlayStatus_kPlayStatusPause;
  87. uint32_t diff = ctx->timeProvider->getSyncedTimestamp() -
  88. innerFrame.state.position_measured_at;
  89. this->updatePositionMs(innerFrame.state.position_ms + diff);
  90. break;
  91. }
  92. }
  93. void PlaybackState::syncWithRemote() {
  94. innerFrame.state.context_uri = (char*)realloc(
  95. innerFrame.state.context_uri, strlen(remoteFrame.state.context_uri) + 1);
  96. strcpy(innerFrame.state.context_uri, remoteFrame.state.context_uri);
  97. innerFrame.state.has_playing_track_index = true;
  98. innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
  99. }
  100. bool PlaybackState::isActive() {
  101. return innerFrame.device_state.is_active;
  102. }
  103. void PlaybackState::setActive(bool isActive) {
  104. innerFrame.device_state.is_active = isActive;
  105. if (isActive) {
  106. innerFrame.device_state.became_active_at =
  107. ctx->timeProvider->getSyncedTimestamp();
  108. innerFrame.device_state.has_became_active_at = true;
  109. }
  110. }
  111. void PlaybackState::updatePositionMs(uint32_t position) {
  112. innerFrame.state.position_ms = position;
  113. innerFrame.state.position_measured_at =
  114. ctx->timeProvider->getSyncedTimestamp();
  115. }
  116. void PlaybackState::setVolume(uint32_t volume) {
  117. innerFrame.device_state.volume = volume;
  118. ctx->config.volume = volume;
  119. }
  120. bool PlaybackState::decodeRemoteFrame(std::vector<uint8_t>& data) {
  121. pb_release(Frame_fields, &remoteFrame);
  122. remoteTracks.clear();
  123. pbDecode(remoteFrame, Frame_fields, data);
  124. return true;
  125. }
  126. std::vector<uint8_t> PlaybackState::encodeCurrentFrame(MessageType typ) {
  127. // Prepare current frame info
  128. innerFrame.version = 1;
  129. innerFrame.seq_nr = this->seqNum;
  130. innerFrame.typ = typ;
  131. innerFrame.state_update_id = ctx->timeProvider->getSyncedTimestamp();
  132. innerFrame.has_version = true;
  133. innerFrame.has_seq_nr = true;
  134. innerFrame.recipient_count = 0;
  135. innerFrame.has_state = true;
  136. innerFrame.has_device_state = true;
  137. innerFrame.has_typ = true;
  138. innerFrame.has_state_update_id = true;
  139. this->seqNum += 1;
  140. return pbEncode(Frame_fields, &innerFrame);
  141. }
  142. // Wraps messy nanopb setters. @TODO: find a better way to handle this
  143. void PlaybackState::addCapability(CapabilityType typ, int intValue,
  144. std::vector<std::string> stringValue) {
  145. innerFrame.device_state.capabilities[capabilityIndex].has_typ = true;
  146. this->innerFrame.device_state.capabilities[capabilityIndex].typ = typ;
  147. if (intValue != -1) {
  148. this->innerFrame.device_state.capabilities[capabilityIndex].intValue[0] =
  149. intValue;
  150. this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count =
  151. 1;
  152. } else {
  153. this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count =
  154. 0;
  155. }
  156. for (int x = 0; x < stringValue.size(); x++) {
  157. pbPutString(stringValue[x],
  158. this->innerFrame.device_state.capabilities[capabilityIndex]
  159. .stringValue[x]);
  160. }
  161. this->innerFrame.device_state.capabilities[capabilityIndex]
  162. .stringValue_count = stringValue.size();
  163. this->capabilityIndex += 1;
  164. }