ChunkedAudioStream.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #include "ChunkedAudioStream.h"
  2. #include "Logger.h"
  3. #include "BellUtils.h"
  4. static size_t vorbisReadCb(void *ptr, size_t size, size_t nmemb, ChunkedAudioStream *self)
  5. {
  6. size_t readSize = 0;
  7. while (readSize < nmemb * size && self->byteStream->position() < self->byteStream->size() && self->isRunning) {
  8. size_t bytes = self->byteStream->read((uint8_t *) ptr + readSize, (size * nmemb) - readSize);
  9. if (bytes <= 0) {
  10. CSPOT_LOG(info, "unexpected end/error of stream");
  11. return readSize;
  12. }
  13. readSize += bytes;
  14. }
  15. return readSize;
  16. }
  17. static int vorbisCloseCb(ChunkedAudioStream *self)
  18. {
  19. return 0;
  20. }
  21. static int vorbisSeekCb(ChunkedAudioStream *self, int64_t offset, int whence)
  22. {
  23. if (whence == 0)
  24. {
  25. offset += SPOTIFY_HEADER_SIZE;
  26. }
  27. static constexpr std::array<Whence, 3> seekDirections{
  28. Whence::START, Whence::CURRENT, Whence::END};
  29. self->seek(offset, seekDirections.at(static_cast<size_t>(whence)));
  30. return 0;
  31. }
  32. static long vorbisTellCb(ChunkedAudioStream *self)
  33. {
  34. return static_cast<long>(self->byteStream->position());
  35. }
  36. ChunkedAudioStream::~ChunkedAudioStream()
  37. {
  38. }
  39. ChunkedAudioStream::ChunkedAudioStream(std::vector<uint8_t> fileId, std::vector<uint8_t> audioKey, uint32_t duration, std::shared_ptr<MercuryManager> manager, uint32_t startPositionMs, bool isPaused)
  40. {
  41. this->duration = duration;
  42. this->startPositionMs = startPositionMs;
  43. this->isPaused = isPaused;
  44. // auto beginChunk = manager->fetchAudioChunk(fileId, audioKey, 0, 0x4000);
  45. // beginChunk->keepInMemory = true;
  46. // while(beginChunk->isHeaderFileSizeLoadedSemaphore->twait() != 0);
  47. // this->fileSize = beginChunk->headerFileSize;
  48. // chunks.push_back(beginChunk);
  49. //
  50. // // File size is required for this packet to be downloaded
  51. // this->fetchTraillingPacket();
  52. this->byteStream = std::make_shared<ChunkedByteStream>(manager);
  53. this->byteStream->setFileInfo(fileId, audioKey);
  54. this->byteStream->fetchFileInformation();
  55. vorbisFile = { };
  56. vorbisCallbacks =
  57. {
  58. (decltype(ov_callbacks::read_func)) & vorbisReadCb,
  59. (decltype(ov_callbacks::seek_func)) & vorbisSeekCb,
  60. (decltype(ov_callbacks::close_func)) & vorbisCloseCb,
  61. (decltype(ov_callbacks::tell_func)) & vorbisTellCb,
  62. };
  63. }
  64. void ChunkedAudioStream::seekMs(uint32_t positionMs)
  65. {
  66. byteStream->setEnableLoadAhead(false);
  67. this->seekMutex.lock();
  68. ov_time_seek(&vorbisFile, positionMs);
  69. this->seekMutex.unlock();
  70. byteStream->setEnableLoadAhead(true);
  71. CSPOT_LOG(debug, "--- Finished seeking!");
  72. }
  73. void ChunkedAudioStream::startPlaybackLoop(uint8_t *pcmOut, size_t pcmOut_len)
  74. {
  75. isRunning = true;
  76. byteStream->setEnableLoadAhead(false);
  77. int32_t r = ov_open_callbacks(this, &vorbisFile, NULL, 0, vorbisCallbacks);
  78. CSPOT_LOG(debug, "--- Loaded file");
  79. if (this->startPositionMs != 0)
  80. {
  81. ov_time_seek(&vorbisFile, startPositionMs);
  82. }
  83. bool eof = false;
  84. byteStream->setEnableLoadAhead(true);
  85. while (!eof && isRunning)
  86. {
  87. if (!isPaused)
  88. {
  89. this->seekMutex.lock();
  90. long ret = ov_read(&vorbisFile, (char *)&pcmOut[0], pcmOut_len, &currentSection);
  91. this->seekMutex.unlock();
  92. if (ret == 0)
  93. {
  94. CSPOT_LOG(info, "EOL");
  95. // and done :)
  96. eof = true;
  97. }
  98. else if (ret < 0)
  99. {
  100. CSPOT_LOG(error, "An error has occured in the stream");
  101. // Error in the stream
  102. }
  103. else
  104. {
  105. // Write the actual data
  106. pcmCallback(pcmOut, ret);
  107. // audioSink->feedPCMFrames(data);
  108. }
  109. }
  110. else
  111. {
  112. BELL_SLEEP_MS(100);
  113. }
  114. }
  115. ov_clear(&vorbisFile);
  116. vorbisCallbacks = {};
  117. CSPOT_LOG(debug, "Track finished");
  118. finished = true;
  119. if (eof)
  120. {
  121. this->streamFinishedCallback();
  122. }
  123. }
  124. //
  125. //void ChunkedAudioStream::fetchTraillingPacket()
  126. //{
  127. // auto startPosition = (this->fileSize / 4) - 0x1000;
  128. //
  129. // // AES block size is 16, so the index must be divisible by it
  130. // while ((startPosition * 4) % 16 != 0)
  131. // startPosition++; // ik, ugly lol
  132. //
  133. // auto endChunk = manager->fetchAudioChunk(fileId, audioKey, startPosition, fileSize / 4);
  134. // endChunk->keepInMemory = true;
  135. //
  136. // chunks.push_back(endChunk);
  137. // while (endChunk->isLoadedSemaphore->twait() != 0);
  138. //}
  139. void ChunkedAudioStream::seek(size_t dpos, Whence whence)
  140. {
  141. auto seekPos = 0;
  142. switch (whence)
  143. {
  144. case Whence::START:
  145. seekPos = dpos;
  146. break;
  147. case Whence::CURRENT:
  148. seekPos = byteStream->position() + dpos;
  149. break;
  150. case Whence::END:
  151. seekPos = byteStream->size() + dpos;
  152. break;
  153. }
  154. byteStream->seek(seekPos);
  155. }