[WASAPI] set stream audio category
[xbmc.git] / xbmc / cores / paplayer / PAPlayer.cpp
blob97a7ad5262a33634fd29f7e9238cfde43bfe8d47
1 /*
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.
7 */
9 #include "PAPlayer.h"
11 #include "FileItem.h"
12 #include "ICodec.h"
13 #include "ServiceBroker.h"
14 #include "URL.h"
15 #include "Util.h"
16 #include "cores/AudioEngine/Interfaces/AE.h"
17 #include "cores/AudioEngine/Interfaces/AEStream.h"
18 #include "cores/AudioEngine/Utils/AEStreamData.h"
19 #include "cores/AudioEngine/Utils/AEUtil.h"
20 #include "cores/DataCacheCore.h"
21 #include "cores/VideoPlayer/Process/ProcessInfo.h"
22 #include "messaging/ApplicationMessenger.h"
23 #include "music/MusicFileItemClassify.h"
24 #include "music/tags/MusicInfoTag.h"
25 #include "settings/AdvancedSettings.h"
26 #include "settings/Settings.h"
27 #include "settings/SettingsComponent.h"
28 #include "threads/SystemClock.h"
29 #include "utils/JobManager.h"
30 #include "utils/log.h"
31 #include "video/Bookmark.h"
33 #include <memory>
34 #include <mutex>
36 using namespace KODI;
37 using namespace std::chrono_literals;
39 #define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
40 #define FAST_XFADE_TIME 80 /* 80 milliseconds */
41 #define MAX_SKIP_XFADE_TIME 2000 /* max 2 seconds crossfade on track skip */
43 // PAP: Psycho-acoustic Audio Player
44 // Supporting all open audio codec standards.
45 // First one being nullsoft's nsv audio decoder format
47 PAPlayer::PAPlayer(IPlayerCallback& callback)
48 : IPlayer(callback), CThread("PAPlayer"), m_playbackSpeed(1), m_audioCallback(NULL)
50 memset(&m_playerGUIData, 0, sizeof(m_playerGUIData));
51 m_processInfo.reset(CProcessInfo::CreateInstance());
52 m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore());
55 PAPlayer::~PAPlayer()
57 CloseFile();
60 void PAPlayer::SoftStart(bool wait/* = false */)
62 std::unique_lock<CCriticalSection> lock(m_streamsLock);
63 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
65 StreamInfo* si = *itt;
66 if (si->m_fadeOutTriggered)
67 continue;
69 si->m_stream->Resume();
70 si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
73 if (wait)
75 /* wait for them to fade in */
76 lock.unlock();
77 CThread::Sleep(std::chrono::milliseconds(FAST_XFADE_TIME));
78 lock.lock();
80 /* be sure they have faded in */
81 while(wait)
83 wait = false;
84 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
86 StreamInfo* si = *itt;
87 if (si->m_stream->IsFading())
89 lock.unlock();
90 wait = true;
91 CThread::Sleep(1ms);
92 lock.lock();
93 break;
100 void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
102 /* fade all the streams out fast for a nice soft stop */
103 std::unique_lock<CCriticalSection> lock(m_streamsLock);
104 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
106 StreamInfo* si = *itt;
107 if (si->m_stream)
108 si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
110 if (close)
112 si->m_prepareTriggered = true;
113 si->m_playNextTriggered = true;
114 si->m_fadeOutTriggered = true;
118 /* if we are going to wait for them to finish fading */
119 if(wait)
121 // fail safe timer, do not wait longer than 1000ms
122 XbmcThreads::EndTime<> timer(1000ms);
124 /* wait for them to fade out */
125 lock.unlock();
126 CThread::Sleep(std::chrono::milliseconds(FAST_XFADE_TIME));
127 lock.lock();
129 /* be sure they have faded out */
130 while(wait && !CServiceBroker::GetActiveAE()->IsSuspended() && !timer.IsTimePast())
132 wait = false;
133 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
135 StreamInfo* si = *itt;
136 if (si->m_stream && si->m_stream->IsFading())
138 lock.unlock();
139 wait = true;
140 CThread::Sleep(1ms);
141 lock.lock();
142 break;
147 /* if we are not closing the streams, pause them */
148 if (!close)
150 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
152 StreamInfo* si = *itt;
153 si->m_stream->Pause();
159 void PAPlayer::CloseAllStreams(bool fade/* = true */)
161 if (!fade)
163 std::unique_lock<CCriticalSection> lock(m_streamsLock);
164 while (!m_streams.empty())
166 StreamInfo* si = m_streams.front();
167 m_streams.pop_front();
169 if (si->m_stream)
171 CloseFileCB(*si);
172 si->m_stream.reset();
175 si->m_decoder.Destroy();
176 delete si;
179 while (!m_finishing.empty())
181 StreamInfo* si = m_finishing.front();
182 m_finishing.pop_front();
184 if (si->m_stream)
186 CloseFileCB(*si);
187 si->m_stream.reset();
190 si->m_decoder.Destroy();
191 delete si;
193 m_currentStream = nullptr;
195 else
197 SoftStop(false, true);
198 std::unique_lock<CCriticalSection> lock(m_streamsLock);
199 m_currentStream = NULL;
203 bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
205 m_defaultCrossfadeMS = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MUSICPLAYER_CROSSFADE) * 1000;
206 m_fullScreen = options.fullscreen;
208 if (m_streams.size() > 1 || !m_defaultCrossfadeMS || m_isPaused)
210 CloseAllStreams(!m_isPaused);
211 StopThread();
212 m_isPaused = false; // Make sure to reset the pause state
216 std::unique_lock<CCriticalSection> lock(m_streamsLock);
217 m_jobCounter++;
219 CServiceBroker::GetJobManager()->Submit([=, this]() { QueueNextFileEx(file, false); }, this,
220 CJob::PRIORITY_NORMAL);
222 std::unique_lock<CCriticalSection> lock(m_streamsLock);
223 if (m_streams.size() == 2)
225 //do a short crossfade on trackskip, set to max 2 seconds for these prev/next transitions
226 m_upcomingCrossfadeMS = std::min(m_defaultCrossfadeMS, (unsigned int)MAX_SKIP_XFADE_TIME);
228 //start transition to next track
229 StreamInfo* si = m_streams.front();
230 si->m_playNextAtFrame = si->m_framesSent; //start next track at current frame
231 si->m_prepareTriggered = true; //next track is ready to go
233 lock.unlock();
235 if (!IsRunning())
236 Create();
238 /* trigger playback start */
239 m_isPlaying = true;
240 m_startEvent.Set();
242 // OnPlayBackStarted to be made only once. Callback processing may be slower than player process
243 // so clear signal flag first otherwise async stream processing could also make callback
244 m_signalStarted = false;
245 m_callback.OnPlayBackStarted(file);
247 return true;
250 void PAPlayer::UpdateCrossfadeTime(const CFileItem& file)
252 // we explicitly disable crossfading for audio cds
253 if (MUSIC::IsCDDA(file))
254 m_upcomingCrossfadeMS = 0;
255 else
256 m_upcomingCrossfadeMS = m_defaultCrossfadeMS = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MUSICPLAYER_CROSSFADE) * 1000;
258 if (m_upcomingCrossfadeMS)
260 if (!m_currentStream || (file.HasMusicInfoTag() &&
261 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
262 CSettings::SETTING_MUSICPLAYER_CROSSFADEALBUMTRACKS) &&
263 m_currentStream->m_fileItem->HasMusicInfoTag() &&
264 (m_currentStream->m_fileItem->GetMusicInfoTag()->GetAlbum() != "") &&
265 (m_currentStream->m_fileItem->GetMusicInfoTag()->GetAlbum() ==
266 file.GetMusicInfoTag()->GetAlbum()) &&
267 (m_currentStream->m_fileItem->GetMusicInfoTag()->GetDiscNumber() ==
268 file.GetMusicInfoTag()->GetDiscNumber()) &&
269 (m_currentStream->m_fileItem->GetMusicInfoTag()->GetTrackNumber() ==
270 file.GetMusicInfoTag()->GetTrackNumber() - 1)))
272 //do not crossfade when playing consecutive albumtracks
273 m_upcomingCrossfadeMS = 0;
278 bool PAPlayer::QueueNextFile(const CFileItem &file)
281 std::unique_lock<CCriticalSection> lock(m_streamsLock);
282 m_jobCounter++;
284 CServiceBroker::GetJobManager()->Submit([this, file]() { QueueNextFileEx(file, true); }, this,
285 CJob::PRIORITY_NORMAL);
287 return true;
290 bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn)
292 if (m_currentStream)
294 // check if we advance a track of a CUE sheet
295 // if this is the case we don't need to open a new stream
296 std::string newURL = file.GetDynURL().GetFileName();
297 std::string oldURL = m_currentStream->m_fileItem->GetDynURL().GetFileName();
298 if (newURL.compare(oldURL) == 0 && file.GetStartOffset() &&
299 file.GetStartOffset() == m_currentStream->m_fileItem->GetEndOffset() && m_currentStream &&
300 m_currentStream->m_prepareTriggered)
302 m_currentStream->m_nextFileItem = std::make_unique<CFileItem>(file);
303 m_upcomingCrossfadeMS = 0;
304 return true;
306 m_currentStream->m_nextFileItem.reset();
309 StreamInfo *si = new StreamInfo();
310 si->m_fileItem = std::make_unique<CFileItem>(file);
312 // Start stream at zero offset
313 si->m_startOffset = 0;
314 //File item start offset defines where in song to resume
315 double starttime = CUtil::ConvertMilliSecsToSecs(si->m_fileItem->GetStartOffset());
317 // Music from cuesheet => "item_start" and offset match
318 // Start offset defines where this song starts in file of multiple songs
319 if (si->m_fileItem->HasProperty("item_start") &&
320 (si->m_fileItem->GetProperty("item_start").asInteger() == si->m_fileItem->GetStartOffset()))
322 // Start stream at offset from cuesheet
323 si->m_startOffset = si->m_fileItem->GetStartOffset();
324 starttime = 0; // No resume point
327 if (!si->m_decoder.Create(file, si->m_startOffset))
329 CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
331 // advance playlist
332 AdvancePlaylistOnError(*si->m_fileItem);
333 m_callback.OnQueueNextItem();
335 delete si;
336 return false;
339 /* decode until there is data-available */
340 si->m_decoder.Start();
341 while (si->m_decoder.GetDataSize(true) == 0)
343 int status = si->m_decoder.GetStatus();
344 if (status == STATUS_ENDED ||
345 status == STATUS_NO_FILE ||
346 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
348 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
350 si->m_decoder.Destroy();
351 // advance playlist
352 AdvancePlaylistOnError(*si->m_fileItem);
353 m_callback.OnQueueNextItem();
354 delete si;
355 return false;
358 /* yield our time so that the main PAP thread doesn't stall */
359 CThread::Sleep(1ms);
362 // set m_upcomingCrossfadeMS depending on type of file and user settings
363 UpdateCrossfadeTime(*si->m_fileItem);
365 /* init the streaminfo struct */
366 si->m_audioFormat = si->m_decoder.GetFormat();
367 // si->m_startOffset already initialized
368 si->m_endOffset = file.GetEndOffset();
369 si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_audioFormat.m_dataFormat) >> 3;
370 si->m_bytesPerFrame = si->m_bytesPerSample * si->m_audioFormat.m_channelLayout.Count();
371 si->m_started = false;
372 si->m_finishing = false;
373 si->m_framesSent = 0;
374 si->m_seekNextAtFrame = 0;
375 si->m_seekFrame = -1;
376 si->m_stream = NULL;
377 si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
378 si->m_fadeOutTriggered = false;
379 si->m_isSlaved = false;
381 si->m_decoderTotal = si->m_decoder.TotalTime();
382 int64_t streamTotalTime = si->m_decoderTotal;
383 if (si->m_endOffset)
384 streamTotalTime = si->m_endOffset - si->m_startOffset;
386 // Seek to a resume point
387 if (si->m_fileItem->HasProperty("StartPercent") &&
388 (si->m_fileItem->GetProperty("StartPercent").asDouble() > 0) &&
389 (si->m_fileItem->GetProperty("StartPercent").asDouble() <= 100))
391 si->m_seekFrame =
392 si->m_audioFormat.m_sampleRate *
393 CUtil::ConvertMilliSecsToSecs(static_cast<int>(+(static_cast<double>(
394 streamTotalTime * (si->m_fileItem->GetProperty("StartPercent").asDouble() / 100.0)))));
396 else if (starttime > 0)
397 si->m_seekFrame = si->m_audioFormat.m_sampleRate * starttime;
398 else if (si->m_fileItem->HasProperty("audiobook_bookmark"))
399 si->m_seekFrame = si->m_audioFormat.m_sampleRate *
400 CUtil::ConvertMilliSecsToSecs(
401 si->m_fileItem->GetProperty("audiobook_bookmark").asInteger());
403 si->m_prepareNextAtFrame = 0;
404 // cd drives don't really like it to be crossfaded or prepared
405 if (!MUSIC::IsCDDA(file))
407 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
408 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f);
411 if (m_currentStream && ((m_currentStream->m_audioFormat.m_dataFormat == AE_FMT_RAW) || (si->m_audioFormat.m_dataFormat == AE_FMT_RAW)))
413 m_currentStream->m_prepareTriggered = false;
414 m_currentStream->m_waitOnDrain = true;
415 m_currentStream->m_prepareNextAtFrame = 0;
416 si->m_decoder.Destroy();
417 delete si;
418 return false;
421 si->m_prepareTriggered = false;
422 si->m_playNextAtFrame = 0;
423 si->m_playNextTriggered = false;
424 si->m_waitOnDrain = false;
426 if (!PrepareStream(si))
428 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
430 si->m_decoder.Destroy();
431 // advance playlist
432 AdvancePlaylistOnError(*si->m_fileItem);
433 m_callback.OnQueueNextItem();
434 delete si;
435 return false;
438 /* add the stream to the list */
439 std::unique_lock<CCriticalSection> lock(m_streamsLock);
440 m_streams.push_back(si);
441 //update the current stream to start playing the next track at the correct frame.
442 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
444 return true;
447 void PAPlayer::UpdateStreamInfoPlayNextAtFrame(StreamInfo *si, unsigned int crossFadingTime)
449 // if no crossfading or cue sheet, wait for eof
450 if (si && (crossFadingTime || si->m_endOffset))
452 int64_t streamTotalTime = si->m_decoder.TotalTime();
453 if (si->m_endOffset)
454 streamTotalTime = si->m_endOffset - si->m_startOffset;
455 if (streamTotalTime < crossFadingTime)
456 si->m_playNextAtFrame = (int)((streamTotalTime / 2) * si->m_audioFormat.m_sampleRate / 1000.0f);
457 else
458 si->m_playNextAtFrame = (int)((streamTotalTime - crossFadingTime) * si->m_audioFormat.m_sampleRate / 1000.0f);
462 inline bool PAPlayer::PrepareStream(StreamInfo *si)
464 /* if we have a stream we are already prepared */
465 if (si->m_stream)
466 return true;
468 /* get a paused stream */
469 AEAudioFormat format = si->m_audioFormat;
470 si->m_stream = CServiceBroker::GetActiveAE()->MakeStream(
471 format,
472 AESTREAM_PAUSED
475 if (!si->m_stream)
477 CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
478 return false;
481 si->m_stream->SetVolume(si->m_volume);
482 float peak = 1.0;
483 float gain = si->m_decoder.GetReplayGain(peak);
484 if (peak * gain <= 1.0f)
485 // No clipping protection needed
486 si->m_stream->SetReplayGain(gain);
487 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING))
488 // Normalise volume reducing replaygain to avoid needing clipping protection, plays file at lower level
489 si->m_stream->SetReplayGain(1.0f / fabs(peak));
490 else
491 // Clipping protection (when enabled in AE) by audio limiting, applied just where needed
492 si->m_stream->SetAmplification(gain);
494 /* if its not the first stream and crossfade is not enabled */
495 if (m_currentStream && m_currentStream != si && !m_upcomingCrossfadeMS)
497 /* slave the stream for gapless */
498 si->m_isSlaved = true;
499 m_currentStream->m_stream->RegisterSlave(si->m_stream.get());
502 /* fill the stream's buffer */
503 while(si->m_stream->IsBuffering())
505 int status = si->m_decoder.GetStatus();
506 if (status == STATUS_ENDED ||
507 status == STATUS_NO_FILE ||
508 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
510 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
511 break;
514 if (!QueueData(si))
515 break;
517 /* yield our time so that the main PAP thread doesn't stall */
518 CThread::Sleep(1ms);
521 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
523 return true;
526 bool PAPlayer::CloseFile(bool reopen)
528 if (reopen)
529 CServiceBroker::GetActiveAE()->KeepConfiguration(3000);
531 if (!m_isPaused)
532 SoftStop(true, true);
533 CloseAllStreams(false);
535 /* wait for the thread to terminate */
536 StopThread(true);//true - wait for end of thread
538 // wait for any pending jobs to complete
540 std::unique_lock<CCriticalSection> lock(m_streamsLock);
541 while (m_jobCounter > 0)
543 lock.unlock();
544 m_jobEvent.Wait(100ms);
545 lock.lock();
548 CServiceBroker::GetDataCacheCore().Reset();
549 return true;
552 void PAPlayer::Process()
554 if (!m_startEvent.Wait(100ms))
556 CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
557 return;
560 CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");
561 while(m_isPlaying && !m_bStop)
563 /* this needs to happen outside of any locks to prevent deadlocks */
564 if (m_signalSpeedChange)
566 m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
567 m_signalSpeedChange = false;
570 double freeBufferTime = 0.0;
571 ProcessStreams(freeBufferTime);
573 // if none of our streams wants at least 10ms of data, we sleep
574 if (freeBufferTime < 0.01)
576 CThread::Sleep(10ms);
579 if (m_newForcedPlayerTime != -1)
581 if (SetTimeInternal(m_newForcedPlayerTime))
583 m_newForcedPlayerTime = -1;
587 if (m_newForcedTotalTime != -1)
589 if (SetTotalTimeInternal(m_newForcedTotalTime))
591 m_newForcedTotalTime = -1;
595 GetTimeInternal(); //update for GUI
597 m_isPlaying = false;
600 inline void PAPlayer::ProcessStreams(double &freeBufferTime)
602 std::unique_lock<CCriticalSection> sharedLock(m_streamsLock);
603 if (m_isFinished && m_streams.empty() && m_finishing.empty())
605 m_isPlaying = false;
606 freeBufferTime = 1.0;
607 return;
610 /* destroy any drained streams */
611 for (auto itt = m_finishing.begin(); itt != m_finishing.end();)
613 StreamInfo* si = *itt;
614 if (si->m_stream->IsDrained())
616 itt = m_finishing.erase(itt);
617 CloseFileCB(*si);
618 delete si;
619 CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
621 else
622 ++itt;
625 sharedLock.unlock();
626 std::unique_lock<CCriticalSection> lock(m_streamsLock);
628 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
630 StreamInfo* si = *itt;
631 if (!m_currentStream && !si->m_started)
633 m_currentStream = si;
634 UpdateGUIData(si); //update for GUI
636 /* if the stream is finishing */
637 if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, freeBufferTime))
639 if (!si->m_prepareTriggered)
641 if (si->m_waitOnDrain)
643 si->m_stream->Drain(true);
644 si->m_waitOnDrain = false;
646 si->m_prepareTriggered = true;
647 m_callback.OnQueueNextItem();
650 /* remove the stream */
651 itt = m_streams.erase(itt);
652 /* if its the current stream */
653 if (si == m_currentStream)
655 /* if it was the last stream */
656 if (itt == m_streams.end())
658 /* if it didn't trigger the next queue item */
659 if (!si->m_prepareTriggered)
661 if (si->m_waitOnDrain)
663 si->m_stream->Drain(true);
664 si->m_waitOnDrain = false;
666 m_callback.OnQueueNextItem();
667 si->m_prepareTriggered = true;
669 m_currentStream = NULL;
671 else
673 m_currentStream = *itt;
674 UpdateGUIData(*itt); //update for GUI
678 /* unregister the audio callback */
679 si->m_stream->UnRegisterAudioCallback();
680 si->m_decoder.Destroy();
681 si->m_stream->Drain(false);
682 m_finishing.push_back(si);
683 return;
686 if (!si->m_started)
687 continue;
689 // is it time to prepare the next stream?
690 if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
692 si->m_prepareTriggered = true;
693 m_callback.OnQueueNextItem();
696 // it is time to start playing the next stream?
697 if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && !si->m_nextFileItem && si->m_framesSent >= si->m_playNextAtFrame)
699 if (!si->m_prepareTriggered)
701 si->m_prepareTriggered = true;
702 m_callback.OnQueueNextItem();
705 if (!m_isFinished)
707 if (m_upcomingCrossfadeMS)
709 si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS);
710 si->m_fadeOutTriggered = true;
712 m_currentStream = NULL;
714 /* unregister the audio callback */
715 si->m_stream->UnRegisterAudioCallback();
718 si->m_playNextTriggered = true;
723 inline bool PAPlayer::ProcessStream(StreamInfo *si, double &freeBufferTime)
725 /* if playback needs to start on this stream, do it */
726 if (si == m_currentStream && !si->m_started)
728 si->m_started = true;
729 si->m_stream->RegisterAudioCallback(m_audioCallback);
730 if (!si->m_isSlaved)
731 si->m_stream->Resume();
732 si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS);
733 if (m_signalStarted)
734 m_callback.OnPlayBackStarted(*si->m_fileItem);
735 m_signalStarted = true;
736 if (m_fullScreen)
738 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SWITCHTOFULLSCREEN);
739 m_fullScreen = false;
741 m_callback.OnAVStarted(*si->m_fileItem);
744 /* if we have not started yet and the stream has been primed */
745 unsigned int space = si->m_stream->GetSpace();
746 if (!si->m_started && !space)
747 return true;
749 if (!m_playbackSpeed)
750 return true;
752 /* see if it is time yet to FF/RW or a direct seek */
753 if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
755 int64_t time = (int64_t)0;
756 /* if its a direct seek */
757 if (si->m_seekFrame > -1)
759 time = (int64_t)((float)si->m_seekFrame / (float)si->m_audioFormat.m_sampleRate * 1000.0f);
760 si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_audioFormat.m_sampleRate) / 1000.0f);
761 si->m_seekFrame = -1;
762 m_playerGUIData.m_time = time; //update for GUI
763 si->m_seekNextAtFrame = 0;
764 CDataCacheCore::GetInstance().SetPlayTimes(0, time, 0, m_playerGUIData.m_totalTime);
766 /* if its FF/RW */
767 else
769 si->m_framesSent += si->m_audioFormat.m_sampleRate * (m_playbackSpeed - 1);
770 si->m_seekNextAtFrame = si->m_framesSent + si->m_audioFormat.m_sampleRate / 2;
771 time = (int64_t)(((float)si->m_framesSent / (float)si->m_audioFormat.m_sampleRate * 1000.0f) + (float)si->m_startOffset);
774 /* if we are seeking back before the start of the track start normal playback */
775 if (time < si->m_startOffset || si->m_framesSent < 0)
777 time = si->m_startOffset;
778 si->m_framesSent = 0;
779 si->m_seekNextAtFrame = 0;
780 SetSpeed(1);
783 si->m_decoder.Seek(time);
786 int status = si->m_decoder.GetStatus();
787 if (status == STATUS_ENDED ||
788 status == STATUS_NO_FILE ||
789 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR ||
790 ((si->m_endOffset) && (si->m_framesSent / si->m_audioFormat.m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000)))
792 if (si == m_currentStream && si->m_nextFileItem)
794 CloseFileCB(*si);
796 // update current stream with info of next track
797 si->m_startOffset = si->m_nextFileItem->GetStartOffset();
798 if (si->m_nextFileItem->GetEndOffset())
799 si->m_endOffset = si->m_nextFileItem->GetEndOffset();
800 else
801 si->m_endOffset = 0;
802 si->m_framesSent = 0;
804 *si->m_fileItem = *si->m_nextFileItem;
805 si->m_nextFileItem.reset();
807 int64_t streamTotalTime = si->m_decoder.TotalTime() - si->m_startOffset;
808 if (si->m_endOffset)
809 streamTotalTime = si->m_endOffset - si->m_startOffset;
811 // calculate time when to prepare next stream
812 si->m_prepareNextAtFrame = 0;
813 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
814 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f);
816 si->m_prepareTriggered = false;
817 si->m_playNextAtFrame = 0;
818 si->m_playNextTriggered = false;
819 si->m_seekNextAtFrame = 0;
821 //update the current stream to start playing the next track at the correct frame.
822 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
824 UpdateGUIData(si);
825 if (m_signalStarted)
826 m_callback.OnPlayBackStarted(*si->m_fileItem);
827 m_signalStarted = true;
828 m_callback.OnAVStarted(*si->m_fileItem);
830 else
832 CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
833 return false;
837 if (!QueueData(si))
838 return false;
840 /* update free buffer time if we are running */
841 if (si->m_started)
843 if (si->m_stream->IsBuffering())
844 freeBufferTime = 1.0;
845 else
847 double free_space;
848 if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW)
849 free_space = (double)(si->m_stream->GetSpace() / si->m_bytesPerSample) / si->m_audioFormat.m_sampleRate;
850 else
852 free_space = static_cast<double>(si->m_stream->GetSpace()) *
853 si->m_audioFormat.m_streamInfo.GetDuration() / 1000;
856 freeBufferTime = std::max(freeBufferTime , free_space);
860 return true;
863 bool PAPlayer::QueueData(StreamInfo *si)
865 unsigned int space = si->m_stream->GetSpace();
867 if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW)
869 unsigned int samples = std::min(si->m_decoder.GetDataSize(false), space / si->m_bytesPerSample);
870 if (!samples)
871 return true;
873 // we want complete frames
874 samples -= samples % si->m_audioFormat.m_channelLayout.Count();
876 uint8_t* data = (uint8_t*)si->m_decoder.GetData(samples);
877 if (!data)
879 CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
880 return false;
883 unsigned int frames = samples/si->m_audioFormat.m_channelLayout.Count();
884 unsigned int added = si->m_stream->AddData(&data, 0, frames, nullptr);
885 si->m_framesSent += added;
887 else
889 if (!space)
890 return true;
892 int size;
893 uint8_t *data = si->m_decoder.GetRawData(size);
894 if (data && size)
896 int added = si->m_stream->AddData(&data, 0, size, nullptr);
897 if (added != size)
899 CLog::Log(LOGERROR, "PAPlayer::QueueData - unknown error");
900 return false;
903 si->m_framesSent += si->m_audioFormat.m_streamInfo.GetDuration() / 1000 *
904 si->m_audioFormat.m_streamInfo.m_sampleRate;
908 const ICodec* codec = si->m_decoder.GetCodec();
909 m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0; //update for GUI
911 return true;
914 void PAPlayer::OnExit()
916 //@todo signal OnPlayBackError if there was an error on last stream
917 if (m_isFinished && !m_bStop)
918 m_callback.OnPlayBackEnded();
919 else
920 m_callback.OnPlayBackStopped();
923 void PAPlayer::OnNothingToQueueNotify()
925 m_isFinished = true;
928 bool PAPlayer::IsPlaying() const
930 return m_isPlaying;
933 void PAPlayer::Pause()
935 if (m_isPaused)
937 SetSpeed(1);
939 else
941 SetSpeed(0);
945 void PAPlayer::SetVolume(float volume)
950 void PAPlayer::SetDynamicRangeCompression(long drc)
955 void PAPlayer::SetSpeed(float speed)
957 m_playbackSpeed = static_cast<int>(speed);
958 CDataCacheCore::GetInstance().SetSpeed(1.0, speed);
959 if (m_playbackSpeed != 0 && m_isPaused)
961 m_isPaused = false;
962 SoftStart();
963 m_callback.OnPlayBackResumed();
965 else if (m_playbackSpeed == 0 && !m_isPaused)
967 m_isPaused = true;
968 SoftStop(true, false);
969 m_callback.OnPlayBackPaused();
971 m_signalSpeedChange = true;
974 int64_t PAPlayer::GetTimeInternal()
976 std::unique_lock<CCriticalSection> lock(m_streamsLock);
977 if (!m_currentStream)
978 return 0;
980 double time = ((double)m_currentStream->m_framesSent / (double)m_currentStream->m_audioFormat.m_sampleRate);
981 if (m_currentStream->m_stream)
982 time -= m_currentStream->m_stream->GetDelay();
983 time = time * 1000.0;
985 m_playerGUIData.m_time = (int64_t)time; //update for GUI
986 CDataCacheCore::GetInstance().SetPlayTimes(0, time, 0, m_playerGUIData.m_totalTime);
988 return (int64_t)time;
991 bool PAPlayer::SetTotalTimeInternal(int64_t time)
993 std::unique_lock<CCriticalSection> lock(m_streamsLock);
994 if (!m_currentStream)
996 return false;
999 m_currentStream->m_decoder.SetTotalTime(time);
1000 UpdateGUIData(m_currentStream);
1002 return true;
1005 bool PAPlayer::SetTimeInternal(int64_t time)
1007 std::unique_lock<CCriticalSection> lock(m_streamsLock);
1008 if (!m_currentStream)
1009 return false;
1011 m_currentStream->m_framesSent = time / 1000 * m_currentStream->m_audioFormat.m_sampleRate;
1013 if (m_currentStream->m_stream)
1014 m_currentStream->m_framesSent += m_currentStream->m_stream->GetDelay() * m_currentStream->m_audioFormat.m_sampleRate;
1016 return true;
1019 void PAPlayer::SetTime(int64_t time)
1021 m_newForcedPlayerTime = time;
1024 int64_t PAPlayer::GetTotalTime64()
1026 std::unique_lock<CCriticalSection> lock(m_streamsLock);
1027 if (!m_currentStream)
1028 return 0;
1030 int64_t total = m_currentStream->m_decoder.TotalTime();
1031 if (m_currentStream->m_endOffset)
1032 total = m_currentStream->m_endOffset;
1033 total -= m_currentStream->m_startOffset;
1034 return total;
1037 void PAPlayer::SetTotalTime(int64_t time)
1039 m_newForcedTotalTime = time;
1042 int PAPlayer::GetCacheLevel() const
1044 return m_playerGUIData.m_cacheLevel;
1047 void PAPlayer::GetAudioStreamInfo(int index, AudioStreamInfo& info) const
1049 info.bitrate = m_playerGUIData.m_audioBitrate;
1050 info.channels = m_playerGUIData.m_channelCount;
1051 info.codecName = m_playerGUIData.m_codec;
1052 info.samplerate = m_playerGUIData.m_sampleRate;
1053 info.bitspersample = m_playerGUIData.m_bitsPerSample;
1056 bool PAPlayer::CanSeek() const
1058 return m_playerGUIData.m_canSeek;
1061 void PAPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
1063 if (!CanSeek()) return;
1065 long long seek;
1066 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
1067 if (advancedSettings->m_musicUseTimeSeeking && m_playerGUIData.m_totalTime > 2 * advancedSettings->m_musicTimeSeekForwardBig)
1069 if (bLargeStep)
1070 seek = bPlus ? advancedSettings->m_musicTimeSeekForwardBig : advancedSettings->m_musicTimeSeekBackwardBig;
1071 else
1072 seek = bPlus ? advancedSettings->m_musicTimeSeekForward : advancedSettings->m_musicTimeSeekBackward;
1073 seek *= 1000;
1074 seek += m_playerGUIData.m_time;
1076 else
1078 float percent;
1079 if (bLargeStep)
1080 percent = bPlus ? static_cast<float>(advancedSettings->m_musicPercentSeekForwardBig) : static_cast<float>(advancedSettings->m_musicPercentSeekBackwardBig);
1081 else
1082 percent = bPlus ? static_cast<float>(advancedSettings->m_musicPercentSeekForward) : static_cast<float>(advancedSettings->m_musicPercentSeekBackward);
1083 seek = static_cast<long long>(GetTotalTime64() * (GetPercentage() + percent) / 100);
1086 SeekTime(seek);
1089 void PAPlayer::SeekTime(int64_t iTime /*=0*/)
1091 if (!CanSeek()) return;
1093 std::unique_lock<CCriticalSection> lock(m_streamsLock);
1094 if (!m_currentStream)
1095 return;
1097 int64_t seekOffset = iTime - GetTimeInternal();
1099 if (m_playbackSpeed != 1)
1100 SetSpeed(1);
1102 m_currentStream->m_seekFrame = (int)((float)m_currentStream->m_audioFormat.m_sampleRate * ((float)iTime + (float)m_currentStream->m_startOffset) / 1000.0f);
1103 m_callback.OnPlayBackSeek(iTime, seekOffset);
1106 void PAPlayer::SeekPercentage(float fPercent /*=0*/)
1108 if (fPercent < 0.0f ) fPercent = 0.0f;
1109 if (fPercent > 100.0f) fPercent = 100.0f;
1110 SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
1113 float PAPlayer::GetPercentage()
1115 if (m_playerGUIData.m_totalTime > 0)
1116 return m_playerGUIData.m_time * 100.0f / m_playerGUIData.m_totalTime;
1118 return 0.0f;
1121 void PAPlayer::UpdateGUIData(StreamInfo *si)
1123 /* Store data need by external threads in member
1124 * structure to prevent locking conflicts when
1125 * data required by GUI and main application
1127 std::unique_lock<CCriticalSection> lock(m_streamsLock);
1129 m_playerGUIData.m_sampleRate = si->m_audioFormat.m_sampleRate;
1130 m_playerGUIData.m_channelCount = si->m_audioFormat.m_channelLayout.Count();
1131 m_playerGUIData.m_canSeek = si->m_decoder.CanSeek();
1133 const ICodec* codec = si->m_decoder.GetCodec();
1135 m_playerGUIData.m_audioBitrate = codec ? codec->m_bitRate : 0;
1136 strncpy(m_playerGUIData.m_codec,codec ? codec->m_CodecName.c_str() : "",20);
1137 m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0;
1138 m_playerGUIData.m_bitsPerSample = (codec && codec->m_bitsPerCodedSample) ? codec->m_bitsPerCodedSample : si->m_bytesPerSample << 3;
1140 int64_t total = si->m_decoder.TotalTime();
1141 if (si->m_endOffset)
1142 total = m_currentStream->m_endOffset;
1143 total -= m_currentStream->m_startOffset;
1144 m_playerGUIData.m_totalTime = total;
1146 CServiceBroker::GetDataCacheCore().SignalAudioInfoChange();
1149 void PAPlayer::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1151 std::unique_lock<CCriticalSection> lock(m_streamsLock);
1152 m_jobCounter--;
1153 m_jobEvent.Set();
1156 void PAPlayer::CloseFileCB(StreamInfo &si)
1158 IPlayerCallback *cb = &m_callback;
1159 CFileItem fileItem(*si.m_fileItem);
1160 CBookmark bookmark;
1161 double total = si.m_decoderTotal;
1162 if (si.m_endOffset)
1163 total = si.m_endOffset;
1164 total -= si.m_startOffset;
1165 bookmark.totalTimeInSeconds = total / 1000;
1166 bookmark.timeInSeconds = (static_cast<double>(si.m_framesSent) /
1167 static_cast<double>(si.m_audioFormat.m_sampleRate));
1168 bookmark.timeInSeconds -= si.m_stream->GetDelay();
1169 bookmark.player = m_name;
1170 bookmark.playerState = GetPlayerState();
1171 CServiceBroker::GetJobManager()->Submit([=]() { cb->OnPlayerCloseFile(fileItem, bookmark); },
1172 CJob::PRIORITY_NORMAL);
1175 void PAPlayer::AdvancePlaylistOnError(CFileItem &fileItem)
1177 if (m_signalStarted)
1178 m_callback.OnPlayBackStarted(fileItem);
1179 m_signalStarted = true;
1180 m_callback.OnAVStarted(fileItem);