TrackPlayer.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #include "TrackPlayer.h"
  2. #include <mutex> // for mutex, scoped_lock
  3. #include <string> // for string
  4. #include <type_traits> // for remove_extent_t
  5. #include <vector> // for vector, vector<>::value_type
  6. #include "BellLogger.h" // for AbstractLogger
  7. #include "BellUtils.h" // for BELL_SLEEP_MS
  8. #include "Logger.h" // for CSPOT_LOG
  9. #include "Packet.h" // for cspot
  10. #include "TrackQueue.h" // for CDNTrackStream, CDNTrackStream::TrackInfo
  11. #include "WrappedSemaphore.h" // for WrappedSemaphore
  12. #ifdef BELL_VORBIS_FLOAT
  13. #define VORBIS_SEEK(file, position) \
  14. (ov_time_seek(file, (double)position / 1000))
  15. #define VORBIS_READ(file, buffer, bufferSize, section) \
  16. (ov_read(file, buffer, bufferSize, 0, 2, 1, section))
  17. #else
  18. #define VORBIS_SEEK(file, position) (ov_time_seek(file, position))
  19. #define VORBIS_READ(file, buffer, bufferSize, section) \
  20. (ov_read(file, buffer, bufferSize, section))
  21. #endif
  22. namespace cspot {
  23. struct Context;
  24. struct TrackReference;
  25. } // namespace cspot
  26. using namespace cspot;
  27. static size_t vorbisReadCb(void* ptr, size_t size, size_t nmemb,
  28. TrackPlayer* self) {
  29. return self->_vorbisRead(ptr, size, nmemb);
  30. }
  31. static int vorbisCloseCb(TrackPlayer* self) {
  32. return self->_vorbisClose();
  33. }
  34. static int vorbisSeekCb(TrackPlayer* self, int64_t offset, int whence) {
  35. return self->_vorbisSeek(offset, whence);
  36. }
  37. static long vorbisTellCb(TrackPlayer* self) {
  38. return self->_vorbisTell();
  39. }
  40. TrackPlayer::TrackPlayer(std::shared_ptr<cspot::Context> ctx,
  41. std::shared_ptr<cspot::TrackQueue> trackQueue,
  42. EOFCallback eof, TrackLoadedCallback trackLoaded)
  43. : bell::Task("cspot_player", 48 * 1024, 5, 1) {
  44. this->ctx = ctx;
  45. this->eofCallback = eof;
  46. this->trackLoaded = trackLoaded;
  47. this->trackQueue = trackQueue;
  48. this->playbackSemaphore = std::make_unique<bell::WrappedSemaphore>(5);
  49. // Initialize vorbis callbacks
  50. vorbisFile = {};
  51. vorbisCallbacks = {
  52. (decltype(ov_callbacks::read_func))&vorbisReadCb,
  53. (decltype(ov_callbacks::seek_func))&vorbisSeekCb,
  54. (decltype(ov_callbacks::close_func))&vorbisCloseCb,
  55. (decltype(ov_callbacks::tell_func))&vorbisTellCb,
  56. };
  57. }
  58. TrackPlayer::~TrackPlayer() {
  59. isRunning = false;
  60. resetState();
  61. std::scoped_lock lock(runningMutex);
  62. }
  63. void TrackPlayer::start() {
  64. if (!isRunning) {
  65. isRunning = true;
  66. startTask();
  67. }
  68. }
  69. void TrackPlayer::stop() {
  70. isRunning = false;
  71. resetState();
  72. std::scoped_lock lock(runningMutex);
  73. }
  74. void TrackPlayer::resetState(bool paused) {
  75. // Mark for reset
  76. this->pendingReset = true;
  77. this->currentSongPlaying = false;
  78. this->startPaused = paused;
  79. std::scoped_lock lock(dataOutMutex);
  80. CSPOT_LOG(info, "Resetting state");
  81. }
  82. void TrackPlayer::seekMs(size_t ms) {
  83. if (inFuture) {
  84. // We're in the middle of the next track, so we need to reset the player in order to seek
  85. resetState();
  86. }
  87. CSPOT_LOG(info, "Seeking...");
  88. this->pendingSeekPositionMs = ms;
  89. }
  90. void TrackPlayer::runTask() {
  91. std::scoped_lock lock(runningMutex);
  92. std::shared_ptr<QueuedTrack> track, newTrack = nullptr;
  93. int trackOffset = 0;
  94. bool eof = false;
  95. bool endOfQueueReached = false;
  96. while (isRunning) {
  97. // Ensure we even have any tracks to play
  98. if (!this->trackQueue->hasTracks() ||
  99. (!pendingReset && endOfQueueReached && trackQueue->isFinished())) {
  100. this->trackQueue->playableSemaphore->twait(300);
  101. continue;
  102. }
  103. // Last track was interrupted, reset to default
  104. if (pendingReset) {
  105. track = nullptr;
  106. pendingReset = false;
  107. inFuture = false;
  108. }
  109. endOfQueueReached = false;
  110. // Wait 800ms. If next reset is requested in meantime, restart the queue.
  111. // Gets rid of excess actions during rapid queueing
  112. BELL_SLEEP_MS(50);
  113. if (pendingReset) {
  114. continue;
  115. }
  116. newTrack = trackQueue->consumeTrack(track, trackOffset);
  117. if (newTrack == nullptr) {
  118. if (trackOffset == -1) {
  119. // Reset required
  120. track = nullptr;
  121. }
  122. BELL_SLEEP_MS(100);
  123. continue;
  124. }
  125. track = newTrack;
  126. inFuture = trackOffset > 0;
  127. if (track->state != QueuedTrack::State::READY) {
  128. track->loadedSemaphore->twait(5000);
  129. if (track->state != QueuedTrack::State::READY) {
  130. CSPOT_LOG(error, "Track failed to load, skipping it");
  131. this->eofCallback();
  132. continue;
  133. }
  134. }
  135. CSPOT_LOG(info, "Got track ID=%s", track->identifier.c_str());
  136. currentSongPlaying = true;
  137. {
  138. std::scoped_lock lock(playbackMutex);
  139. currentTrackStream = track->getAudioFile();
  140. // Open the stream
  141. currentTrackStream->openStream();
  142. if (pendingReset || !currentSongPlaying) {
  143. continue;
  144. }
  145. if (trackOffset == 0 && pendingSeekPositionMs == 0) {
  146. this->trackLoaded(track, startPaused);
  147. startPaused = false;
  148. }
  149. int32_t r =
  150. ov_open_callbacks(this, &vorbisFile, NULL, 0, vorbisCallbacks);
  151. if (pendingSeekPositionMs > 0) {
  152. track->requestedPosition = pendingSeekPositionMs;
  153. }
  154. if (track->requestedPosition > 0) {
  155. VORBIS_SEEK(&vorbisFile, track->requestedPosition);
  156. }
  157. eof = false;
  158. track->loading = true;
  159. CSPOT_LOG(info, "Playing");
  160. while (!eof && currentSongPlaying) {
  161. // Execute seek if needed
  162. if (pendingSeekPositionMs > 0) {
  163. uint32_t seekPosition = pendingSeekPositionMs;
  164. // Reset the pending seek position
  165. pendingSeekPositionMs = 0;
  166. // Seek to the new position
  167. VORBIS_SEEK(&vorbisFile, seekPosition);
  168. }
  169. long ret = VORBIS_READ(&vorbisFile, (char*)&pcmBuffer[0],
  170. pcmBuffer.size(), &currentSection);
  171. if (ret == 0) {
  172. CSPOT_LOG(info, "EOF");
  173. // and done :)
  174. eof = true;
  175. } else if (ret < 0) {
  176. CSPOT_LOG(error, "An error has occured in the stream %d", ret);
  177. currentSongPlaying = false;
  178. } else {
  179. if (this->dataCallback != nullptr) {
  180. auto toWrite = ret;
  181. while (!eof && currentSongPlaying && !pendingReset && toWrite > 0) {
  182. int written = 0;
  183. {
  184. std::scoped_lock dataOutLock(dataOutMutex);
  185. // If reset happened during playback, return
  186. if (!currentSongPlaying || pendingReset)
  187. break;
  188. written = dataCallback(pcmBuffer.data() + (ret - toWrite),
  189. toWrite, track->identifier);
  190. }
  191. if (written == 0) {
  192. BELL_SLEEP_MS(50);
  193. }
  194. toWrite -= written;
  195. }
  196. }
  197. }
  198. }
  199. ov_clear(&vorbisFile);
  200. CSPOT_LOG(info, "Playing done");
  201. // always move back to LOADING (ensure proper seeking after last track has been loaded)
  202. currentTrackStream = nullptr;
  203. track->loading = false;
  204. }
  205. if (eof) {
  206. if (trackQueue->isFinished()) {
  207. endOfQueueReached = true;
  208. }
  209. this->eofCallback();
  210. }
  211. }
  212. }
  213. size_t TrackPlayer::_vorbisRead(void* ptr, size_t size, size_t nmemb) {
  214. if (this->currentTrackStream == nullptr) {
  215. return 0;
  216. }
  217. return this->currentTrackStream->readBytes((uint8_t*)ptr, nmemb * size);
  218. }
  219. size_t TrackPlayer::_vorbisClose() {
  220. return 0;
  221. }
  222. int TrackPlayer::_vorbisSeek(int64_t offset, int whence) {
  223. if (this->currentTrackStream == nullptr) {
  224. return 0;
  225. }
  226. switch (whence) {
  227. case 0:
  228. this->currentTrackStream->seek(offset); // Spotify header offset
  229. break;
  230. case 1:
  231. this->currentTrackStream->seek(this->currentTrackStream->getPosition() +
  232. offset);
  233. break;
  234. case 2:
  235. this->currentTrackStream->seek(this->currentTrackStream->getSize() +
  236. offset);
  237. break;
  238. }
  239. return 0;
  240. }
  241. long TrackPlayer::_vorbisTell() {
  242. if (this->currentTrackStream == nullptr) {
  243. return 0;
  244. }
  245. return this->currentTrackStream->getPosition();
  246. }
  247. void TrackPlayer::setDataCallback(DataCallback callback) {
  248. this->dataCallback = callback;
  249. }