/xbmc/cores/paplayer/AudioDecoder.cpp

http://github.com/xbmc/xbmc · C++ · 374 lines · 293 code · 53 blank · 28 comment · 106 complexity · 8fa1da4c792b7b8bfa6e4acc028d9788 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "AudioDecoder.h"
  9. #include "Application.h"
  10. #include "CodecFactory.h"
  11. #include "FileItem.h"
  12. #include "ServiceBroker.h"
  13. #include "music/tags/MusicInfoTag.h"
  14. #include "settings/Settings.h"
  15. #include "settings/SettingsComponent.h"
  16. #include "threads/SingleLock.h"
  17. #include "utils/log.h"
  18. #include <math.h>
  19. CAudioDecoder::CAudioDecoder()
  20. {
  21. m_codec = NULL;
  22. m_rawBuffer = nullptr;
  23. m_eof = false;
  24. m_status = STATUS_NO_FILE;
  25. m_canPlay = false;
  26. // output buffer (for transferring data from the Pcm Buffer to the rest of the audio chain)
  27. memset(&m_outputBuffer, 0, OUTPUT_SAMPLES * sizeof(float));
  28. memset(&m_pcmInputBuffer, 0, INPUT_SIZE * sizeof(unsigned char));
  29. memset(&m_inputBuffer, 0, INPUT_SAMPLES * sizeof(float));
  30. m_rawBufferSize = 0;
  31. }
  32. CAudioDecoder::~CAudioDecoder()
  33. {
  34. Destroy();
  35. }
  36. void CAudioDecoder::Destroy()
  37. {
  38. CSingleLock lock(m_critSection);
  39. m_status = STATUS_NO_FILE;
  40. m_pcmBuffer.Destroy();
  41. if ( m_codec )
  42. delete m_codec;
  43. m_codec = NULL;
  44. m_canPlay = false;
  45. }
  46. bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset)
  47. {
  48. Destroy();
  49. CSingleLock lock(m_critSection);
  50. // reset our playback timing variables
  51. m_eof = false;
  52. // get correct cache size
  53. const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
  54. unsigned int filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_INTERNET);
  55. if ( file.IsHD() )
  56. filecache = settings->GetInt(CSettings::SETTING_CACHE_HARDDISK);
  57. else if ( file.IsOnDVD() )
  58. filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_DVDROM);
  59. else if ( file.IsOnLAN() )
  60. filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_LAN);
  61. // create our codec
  62. m_codec=CodecFactory::CreateCodecDemux(file, filecache * 1024);
  63. if (!m_codec || !m_codec->Init(file, filecache * 1024))
  64. {
  65. CLog::Log(LOGERROR, "CAudioDecoder: Unable to Init Codec while loading file %s", file.GetDynPath().c_str());
  66. Destroy();
  67. return false;
  68. }
  69. unsigned int blockSize = (m_codec->m_bitsPerSample >> 3) * m_codec->m_format.m_channelLayout.Count();
  70. if (blockSize == 0)
  71. {
  72. CLog::Log(LOGERROR, "CAudioDecoder: Codec provided invalid parameters (%d-bit, %u channels)",
  73. m_codec->m_bitsPerSample, GetFormat().m_channelLayout.Count());
  74. return false;
  75. }
  76. /* allocate the pcmBuffer for 2 seconds of audio */
  77. m_pcmBuffer.Create(2 * blockSize * m_codec->m_format.m_sampleRate);
  78. if (file.HasMusicInfoTag())
  79. {
  80. // set total time from the given tag
  81. if (file.GetMusicInfoTag()->GetDuration())
  82. m_codec->SetTotalTime(file.GetMusicInfoTag()->GetDuration());
  83. // update ReplayGain from the given tag if it's better then original (cuesheet)
  84. ReplayGain rgInfo = m_codec->m_tag.GetReplayGain();
  85. bool anySet = false;
  86. if (!rgInfo.Get(ReplayGain::ALBUM).Valid()
  87. && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM).Valid())
  88. {
  89. rgInfo.Set(ReplayGain::ALBUM, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM));
  90. anySet = true;
  91. }
  92. if (!rgInfo.Get(ReplayGain::TRACK).Valid()
  93. && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK).Valid())
  94. {
  95. rgInfo.Set(ReplayGain::TRACK, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK));
  96. anySet = true;
  97. }
  98. if (anySet)
  99. m_codec->m_tag.SetReplayGain(rgInfo);
  100. }
  101. if (seekOffset)
  102. m_codec->Seek(seekOffset);
  103. m_status = STATUS_QUEUING;
  104. m_rawBufferSize = 0;
  105. return true;
  106. }
  107. AEAudioFormat CAudioDecoder::GetFormat()
  108. {
  109. AEAudioFormat format;
  110. if (!m_codec)
  111. return format;
  112. return m_codec->m_format;
  113. }
  114. int64_t CAudioDecoder::Seek(int64_t time)
  115. {
  116. m_pcmBuffer.Clear();
  117. m_rawBufferSize = 0;
  118. if (!m_codec)
  119. return 0;
  120. if (time < 0) time = 0;
  121. if (time > m_codec->m_TotalTime) time = m_codec->m_TotalTime;
  122. return m_codec->Seek(time);
  123. }
  124. void CAudioDecoder::SetTotalTime(int64_t time)
  125. {
  126. if (m_codec)
  127. m_codec->m_TotalTime = time;
  128. }
  129. int64_t CAudioDecoder::TotalTime()
  130. {
  131. if (m_codec)
  132. return m_codec->m_TotalTime;
  133. return 0;
  134. }
  135. unsigned int CAudioDecoder::GetDataSize(bool checkPktSize)
  136. {
  137. if (m_status == STATUS_QUEUING || m_status == STATUS_NO_FILE)
  138. return 0;
  139. if (m_codec->m_format.m_dataFormat != AE_FMT_RAW)
  140. {
  141. // check for end of file and end of buffer
  142. if (m_status == STATUS_ENDING)
  143. {
  144. if (m_pcmBuffer.getMaxReadSize() == 0)
  145. m_status = STATUS_ENDED;
  146. else if (checkPktSize && m_pcmBuffer.getMaxReadSize() < PACKET_SIZE)
  147. m_status = STATUS_ENDED;
  148. }
  149. return std::min(m_pcmBuffer.getMaxReadSize() / (m_codec->m_bitsPerSample >> 3), (unsigned int)OUTPUT_SAMPLES);
  150. }
  151. else
  152. {
  153. if (m_status == STATUS_ENDING)
  154. m_status = STATUS_ENDED;
  155. return m_rawBufferSize;
  156. }
  157. }
  158. void *CAudioDecoder::GetData(unsigned int samples)
  159. {
  160. unsigned int size = samples * (m_codec->m_bitsPerSample >> 3);
  161. if (size > sizeof(m_outputBuffer))
  162. {
  163. CLog::Log(LOGERROR, "CAudioDecoder::GetData - More data was requested then we have space to buffer!");
  164. return NULL;
  165. }
  166. if (size > m_pcmBuffer.getMaxReadSize())
  167. {
  168. CLog::Log(LOGWARNING, "CAudioDecoder::GetData() more bytes/samples (%i) requested than we have to give (%i)!", size, m_pcmBuffer.getMaxReadSize());
  169. size = m_pcmBuffer.getMaxReadSize();
  170. }
  171. if (m_pcmBuffer.ReadData((char *)m_outputBuffer, size))
  172. {
  173. if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() == 0)
  174. m_status = STATUS_ENDED;
  175. return m_outputBuffer;
  176. }
  177. CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with %i samples", samples);
  178. return NULL;
  179. }
  180. uint8_t *CAudioDecoder::GetRawData(int &size)
  181. {
  182. if (m_status == STATUS_ENDING)
  183. m_status = STATUS_ENDED;
  184. if (m_rawBufferSize)
  185. {
  186. size = m_rawBufferSize;
  187. m_rawBufferSize = 0;
  188. return m_rawBuffer;
  189. }
  190. return nullptr;
  191. }
  192. int CAudioDecoder::ReadSamples(int numsamples)
  193. {
  194. if (m_status == STATUS_NO_FILE || m_status == STATUS_ENDING || m_status == STATUS_ENDED)
  195. return RET_SLEEP; // nothing loaded yet
  196. // start playing once we're fully queued and we're ready to go
  197. if (m_status == STATUS_QUEUED && m_canPlay)
  198. m_status = STATUS_PLAYING;
  199. // grab a lock to ensure the codec is created at this point.
  200. CSingleLock lock(m_critSection);
  201. if (m_codec->m_format.m_dataFormat != AE_FMT_RAW)
  202. {
  203. // Read in more data
  204. int maxsize = std::min<int>(INPUT_SAMPLES, m_pcmBuffer.getMaxWriteSize() / (m_codec->m_bitsPerSample >> 3));
  205. numsamples = std::min<int>(numsamples, maxsize);
  206. numsamples -= (numsamples % GetFormat().m_channelLayout.Count()); // make sure it's divisible by our number of channels
  207. if (numsamples)
  208. {
  209. int readSize = 0;
  210. int result = m_codec->ReadPCM(m_pcmInputBuffer, numsamples * (m_codec->m_bitsPerSample >> 3), &readSize);
  211. if (result != READ_ERROR && readSize)
  212. {
  213. // move it into our buffer
  214. m_pcmBuffer.WriteData((char *)m_pcmInputBuffer, readSize);
  215. // update status
  216. if (m_status == STATUS_QUEUING && m_pcmBuffer.getMaxReadSize() > m_pcmBuffer.getSize() * 0.9)
  217. {
  218. CLog::Log(LOGINFO, "AudioDecoder: File is queued");
  219. m_status = STATUS_QUEUED;
  220. }
  221. if (result == READ_EOF) // EOF reached
  222. {
  223. // setup ending if we're within set time of the end (currently just EOF)
  224. m_eof = true;
  225. if (m_status < STATUS_ENDING)
  226. m_status = STATUS_ENDING;
  227. }
  228. return RET_SUCCESS;
  229. }
  230. if (result == READ_ERROR)
  231. {
  232. // error decoding, lets finish up and get out
  233. CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding %i", result);
  234. return RET_ERROR;
  235. }
  236. if (result == READ_EOF)
  237. {
  238. m_eof = true;
  239. // setup ending if we're within set time of the end (currently just EOF)
  240. if (m_status < STATUS_ENDING)
  241. m_status = STATUS_ENDING;
  242. }
  243. }
  244. }
  245. else
  246. {
  247. if (m_rawBufferSize == 0)
  248. {
  249. int result = m_codec->ReadRaw(&m_rawBuffer, &m_rawBufferSize);
  250. if (result == READ_SUCCESS && m_rawBufferSize)
  251. {
  252. //! @todo trash this useless ringbuffer
  253. if (m_status == STATUS_QUEUING)
  254. {
  255. m_status = STATUS_QUEUED;
  256. }
  257. return RET_SUCCESS;
  258. }
  259. else if (result == READ_ERROR)
  260. {
  261. // error decoding, lets finish up and get out
  262. CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding %i", result);
  263. return RET_ERROR;
  264. }
  265. else if (result == READ_EOF)
  266. {
  267. m_eof = true;
  268. // setup ending if we're within set time of the end (currently just EOF)
  269. if (m_status < STATUS_ENDING)
  270. m_status = STATUS_ENDING;
  271. }
  272. }
  273. }
  274. return RET_SLEEP; // nothing to do
  275. }
  276. float CAudioDecoder::GetReplayGain(float &peakVal)
  277. {
  278. #define REPLAY_GAIN_DEFAULT_LEVEL 89.0f
  279. const ReplayGainSettings &replayGainSettings = g_application.GetReplayGainSettings();
  280. if (replayGainSettings.iType == ReplayGain::NONE)
  281. return 1.0f;
  282. // Compute amount of gain
  283. float replaydB = (float)replayGainSettings.iNoGainPreAmp;
  284. float peak = 1.0f;
  285. const ReplayGain& rgInfo = m_codec->m_tag.GetReplayGain();
  286. if (replayGainSettings.iType == ReplayGain::ALBUM)
  287. {
  288. if (rgInfo.Get(ReplayGain::ALBUM).HasGain())
  289. {
  290. replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain();
  291. if (rgInfo.Get(ReplayGain::ALBUM).HasPeak())
  292. peak = rgInfo.Get(ReplayGain::ALBUM).Peak();
  293. }
  294. else if (rgInfo.Get(ReplayGain::TRACK).HasGain())
  295. {
  296. replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain();
  297. if (rgInfo.Get(ReplayGain::TRACK).HasPeak())
  298. peak = rgInfo.Get(ReplayGain::TRACK).Peak();
  299. }
  300. }
  301. else if (replayGainSettings.iType == ReplayGain::TRACK)
  302. {
  303. if (rgInfo.Get(ReplayGain::TRACK).HasGain())
  304. {
  305. replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain();
  306. if (rgInfo.Get(ReplayGain::TRACK).HasPeak())
  307. peak = rgInfo.Get(ReplayGain::TRACK).Peak();
  308. }
  309. else if (rgInfo.Get(ReplayGain::ALBUM).HasGain())
  310. {
  311. replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain();
  312. if (rgInfo.Get(ReplayGain::ALBUM).HasPeak())
  313. peak = rgInfo.Get(ReplayGain::ALBUM).Peak();
  314. }
  315. }
  316. // convert to a gain type
  317. float replaygain = pow(10.0f, (replaydB - REPLAY_GAIN_DEFAULT_LEVEL)* 0.05f);
  318. CLog::Log(LOGDEBUG, "AudioDecoder::GetReplayGain - Final Replaygain applied: %f, Track/Album Gain %f, Peak %f", replaygain, replaydB, peak);
  319. peakVal = peak;
  320. return replaygain;
  321. }