2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "AudioDecoder.h"
11 #include "CodecFactory.h"
14 #include "ServiceBroker.h"
15 #include "application/ApplicationComponents.h"
16 #include "application/ApplicationVolumeHandling.h"
17 #include "music/tags/MusicInfoTag.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/URIUtils.h"
21 #include "utils/log.h"
28 CAudioDecoder::CAudioDecoder()
31 m_rawBuffer
= nullptr;
35 m_status
= STATUS_NO_FILE
;
38 // output buffer (for transferring data from the Pcm Buffer to the rest of the audio chain)
39 memset(&m_outputBuffer
, 0, OUTPUT_SAMPLES
* sizeof(float));
40 memset(&m_pcmInputBuffer
, 0, INPUT_SIZE
* sizeof(unsigned char));
41 memset(&m_inputBuffer
, 0, INPUT_SAMPLES
* sizeof(float));
46 CAudioDecoder::~CAudioDecoder()
51 void CAudioDecoder::Destroy()
53 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
54 m_status
= STATUS_NO_FILE
;
56 m_pcmBuffer
.Destroy();
65 bool CAudioDecoder::Create(const CFileItem
&file
, int64_t seekOffset
)
69 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
71 // reset our playback timing variables
74 // get correct cache size
75 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
76 unsigned int filecache
= settings
->GetInt(CSettings::SETTING_CACHEAUDIO_INTERNET
);
78 filecache
= settings
->GetInt(CSettings::SETTING_CACHE_HARDDISK
);
79 else if ( file
.IsOnDVD() )
80 filecache
= settings
->GetInt(CSettings::SETTING_CACHEAUDIO_DVDROM
);
81 else if (URIUtils::IsOnLAN(file
.GetPath()))
82 filecache
= settings
->GetInt(CSettings::SETTING_CACHEAUDIO_LAN
);
85 m_codec
=CodecFactory::CreateCodecDemux(file
, filecache
* 1024);
87 if (!m_codec
|| !m_codec
->Init(file
, filecache
* 1024))
89 CLog::Log(LOGERROR
, "CAudioDecoder: Unable to Init Codec while loading file {}",
94 unsigned int blockSize
= (m_codec
->m_bitsPerSample
>> 3) * m_codec
->m_format
.m_channelLayout
.Count();
98 CLog::Log(LOGERROR
, "CAudioDecoder: Codec provided invalid parameters ({}-bit, {} channels)",
99 m_codec
->m_bitsPerSample
, GetFormat().m_channelLayout
.Count());
103 /* allocate the pcmBuffer for 2 seconds of audio */
104 m_pcmBuffer
.Create(2 * blockSize
* m_codec
->m_format
.m_sampleRate
);
106 if (file
.HasMusicInfoTag())
108 // set total time from the given tag
109 if (file
.GetMusicInfoTag()->GetDuration())
110 m_codec
->SetTotalTime(file
.GetMusicInfoTag()->GetDuration());
112 // update ReplayGain from the given tag if it's better then original (cuesheet)
113 ReplayGain rgInfo
= m_codec
->m_tag
.GetReplayGain();
115 if (!rgInfo
.Get(ReplayGain::ALBUM
).Valid()
116 && file
.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM
).Valid())
118 rgInfo
.Set(ReplayGain::ALBUM
, file
.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM
));
121 if (!rgInfo
.Get(ReplayGain::TRACK
).Valid()
122 && file
.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK
).Valid())
124 rgInfo
.Set(ReplayGain::TRACK
, file
.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK
));
128 m_codec
->m_tag
.SetReplayGain(rgInfo
);
132 m_codec
->Seek(seekOffset
);
134 m_status
= STATUS_QUEUING
;
141 AEAudioFormat
CAudioDecoder::GetFormat()
143 AEAudioFormat format
;
146 return m_codec
->m_format
;
149 unsigned int CAudioDecoder::GetChannels()
151 return GetFormat().m_channelLayout
.Count();
154 int64_t CAudioDecoder::Seek(int64_t time
)
160 if (time
< 0) time
= 0;
161 if (time
> m_codec
->m_TotalTime
) time
= m_codec
->m_TotalTime
;
162 return m_codec
->Seek(time
);
165 void CAudioDecoder::SetTotalTime(int64_t time
)
168 m_codec
->m_TotalTime
= time
;
171 int64_t CAudioDecoder::TotalTime()
174 return m_codec
->m_TotalTime
;
178 unsigned int CAudioDecoder::GetDataSize(bool checkPktSize
)
180 if (m_status
== STATUS_QUEUING
|| m_status
== STATUS_NO_FILE
)
183 if (m_codec
->m_format
.m_dataFormat
!= AE_FMT_RAW
)
185 // check for end of file and end of buffer
186 if (m_status
== STATUS_ENDING
)
188 if (m_pcmBuffer
.getMaxReadSize() == 0)
189 m_status
= STATUS_ENDED
;
190 else if (checkPktSize
&& m_pcmBuffer
.getMaxReadSize() < PACKET_SIZE
)
191 m_status
= STATUS_ENDED
;
193 return std::min(m_pcmBuffer
.getMaxReadSize() / (m_codec
->m_bitsPerSample
>> 3), (unsigned int)OUTPUT_SAMPLES
);
197 if (m_status
== STATUS_ENDING
)
198 m_status
= STATUS_ENDED
;
199 return m_rawBufferSize
;
203 void *CAudioDecoder::GetData(unsigned int samples
)
205 unsigned int size
= samples
* (m_codec
->m_bitsPerSample
>> 3);
206 if (size
> sizeof(m_outputBuffer
))
208 CLog::Log(LOGERROR
, "CAudioDecoder::GetData - More data was requested then we have space to buffer!");
212 if (size
> m_pcmBuffer
.getMaxReadSize())
216 "CAudioDecoder::GetData() more bytes/samples ({}) requested than we have to give ({})!",
217 size
, m_pcmBuffer
.getMaxReadSize());
218 size
= m_pcmBuffer
.getMaxReadSize();
221 if (m_pcmBuffer
.ReadData((char *)m_outputBuffer
, size
))
223 if (m_status
== STATUS_ENDING
&& m_pcmBuffer
.getMaxReadSize() == 0)
224 m_status
= STATUS_ENDED
;
226 return m_outputBuffer
;
229 CLog::Log(LOGERROR
, "CAudioDecoder::GetData() ReadBinary failed with {} samples", samples
);
233 uint8_t *CAudioDecoder::GetRawData(int &size
)
235 if (m_status
== STATUS_ENDING
)
236 m_status
= STATUS_ENDED
;
240 size
= m_rawBufferSize
;
247 int CAudioDecoder::ReadSamples(int numsamples
)
249 if (m_status
== STATUS_NO_FILE
|| m_status
== STATUS_ENDING
|| m_status
== STATUS_ENDED
)
250 return RET_SLEEP
; // nothing loaded yet
252 // start playing once we're fully queued and we're ready to go
253 if (m_status
== STATUS_QUEUED
&& m_canPlay
)
254 m_status
= STATUS_PLAYING
;
256 // grab a lock to ensure the codec is created at this point.
257 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
259 if (m_codec
->m_format
.m_dataFormat
!= AE_FMT_RAW
)
262 int maxsize
= std::min
<int>(INPUT_SAMPLES
, m_pcmBuffer
.getMaxWriteSize() / (m_codec
->m_bitsPerSample
>> 3));
263 numsamples
= std::min
<int>(numsamples
, maxsize
);
264 numsamples
-= (numsamples
% GetFormat().m_channelLayout
.Count()); // make sure it's divisible by our number of channels
268 int result
= m_codec
->ReadPCM(
269 m_pcmInputBuffer
, static_cast<size_t>(numsamples
* (m_codec
->m_bitsPerSample
>> 3)),
272 if (result
!= READ_ERROR
&& readSize
)
274 // move it into our buffer
275 m_pcmBuffer
.WriteData((char *)m_pcmInputBuffer
, readSize
);
278 if (m_status
== STATUS_QUEUING
&& m_pcmBuffer
.getMaxReadSize() > m_pcmBuffer
.getSize() * 0.9)
280 CLog::Log(LOGINFO
, "AudioDecoder: File is queued");
281 m_status
= STATUS_QUEUED
;
284 if (result
== READ_EOF
) // EOF reached
286 // 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
;
294 if (result
== READ_ERROR
)
296 // error decoding, lets finish up and get out
297 CLog::Log(LOGERROR
, "CAudioDecoder: Error while decoding {}", result
);
300 if (result
== READ_EOF
)
303 // setup ending if we're within set time of the end (currently just EOF)
304 if (m_status
< STATUS_ENDING
)
305 m_status
= STATUS_ENDING
;
311 if (m_rawBufferSize
== 0)
313 int result
= m_codec
->ReadRaw(&m_rawBuffer
, &m_rawBufferSize
);
314 if (result
== READ_SUCCESS
&& m_rawBufferSize
)
316 //! @todo trash this useless ringbuffer
317 if (m_status
== STATUS_QUEUING
)
319 m_status
= STATUS_QUEUED
;
323 else if (result
== READ_ERROR
)
325 // error decoding, lets finish up and get out
326 CLog::Log(LOGERROR
, "CAudioDecoder: Error while decoding {}", result
);
329 else if (result
== READ_EOF
)
332 // setup ending if we're within set time of the end (currently just EOF)
333 if (m_status
< STATUS_ENDING
)
334 m_status
= STATUS_ENDING
;
338 return RET_SLEEP
; // nothing to do
341 bool CAudioDecoder::CanSeek()
344 return m_codec
->CanSeek();
349 float CAudioDecoder::GetReplayGain(float &peakVal
)
351 #define REPLAY_GAIN_DEFAULT_LEVEL 89.0f
352 auto& components
= CServiceBroker::GetAppComponents();
353 const auto appVolume
= components
.GetComponent
<CApplicationVolumeHandling
>();
355 const auto& replayGainSettings
= appVolume
->GetReplayGainSettings();
356 if (replayGainSettings
.iType
== ReplayGain::NONE
)
359 // Compute amount of gain
360 float replaydB
= (float)replayGainSettings
.iNoGainPreAmp
;
362 const ReplayGain
& rgInfo
= m_codec
->m_tag
.GetReplayGain();
363 if (replayGainSettings
.iType
== ReplayGain::ALBUM
)
365 if (rgInfo
.Get(ReplayGain::ALBUM
).HasGain())
367 replaydB
= (float)replayGainSettings
.iPreAmp
+ rgInfo
.Get(ReplayGain::ALBUM
).Gain();
368 if (rgInfo
.Get(ReplayGain::ALBUM
).HasPeak())
369 peak
= rgInfo
.Get(ReplayGain::ALBUM
).Peak();
371 else if (rgInfo
.Get(ReplayGain::TRACK
).HasGain())
373 replaydB
= (float)replayGainSettings
.iPreAmp
+ rgInfo
.Get(ReplayGain::TRACK
).Gain();
374 if (rgInfo
.Get(ReplayGain::TRACK
).HasPeak())
375 peak
= rgInfo
.Get(ReplayGain::TRACK
).Peak();
378 else if (replayGainSettings
.iType
== ReplayGain::TRACK
)
380 if (rgInfo
.Get(ReplayGain::TRACK
).HasGain())
382 replaydB
= (float)replayGainSettings
.iPreAmp
+ rgInfo
.Get(ReplayGain::TRACK
).Gain();
383 if (rgInfo
.Get(ReplayGain::TRACK
).HasPeak())
384 peak
= rgInfo
.Get(ReplayGain::TRACK
).Peak();
386 else if (rgInfo
.Get(ReplayGain::ALBUM
).HasGain())
388 replaydB
= (float)replayGainSettings
.iPreAmp
+ rgInfo
.Get(ReplayGain::ALBUM
).Gain();
389 if (rgInfo
.Get(ReplayGain::ALBUM
).HasPeak())
390 peak
= rgInfo
.Get(ReplayGain::ALBUM
).Peak();
393 // convert to a gain type
394 float replaygain
= std::pow(10.0f
, (replaydB
- REPLAY_GAIN_DEFAULT_LEVEL
) * 0.05f
);
397 "AudioDecoder::GetReplayGain - Final Replaygain applied: {:f}, Track/Album Gain {:f}, "
399 replaygain
, replaydB
, peak
);