diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/cores/paplayer/AudioDecoder.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/cores/paplayer/AudioDecoder.cpp')
-rw-r--r-- | xbmc/cores/paplayer/AudioDecoder.cpp | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/xbmc/cores/paplayer/AudioDecoder.cpp b/xbmc/cores/paplayer/AudioDecoder.cpp new file mode 100644 index 0000000..03dc907 --- /dev/null +++ b/xbmc/cores/paplayer/AudioDecoder.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AudioDecoder.h" + +#include "CodecFactory.h" +#include "FileItem.h" +#include "ICodec.h" +#include "ServiceBroker.h" +#include "application/ApplicationComponents.h" +#include "application/ApplicationVolumeHandling.h" +#include "music/tags/MusicInfoTag.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "utils/log.h" + +#include <cmath> +#include <mutex> + +CAudioDecoder::CAudioDecoder() +{ + m_codec = NULL; + m_rawBuffer = nullptr; + + m_eof = false; + + m_status = STATUS_NO_FILE; + m_canPlay = false; + + // output buffer (for transferring data from the Pcm Buffer to the rest of the audio chain) + memset(&m_outputBuffer, 0, OUTPUT_SAMPLES * sizeof(float)); + memset(&m_pcmInputBuffer, 0, INPUT_SIZE * sizeof(unsigned char)); + memset(&m_inputBuffer, 0, INPUT_SAMPLES * sizeof(float)); + + m_rawBufferSize = 0; +} + +CAudioDecoder::~CAudioDecoder() +{ + Destroy(); +} + +void CAudioDecoder::Destroy() +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + m_status = STATUS_NO_FILE; + + m_pcmBuffer.Destroy(); + + if ( m_codec ) + delete m_codec; + m_codec = NULL; + + m_canPlay = false; +} + +bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset) +{ + Destroy(); + + std::unique_lock<CCriticalSection> lock(m_critSection); + + // reset our playback timing variables + m_eof = false; + + // get correct cache size + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + unsigned int filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_INTERNET); + if ( file.IsHD() ) + filecache = settings->GetInt(CSettings::SETTING_CACHE_HARDDISK); + else if ( file.IsOnDVD() ) + filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_DVDROM); + else if ( file.IsOnLAN() ) + filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_LAN); + + // create our codec + m_codec=CodecFactory::CreateCodecDemux(file, filecache * 1024); + + if (!m_codec || !m_codec->Init(file, filecache * 1024)) + { + CLog::Log(LOGERROR, "CAudioDecoder: Unable to Init Codec while loading file {}", + file.GetDynPath()); + Destroy(); + return false; + } + unsigned int blockSize = (m_codec->m_bitsPerSample >> 3) * m_codec->m_format.m_channelLayout.Count(); + + if (blockSize == 0) + { + CLog::Log(LOGERROR, "CAudioDecoder: Codec provided invalid parameters ({}-bit, {} channels)", + m_codec->m_bitsPerSample, GetFormat().m_channelLayout.Count()); + return false; + } + + /* allocate the pcmBuffer for 2 seconds of audio */ + m_pcmBuffer.Create(2 * blockSize * m_codec->m_format.m_sampleRate); + + if (file.HasMusicInfoTag()) + { + // set total time from the given tag + if (file.GetMusicInfoTag()->GetDuration()) + m_codec->SetTotalTime(file.GetMusicInfoTag()->GetDuration()); + + // update ReplayGain from the given tag if it's better then original (cuesheet) + ReplayGain rgInfo = m_codec->m_tag.GetReplayGain(); + bool anySet = false; + if (!rgInfo.Get(ReplayGain::ALBUM).Valid() + && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM).Valid()) + { + rgInfo.Set(ReplayGain::ALBUM, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM)); + anySet = true; + } + if (!rgInfo.Get(ReplayGain::TRACK).Valid() + && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK).Valid()) + { + rgInfo.Set(ReplayGain::TRACK, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK)); + anySet = true; + } + if (anySet) + m_codec->m_tag.SetReplayGain(rgInfo); + } + + if (seekOffset) + m_codec->Seek(seekOffset); + + m_status = STATUS_QUEUING; + + m_rawBufferSize = 0; + + return true; +} + +AEAudioFormat CAudioDecoder::GetFormat() +{ + AEAudioFormat format; + if (!m_codec) + return format; + return m_codec->m_format; +} + +unsigned int CAudioDecoder::GetChannels() +{ + return GetFormat().m_channelLayout.Count(); +} + +int64_t CAudioDecoder::Seek(int64_t time) +{ + m_pcmBuffer.Clear(); + m_rawBufferSize = 0; + if (!m_codec) + return 0; + if (time < 0) time = 0; + if (time > m_codec->m_TotalTime) time = m_codec->m_TotalTime; + return m_codec->Seek(time); +} + +void CAudioDecoder::SetTotalTime(int64_t time) +{ + if (m_codec) + m_codec->m_TotalTime = time; +} + +int64_t CAudioDecoder::TotalTime() +{ + if (m_codec) + return m_codec->m_TotalTime; + return 0; +} + +unsigned int CAudioDecoder::GetDataSize(bool checkPktSize) +{ + if (m_status == STATUS_QUEUING || m_status == STATUS_NO_FILE) + return 0; + + if (m_codec->m_format.m_dataFormat != AE_FMT_RAW) + { + // check for end of file and end of buffer + if (m_status == STATUS_ENDING) + { + if (m_pcmBuffer.getMaxReadSize() == 0) + m_status = STATUS_ENDED; + else if (checkPktSize && m_pcmBuffer.getMaxReadSize() < PACKET_SIZE) + m_status = STATUS_ENDED; + } + return std::min(m_pcmBuffer.getMaxReadSize() / (m_codec->m_bitsPerSample >> 3), (unsigned int)OUTPUT_SAMPLES); + } + else + { + if (m_status == STATUS_ENDING) + m_status = STATUS_ENDED; + return m_rawBufferSize; + } +} + +void *CAudioDecoder::GetData(unsigned int samples) +{ + unsigned int size = samples * (m_codec->m_bitsPerSample >> 3); + if (size > sizeof(m_outputBuffer)) + { + CLog::Log(LOGERROR, "CAudioDecoder::GetData - More data was requested then we have space to buffer!"); + return NULL; + } + + if (size > m_pcmBuffer.getMaxReadSize()) + { + CLog::Log( + LOGWARNING, + "CAudioDecoder::GetData() more bytes/samples ({}) requested than we have to give ({})!", + size, m_pcmBuffer.getMaxReadSize()); + size = m_pcmBuffer.getMaxReadSize(); + } + + if (m_pcmBuffer.ReadData((char *)m_outputBuffer, size)) + { + if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() == 0) + m_status = STATUS_ENDED; + + return m_outputBuffer; + } + + CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with {} samples", samples); + return NULL; +} + +uint8_t *CAudioDecoder::GetRawData(int &size) +{ + if (m_status == STATUS_ENDING) + m_status = STATUS_ENDED; + + if (m_rawBufferSize) + { + size = m_rawBufferSize; + m_rawBufferSize = 0; + return m_rawBuffer; + } + return nullptr; +} + +int CAudioDecoder::ReadSamples(int numsamples) +{ + if (m_status == STATUS_NO_FILE || m_status == STATUS_ENDING || m_status == STATUS_ENDED) + return RET_SLEEP; // nothing loaded yet + + // start playing once we're fully queued and we're ready to go + if (m_status == STATUS_QUEUED && m_canPlay) + m_status = STATUS_PLAYING; + + // grab a lock to ensure the codec is created at this point. + std::unique_lock<CCriticalSection> lock(m_critSection); + + if (m_codec->m_format.m_dataFormat != AE_FMT_RAW) + { + // Read in more data + int maxsize = std::min<int>(INPUT_SAMPLES, m_pcmBuffer.getMaxWriteSize() / (m_codec->m_bitsPerSample >> 3)); + numsamples = std::min<int>(numsamples, maxsize); + numsamples -= (numsamples % GetFormat().m_channelLayout.Count()); // make sure it's divisible by our number of channels + if (numsamples) + { + size_t readSize = 0; + int result = m_codec->ReadPCM( + m_pcmInputBuffer, static_cast<size_t>(numsamples * (m_codec->m_bitsPerSample >> 3)), + &readSize); + + if (result != READ_ERROR && readSize) + { + // move it into our buffer + m_pcmBuffer.WriteData((char *)m_pcmInputBuffer, readSize); + + // update status + if (m_status == STATUS_QUEUING && m_pcmBuffer.getMaxReadSize() > m_pcmBuffer.getSize() * 0.9) + { + CLog::Log(LOGINFO, "AudioDecoder: File is queued"); + m_status = STATUS_QUEUED; + } + + if (result == READ_EOF) // EOF reached + { + // setup ending if we're within set time of the end (currently just EOF) + m_eof = true; + if (m_status < STATUS_ENDING) + m_status = STATUS_ENDING; + } + + return RET_SUCCESS; + } + if (result == READ_ERROR) + { + // error decoding, lets finish up and get out + CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding {}", result); + return RET_ERROR; + } + if (result == READ_EOF) + { + m_eof = true; + // setup ending if we're within set time of the end (currently just EOF) + if (m_status < STATUS_ENDING) + m_status = STATUS_ENDING; + } + } + } + else + { + if (m_rawBufferSize == 0) + { + int result = m_codec->ReadRaw(&m_rawBuffer, &m_rawBufferSize); + if (result == READ_SUCCESS && m_rawBufferSize) + { + //! @todo trash this useless ringbuffer + if (m_status == STATUS_QUEUING) + { + m_status = STATUS_QUEUED; + } + return RET_SUCCESS; + } + else if (result == READ_ERROR) + { + // error decoding, lets finish up and get out + CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding {}", result); + return RET_ERROR; + } + else if (result == READ_EOF) + { + m_eof = true; + // setup ending if we're within set time of the end (currently just EOF) + if (m_status < STATUS_ENDING) + m_status = STATUS_ENDING; + } + } + } + return RET_SLEEP; // nothing to do +} + +bool CAudioDecoder::CanSeek() +{ + if (m_codec) + return m_codec->CanSeek(); + else + return false; +} + +float CAudioDecoder::GetReplayGain(float &peakVal) +{ +#define REPLAY_GAIN_DEFAULT_LEVEL 89.0f + auto& components = CServiceBroker::GetAppComponents(); + const auto appVolume = components.GetComponent<CApplicationVolumeHandling>(); + + const auto& replayGainSettings = appVolume->GetReplayGainSettings(); + if (replayGainSettings.iType == ReplayGain::NONE) + return 1.0f; + + // Compute amount of gain + float replaydB = (float)replayGainSettings.iNoGainPreAmp; + float peak = 1.0f; + const ReplayGain& rgInfo = m_codec->m_tag.GetReplayGain(); + if (replayGainSettings.iType == ReplayGain::ALBUM) + { + if (rgInfo.Get(ReplayGain::ALBUM).HasGain()) + { + replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain(); + if (rgInfo.Get(ReplayGain::ALBUM).HasPeak()) + peak = rgInfo.Get(ReplayGain::ALBUM).Peak(); + } + else if (rgInfo.Get(ReplayGain::TRACK).HasGain()) + { + replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain(); + if (rgInfo.Get(ReplayGain::TRACK).HasPeak()) + peak = rgInfo.Get(ReplayGain::TRACK).Peak(); + } + } + else if (replayGainSettings.iType == ReplayGain::TRACK) + { + if (rgInfo.Get(ReplayGain::TRACK).HasGain()) + { + replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain(); + if (rgInfo.Get(ReplayGain::TRACK).HasPeak()) + peak = rgInfo.Get(ReplayGain::TRACK).Peak(); + } + else if (rgInfo.Get(ReplayGain::ALBUM).HasGain()) + { + replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain(); + if (rgInfo.Get(ReplayGain::ALBUM).HasPeak()) + peak = rgInfo.Get(ReplayGain::ALBUM).Peak(); + } + } + // convert to a gain type + float replaygain = std::pow(10.0f, (replaydB - REPLAY_GAIN_DEFAULT_LEVEL) * 0.05f); + + CLog::Log(LOGDEBUG, + "AudioDecoder::GetReplayGain - Final Replaygain applied: {:f}, Track/Album Gain {:f}, " + "Peak {:f}", + replaygain, replaydB, peak); + + peakVal = peak; + return replaygain; +} + |