CAudioStream.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. #include "common.h"
  2. #include "CAudioStream.h"
  3. #include <cassert>
  4. #include <iostream>
  5. #include <math.h>
  6. // maximum size of the queue to prevent reading too much data
  7. #define MAX_QUEUE_SIZE (5 * 1024 * 1024)
  8. #define MIN_FRAMES 25
  9. using namespace WallpaperEngine::Audio;
  10. int audio_read_thread (void* arg)
  11. {
  12. SDL_mutex* waitMutex = SDL_CreateMutex ();
  13. auto* stream = static_cast <CAudioStream*> (arg);
  14. AVPacket* packet = av_packet_alloc ();
  15. int ret = 0;
  16. if (waitMutex == nullptr)
  17. sLog.exception ("Cannot create mutex for audio playback waiting");
  18. while (ret >= 0 && stream->getAudioContext ().getApplicationContext ().state.general.keepRunning)
  19. {
  20. // give the cpu some time to play the queued frames if there's enough info there
  21. if (
  22. stream->getQueueSize () >= MAX_QUEUE_SIZE ||
  23. (stream->getQueuePacketCount () > MIN_FRAMES &&
  24. (av_q2d (stream->getTimeBase ()) * stream->getQueueDuration () > 1.0))
  25. )
  26. {
  27. SDL_LockMutex (waitMutex);
  28. SDL_CondWaitTimeout (stream->getWaitCondition (), waitMutex, 10);
  29. SDL_UnlockMutex (waitMutex);
  30. continue;
  31. }
  32. ret = av_read_frame (stream->getFormatContext (), packet);
  33. if (ret == AVERROR_EOF)
  34. {
  35. // seek to the beginning of the file again
  36. avformat_seek_file (stream->getFormatContext (), stream->getAudioStream (), 0, 0, 0, ~AVSEEK_FLAG_FRAME);
  37. avcodec_flush_buffers (stream->getContext ());
  38. // ensure the thread is not killed if audio has to be looped
  39. if (stream->isRepeat())
  40. ret = 0;
  41. continue;
  42. }
  43. // TODO: PROPERLY IMPLEMENT THIS
  44. if (packet->stream_index == stream->getAudioStream ())
  45. stream->queuePacket (packet);
  46. else
  47. av_packet_unref (packet);
  48. if (!stream->isInitialized ())
  49. break;
  50. }
  51. // stop the audio too just in case
  52. stream->stop ();
  53. SDL_DestroyMutex (waitMutex);
  54. return 0;
  55. }
  56. static int audio_read_data_callback (void* streamarg, uint8_t* buffer, int buffer_size)
  57. {
  58. auto stream = static_cast <CAudioStream*> (streamarg);
  59. int left = stream->getLength () - stream->getPosition ();
  60. buffer_size = FFMIN (buffer_size, left);
  61. memcpy (buffer, (uint8_t*) stream->getBuffer () + stream->getPosition (), buffer_size);
  62. // update position
  63. stream->setPosition (stream->getPosition () + buffer_size);
  64. return buffer_size;
  65. }
  66. int64_t audio_seek_data_callback (void* streamarg, int64_t offset, int whence)
  67. {
  68. auto stream = static_cast <CAudioStream*> (streamarg);
  69. if (whence & AVSEEK_SIZE)
  70. return stream->getLength ();
  71. switch (whence)
  72. {
  73. case SEEK_CUR:
  74. stream->setPosition (stream->getPosition () + offset);
  75. break;
  76. case SEEK_SET:
  77. stream->setPosition (offset);
  78. break;
  79. }
  80. return offset;
  81. }
  82. CAudioStream::CAudioStream (CAudioContext& context, const std::string& filename) :
  83. m_audioContext (context),
  84. m_swrctx (nullptr)
  85. {
  86. this->loadCustomContent (filename.c_str ());
  87. }
  88. CAudioStream::CAudioStream (CAudioContext& context, const void* buffer, int length) :
  89. m_audioContext (context),
  90. m_swrctx (nullptr)
  91. {
  92. // setup a custom context first
  93. this->m_formatContext = avformat_alloc_context ();
  94. if (this->m_formatContext == nullptr)
  95. sLog.exception ("Cannot allocate ffmpeg format context");
  96. this->m_buffer = buffer;
  97. this->m_length = length;
  98. this->m_position = 0;
  99. // setup custom io for it
  100. this->m_formatContext->pb = avio_alloc_context (
  101. static_cast <uint8_t*> (av_malloc (4096)),
  102. 4096,
  103. 0,
  104. static_cast <void*> (this),
  105. &audio_read_data_callback,
  106. nullptr,
  107. &audio_seek_data_callback
  108. );
  109. if (this->m_formatContext->pb == nullptr)
  110. sLog.exception ("Cannot create avio context");
  111. // continue the normal load procedure
  112. this->loadCustomContent ();
  113. }
  114. CAudioStream::CAudioStream(CAudioContext& audioContext, AVCodecContext* context) :
  115. m_context (context),
  116. m_queue (new PacketQueue),
  117. m_audioContext (audioContext),
  118. m_swrctx (nullptr)
  119. {
  120. this->initialize ();
  121. }
  122. CAudioStream::~CAudioStream()
  123. {
  124. if (this->m_swrctx != nullptr && swr_is_initialized (this->m_swrctx) == true)
  125. swr_close (this->m_swrctx);
  126. if (this->m_swrctx != nullptr)
  127. swr_free (&this->m_swrctx);
  128. // TODO: FREE EVERYTHING ELSE THAT THIS CLASS HOLDS!
  129. }
  130. void CAudioStream::loadCustomContent (const char* filename)
  131. {
  132. const AVCodec* aCodec;
  133. AVCodecContext* avCodecContext;
  134. if (avformat_open_input (&this->m_formatContext, filename, nullptr, nullptr) != 0)
  135. sLog.exception ("Cannot open audio file: ", filename);
  136. if (avformat_find_stream_info (this->m_formatContext, nullptr) < 0)
  137. sLog.exception ("Cannot determine file format: ", filename);
  138. // find the audio stream
  139. for (int i = 0; i< this->m_formatContext->nb_streams; i ++)
  140. {
  141. if (this->m_formatContext->streams [i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && this->m_audioStream < 0)
  142. this->m_audioStream = i;
  143. }
  144. if (this->m_audioStream == -1)
  145. sLog.exception ("Cannot find an audio stream in file ", filename);
  146. // get the decoder for it and alloc the required context
  147. aCodec = avcodec_find_decoder (this->m_formatContext->streams [this->m_audioStream]->codecpar->codec_id);
  148. if (aCodec == nullptr)
  149. sLog.exception ("Cannot initialize audio decoder for file: ", filename);
  150. // alocate context
  151. avCodecContext = avcodec_alloc_context3 (aCodec);
  152. if (avcodec_parameters_to_context (avCodecContext, this->m_formatContext->streams [this->m_audioStream]->codecpar) != 0)
  153. sLog.exception ("Cannot initialize audio decoder parameters");
  154. // finally open
  155. avcodec_open2 (avCodecContext, aCodec, nullptr);
  156. // initialize default data
  157. this->m_context = avCodecContext;
  158. this->m_queue = new PacketQueue;
  159. this->initialize ();
  160. // initialize an SDL thread to read the file
  161. SDL_CreateThread (audio_read_thread, filename, this);
  162. }
  163. void CAudioStream::initialize ()
  164. {
  165. #if FF_API_FIFO_OLD_API
  166. // allocate the FIFO buffer
  167. this->m_queue->packetList = av_fifo_alloc2 (1, sizeof (MyAVPacketList), AV_FIFO_FLAG_AUTO_GROW);
  168. #else
  169. this->m_queue->packetList = av_fifo_alloc (sizeof (MyAVPacketList));
  170. #endif
  171. int64_t out_channel_layout;
  172. // set output audio channels based on the input audio channels
  173. switch (this->m_audioContext.getChannels ())
  174. {
  175. case 1: out_channel_layout = AV_CH_LAYOUT_MONO; break;
  176. case 2: out_channel_layout = AV_CH_LAYOUT_STEREO; break;
  177. default: out_channel_layout = AV_CH_LAYOUT_SURROUND; break;
  178. }
  179. #if FF_API_OLD_CHANNEL_LAYOUT
  180. av_channel_layout_from_mask (&this->m_out_channel_layout, out_channel_layout);
  181. swr_alloc_set_opts2 (
  182. &this->m_swrctx,
  183. &this->m_out_channel_layout,
  184. this->m_audioContext.getFormat (),
  185. this->m_audioContext.getSampleRate (),
  186. &this->m_context->ch_layout,
  187. this->m_context->sample_fmt,
  188. this->m_context->sample_rate,
  189. 0,
  190. nullptr
  191. );
  192. #else
  193. // initialize swrctx
  194. this->m_swrctx = swr_alloc_set_opts (
  195. nullptr,
  196. out_channel_layout,
  197. this->m_audioContext.getFormat (),
  198. this->m_audioContext.getSampleRate (),
  199. this->getContext ()->channel_layout,
  200. this->getContext ()->sample_fmt,
  201. this->getContext ()->sample_rate,
  202. 0,
  203. nullptr
  204. );
  205. #endif
  206. if (this->m_swrctx == nullptr)
  207. sLog.exception ("Cannot initialize swrctx for audio resampling");
  208. // initialize the context
  209. if (swr_init (this->m_swrctx) < 0)
  210. sLog.exception("Failed to initialize the resampling context.");
  211. // setup the queue information
  212. this->m_queue->mutex = SDL_CreateMutex ();
  213. this->m_queue->cond = SDL_CreateCond ();
  214. this->m_queue->wait = SDL_CreateCond ();
  215. this->m_initialized = true;
  216. }
  217. void CAudioStream::queuePacket(AVPacket *pkt)
  218. {
  219. // clone the packet
  220. AVPacket* clone = av_packet_alloc ();
  221. if (clone == nullptr)
  222. {
  223. av_packet_unref (clone);
  224. return;
  225. }
  226. av_packet_move_ref (clone, pkt);
  227. SDL_LockMutex (this->m_queue->mutex);
  228. bool gotQueued = this->doQueue (clone);
  229. SDL_UnlockMutex (this->m_queue->mutex);
  230. if (!gotQueued)
  231. av_packet_free (&pkt);
  232. }
  233. bool CAudioStream::doQueue (AVPacket* pkt)
  234. {
  235. MyAVPacketList entry { pkt };
  236. #if FF_API_FIFO_OLD_API
  237. // write the entry if possible
  238. if (av_fifo_write (this->m_queue->packetList, &entry, 1) < 0)
  239. return false;
  240. #else
  241. if (av_fifo_space (this->m_queue->packetList) < sizeof (entry))
  242. if (av_fifo_grow (this->m_queue->packetList, sizeof (entry)) < 0)
  243. return false;
  244. av_fifo_generic_write (this->m_queue->packetList, &entry, sizeof (entry), nullptr);
  245. #endif
  246. this->m_queue->nb_packets ++;
  247. this->m_queue->size += entry.packet->size + sizeof (entry);
  248. this->m_queue->duration += entry.packet->duration;
  249. SDL_CondSignal (this->m_queue->cond);
  250. return true;
  251. }
  252. void CAudioStream::dequeuePacket (AVPacket* output)
  253. {
  254. MyAVPacketList entry;
  255. SDL_LockMutex (this->m_queue->mutex);
  256. while (this->m_audioContext.getApplicationContext ().state.general.keepRunning)
  257. {
  258. #if FF_API_FIFO_OLD_API
  259. int ret = av_fifo_read (this->m_queue->packetList, &entry, 1);
  260. #else
  261. int ret = -1;
  262. if (av_fifo_size (this->m_queue->packetList) >= sizeof (entry))
  263. ret = av_fifo_generic_read (this->m_queue->packetList, &entry, sizeof (entry), nullptr);
  264. #endif
  265. // enough data available, read it
  266. if (ret >= 0)
  267. {
  268. this->m_queue->nb_packets --;
  269. this->m_queue->size -= entry.packet->size + sizeof (entry);
  270. this->m_queue->duration -= entry.packet->duration;
  271. // move the reference and free the old one
  272. av_packet_move_ref (output, entry.packet);
  273. av_packet_free (&entry.packet);
  274. break;
  275. }
  276. // make the thread wait if nothing was available
  277. SDL_CondWait (this->m_queue->cond, this->m_queue->mutex);
  278. }
  279. SDL_UnlockMutex (this->m_queue->mutex);
  280. }
  281. AVCodecContext* CAudioStream::getContext ()
  282. {
  283. return this->m_context;
  284. }
  285. AVFormatContext* CAudioStream::getFormatContext ()
  286. {
  287. return this->m_formatContext;
  288. }
  289. int CAudioStream::getAudioStream ()
  290. {
  291. return this->m_audioStream;
  292. }
  293. bool CAudioStream::isInitialized ()
  294. {
  295. return this->m_initialized;
  296. }
  297. void CAudioStream::setRepeat (bool newRepeat)
  298. {
  299. this->m_repeat = newRepeat;
  300. }
  301. bool CAudioStream::isRepeat ()
  302. {
  303. return this->m_repeat;
  304. }
  305. const void* CAudioStream::getBuffer ()
  306. {
  307. return this->m_buffer;
  308. }
  309. int CAudioStream::getLength ()
  310. {
  311. return this->m_length;
  312. }
  313. int CAudioStream::getPosition ()
  314. {
  315. return this->m_position;
  316. }
  317. void CAudioStream::setPosition (int current)
  318. {
  319. this->m_position = current;
  320. }
  321. SDL_cond* CAudioStream::getWaitCondition ()
  322. {
  323. return this->m_queue->wait;
  324. }
  325. int CAudioStream::getQueueSize ()
  326. {
  327. return this->m_queue->size;
  328. }
  329. int CAudioStream::getQueuePacketCount ()
  330. {
  331. return this->m_queue->nb_packets;
  332. }
  333. AVRational CAudioStream::getTimeBase ()
  334. {
  335. return this->m_formatContext->streams [this->m_audioStream]->time_base;
  336. }
  337. int64_t CAudioStream::getQueueDuration ()
  338. {
  339. return this->m_queue->duration;
  340. }
  341. bool CAudioStream::isQueueEmpty ()
  342. {
  343. return this->m_queue->nb_packets == 0;
  344. }
  345. SDL_mutex* CAudioStream::getMutex ()
  346. {
  347. return this->m_queue->mutex;
  348. }
  349. void CAudioStream::stop ()
  350. {
  351. if (!this->isInitialized ())
  352. return;
  353. // stop the threads running
  354. this->m_initialized = false;
  355. }
  356. int CAudioStream::resampleAudio (AVFrame * decoded_audio_frame, uint8_t * out_buf)
  357. {
  358. int out_linesize = 0;
  359. int ret;
  360. int out_nb_channels;
  361. int in_nb_samples;
  362. int out_nb_samples;
  363. int max_out_nb_samples;
  364. uint8_t** resampled_data = nullptr;
  365. int resampled_data_size;
  366. // retrieve number of audio samples (per channel)
  367. in_nb_samples = decoded_audio_frame->nb_samples;
  368. if (in_nb_samples <= 0)
  369. {
  370. sLog.error("in_nb_samples error.");
  371. return -1;
  372. }
  373. max_out_nb_samples = out_nb_samples = av_rescale_rnd(
  374. in_nb_samples,
  375. this->m_audioContext.getSampleRate (),
  376. this->getContext ()->sample_rate,
  377. AV_ROUND_UP
  378. );
  379. // check rescaling was successful
  380. if (max_out_nb_samples <= 0)
  381. {
  382. sLog.error("av_rescale_rnd error.");
  383. return -1;
  384. }
  385. // get number of output audio channels
  386. #if FF_API_OLD_CHANNEL_LAYOUT
  387. out_nb_channels = this->getContext ()->ch_layout.nb_channels;
  388. #else
  389. int64_t out_channel_layout;
  390. // set output audio channels based on the input audio channels
  391. switch (this->m_audioContext.getChannels ())
  392. {
  393. case 1: out_channel_layout = AV_CH_LAYOUT_MONO; break;
  394. case 2: out_channel_layout = AV_CH_LAYOUT_STEREO; break;
  395. default: out_channel_layout = AV_CH_LAYOUT_SURROUND; break;
  396. }
  397. out_nb_channels = av_get_channel_layout_nb_channels(out_channel_layout);
  398. #endif
  399. ret = av_samples_alloc_array_and_samples(
  400. &resampled_data,
  401. &out_linesize,
  402. out_nb_channels,
  403. out_nb_samples,
  404. this->m_audioContext.getFormat (),
  405. 0
  406. );
  407. if (ret < 0)
  408. {
  409. sLog.error("av_samples_alloc_array_and_samples() error: Could not allocate destination samples.");
  410. return -1;
  411. }
  412. // retrieve output samples number taking into account the progressive delay
  413. out_nb_samples = av_rescale_rnd(
  414. swr_get_delay(this->m_swrctx, this->getContext ()->sample_rate) + in_nb_samples,
  415. this->m_audioContext.getSampleRate (),
  416. this->getContext ()->sample_rate,
  417. AV_ROUND_UP
  418. );
  419. // check output samples number was correctly retrieved
  420. if (out_nb_samples <= 0)
  421. {
  422. sLog.error("av_rescale_rnd error");
  423. return -1;
  424. }
  425. if (out_nb_samples > max_out_nb_samples)
  426. {
  427. // free memory block and set pointer to NULL
  428. av_free(resampled_data[0]);
  429. // Allocate a samples buffer for out_nb_samples samples
  430. ret = av_samples_alloc(
  431. resampled_data,
  432. &out_linesize,
  433. out_nb_channels,
  434. out_nb_samples,
  435. this->m_audioContext.getFormat (),
  436. 1
  437. );
  438. // check samples buffer correctly allocated
  439. if (ret < 0)
  440. {
  441. sLog.error("av_samples_alloc failed.");
  442. return -1;
  443. }
  444. max_out_nb_samples = out_nb_samples;
  445. }
  446. // do the actual audio data resampling
  447. ret = swr_convert(
  448. this->m_swrctx,
  449. resampled_data,
  450. max_out_nb_samples,
  451. (const uint8_t **) decoded_audio_frame->data,
  452. decoded_audio_frame->nb_samples
  453. );
  454. // check audio conversion was successful
  455. if (ret < 0)
  456. {
  457. sLog.error("swr_convert_error.");
  458. return -1;
  459. }
  460. // Get the required buffer size for the given audio parameters
  461. resampled_data_size = av_samples_get_buffer_size(
  462. &out_linesize,
  463. out_nb_channels,
  464. ret,
  465. this->m_audioContext.getFormat (),
  466. 1
  467. );
  468. // check audio buffer size
  469. if (resampled_data_size < 0)
  470. {
  471. sLog.error ("av_samples_get_buffer_size error.");
  472. return -1;
  473. }
  474. // copy the resampled data to the output buffer
  475. memcpy(out_buf, resampled_data[0], resampled_data_size);
  476. /*
  477. * Memory Cleanup.
  478. */
  479. if (resampled_data)
  480. {
  481. // free memory block and set pointer to NULL
  482. av_freep(&resampled_data[0]);
  483. }
  484. av_freep(&resampled_data);
  485. resampled_data = NULL;
  486. return resampled_data_size;
  487. }
  488. int CAudioStream::decodeFrame (uint8_t* audioBuffer, int bufferSize)
  489. {
  490. AVPacket *pkt = av_packet_alloc ();
  491. static uint8_t *audio_pkt_data = NULL;
  492. static int audio_pkt_size = 0;
  493. int len1, data_size;
  494. // allocate a new frame, used to decode audio packets
  495. static AVFrame * avFrame = NULL;
  496. avFrame = av_frame_alloc();
  497. if (!avFrame)
  498. {
  499. sLog.error("Could not allocate AVFrame.\n");
  500. return -1;
  501. }
  502. // block until there's any data in the buffers
  503. while (this->m_audioContext.getApplicationContext ().state.general.keepRunning) {
  504. while (audio_pkt_size > 0) {
  505. int got_frame = 0;
  506. int ret = avcodec_receive_frame(this->getContext (), avFrame);
  507. if (ret == 0)
  508. got_frame = 1;
  509. if (ret == AVERROR(EAGAIN))
  510. ret = 0;
  511. if (ret == 0)
  512. ret = avcodec_send_packet(this->getContext (), pkt);
  513. if (ret < 0 && ret != AVERROR (EAGAIN))
  514. return -1;
  515. len1 = pkt->size;
  516. if (len1 < 0)
  517. {
  518. // if error, skip frame
  519. audio_pkt_size = 0;
  520. break;
  521. }
  522. audio_pkt_data += len1;
  523. audio_pkt_size -= len1;
  524. data_size = 0;
  525. if (got_frame) {
  526. // audio resampling
  527. data_size = this->resampleAudio (avFrame, audioBuffer);
  528. assert(data_size <= bufferSize);
  529. }
  530. if (data_size <= 0) {
  531. // no data found, keep waiting
  532. continue;
  533. }
  534. // some data was found
  535. return data_size;
  536. }
  537. if (pkt->data)
  538. av_packet_unref(pkt);
  539. this->dequeuePacket (pkt);
  540. audio_pkt_data = pkt->data;
  541. audio_pkt_size = pkt->size;
  542. }
  543. return 0;
  544. }
  545. CAudioContext& CAudioStream::getAudioContext () const
  546. {
  547. return this->m_audioContext;
  548. }