PageRenderTime 44ms CodeModel.GetById 13ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

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