TrackPlayer.cpp 7.9 KB

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