[FileItem][Cleanup] Add getter/setter for start/end offset
[xbmc.git] / xbmc / cores / paplayer / PAPlayer.cpp
blobb72712422650c7796b6b43213f5ed7863c30b782
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 "CodecFactory.h"
12 #include "ServiceBroker.h"
13 #include "Util.h"
14 #include "cores/AudioEngine/Interfaces/AE.h"
15 #include "cores/AudioEngine/Interfaces/AEStream.h"
16 #include "cores/AudioEngine/Utils/AEStreamData.h"
17 #include "cores/AudioEngine/Utils/AEUtil.h"
18 #include "cores/DataCacheCore.h"
19 #include "cores/VideoPlayer/Process/ProcessInfo.h"
20 #include "messaging/ApplicationMessenger.h"
21 #include "music/tags/MusicInfoTag.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "threads/SystemClock.h"
26 #include "utils/JobManager.h"
27 #include "utils/log.h"
28 #include "video/Bookmark.h"
30 #include <mutex>
32 using namespace std::chrono_literals;
34 #define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
35 #define FAST_XFADE_TIME 80 /* 80 milliseconds */
36 #define MAX_SKIP_XFADE_TIME 2000 /* max 2 seconds crossfade on track skip */
38 // PAP: Psycho-acoustic Audio Player
39 // Supporting all open audio codec standards.
40 // First one being nullsoft's nsv audio decoder format
42 PAPlayer::PAPlayer(IPlayerCallback& callback) :
43 IPlayer(callback),
44 CThread("PAPlayer"),
45 m_signalSpeedChange(false),
46 m_playbackSpeed(1 ),
47 m_isPlaying(false),
48 m_isPaused(false),
49 m_isFinished(false),
50 m_defaultCrossfadeMS (0),
51 m_upcomingCrossfadeMS(0),
52 m_audioCallback(NULL ),
53 m_jobCounter(0),
54 m_newForcedPlayerTime(-1),
55 m_newForcedTotalTime (-1)
57 memset(&m_playerGUIData, 0, sizeof(m_playerGUIData));
58 m_processInfo.reset(CProcessInfo::CreateInstance());
59 m_processInfo->SetDataCache(&CServiceBroker::GetDataCacheCore());
62 PAPlayer::~PAPlayer()
64 CloseFile();
67 void PAPlayer::SoftStart(bool wait/* = false */)
69 std::unique_lock<CCriticalSection> lock(m_streamsLock);
70 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
72 StreamInfo* si = *itt;
73 if (si->m_fadeOutTriggered)
74 continue;
76 si->m_stream->Resume();
77 si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
80 if (wait)
82 /* wait for them to fade in */
83 lock.unlock();
84 CThread::Sleep(std::chrono::milliseconds(FAST_XFADE_TIME));
85 lock.lock();
87 /* be sure they have faded in */
88 while(wait)
90 wait = false;
91 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
93 StreamInfo* si = *itt;
94 if (si->m_stream->IsFading())
96 lock.unlock();
97 wait = true;
98 CThread::Sleep(1ms);
99 lock.lock();
100 break;
107 void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
109 /* fade all the streams out fast for a nice soft stop */
110 std::unique_lock<CCriticalSection> lock(m_streamsLock);
111 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
113 StreamInfo* si = *itt;
114 if (si->m_stream)
115 si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
117 if (close)
119 si->m_prepareTriggered = true;
120 si->m_playNextTriggered = true;
121 si->m_fadeOutTriggered = true;
125 /* if we are going to wait for them to finish fading */
126 if(wait)
128 // fail safe timer, do not wait longer than 1000ms
129 XbmcThreads::EndTime<> timer(1000ms);
131 /* wait for them to fade out */
132 lock.unlock();
133 CThread::Sleep(std::chrono::milliseconds(FAST_XFADE_TIME));
134 lock.lock();
136 /* be sure they have faded out */
137 while(wait && !CServiceBroker::GetActiveAE()->IsSuspended() && !timer.IsTimePast())
139 wait = false;
140 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
142 StreamInfo* si = *itt;
143 if (si->m_stream && si->m_stream->IsFading())
145 lock.unlock();
146 wait = true;
147 CThread::Sleep(1ms);
148 lock.lock();
149 break;
154 /* if we are not closing the streams, pause them */
155 if (!close)
157 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
159 StreamInfo* si = *itt;
160 si->m_stream->Pause();
166 void PAPlayer::CloseAllStreams(bool fade/* = true */)
168 if (!fade)
170 std::unique_lock<CCriticalSection> lock(m_streamsLock);
171 while (!m_streams.empty())
173 StreamInfo* si = m_streams.front();
174 m_streams.pop_front();
176 if (si->m_stream)
178 CloseFileCB(*si);
179 si->m_stream.reset();
182 si->m_decoder.Destroy();
183 delete si;
186 while (!m_finishing.empty())
188 StreamInfo* si = m_finishing.front();
189 m_finishing.pop_front();
191 if (si->m_stream)
193 CloseFileCB(*si);
194 si->m_stream.reset();
197 si->m_decoder.Destroy();
198 delete si;
200 m_currentStream = nullptr;
202 else
204 SoftStop(false, true);
205 std::unique_lock<CCriticalSection> lock(m_streamsLock);
206 m_currentStream = NULL;
210 bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
212 m_defaultCrossfadeMS = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MUSICPLAYER_CROSSFADE) * 1000;
213 m_fullScreen = options.fullscreen;
215 if (m_streams.size() > 1 || !m_defaultCrossfadeMS || m_isPaused)
217 CloseAllStreams(!m_isPaused);
218 StopThread();
219 m_isPaused = false; // Make sure to reset the pause state
223 std::unique_lock<CCriticalSection> lock(m_streamsLock);
224 m_jobCounter++;
226 CServiceBroker::GetJobManager()->Submit([=]() { QueueNextFileEx(file, false); }, this,
227 CJob::PRIORITY_NORMAL);
229 std::unique_lock<CCriticalSection> lock(m_streamsLock);
230 if (m_streams.size() == 2)
232 //do a short crossfade on trackskip, set to max 2 seconds for these prev/next transitions
233 m_upcomingCrossfadeMS = std::min(m_defaultCrossfadeMS, (unsigned int)MAX_SKIP_XFADE_TIME);
235 //start transition to next track
236 StreamInfo* si = m_streams.front();
237 si->m_playNextAtFrame = si->m_framesSent; //start next track at current frame
238 si->m_prepareTriggered = true; //next track is ready to go
240 lock.unlock();
242 if (!IsRunning())
243 Create();
245 /* trigger playback start */
246 m_isPlaying = true;
247 m_startEvent.Set();
249 // OnPlayBackStarted to be made only once. Callback processing may be slower than player process
250 // so clear signal flag first otherwise async stream processing could also make callback
251 m_signalStarted = false;
252 m_callback.OnPlayBackStarted(file);
254 return true;
257 void PAPlayer::UpdateCrossfadeTime(const CFileItem& file)
259 // we explicitly disable crossfading for audio cds
260 if (file.IsCDDA())
261 m_upcomingCrossfadeMS = 0;
262 else
263 m_upcomingCrossfadeMS = m_defaultCrossfadeMS = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MUSICPLAYER_CROSSFADE) * 1000;
265 if (m_upcomingCrossfadeMS)
267 if (!m_currentStream ||
268 (file.HasMusicInfoTag() && !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_CROSSFADEALBUMTRACKS) &&
269 m_currentStream->m_fileItem.HasMusicInfoTag() &&
270 (m_currentStream->m_fileItem.GetMusicInfoTag()->GetAlbum() != "") &&
271 (m_currentStream->m_fileItem.GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
272 (m_currentStream->m_fileItem.GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
273 (m_currentStream->m_fileItem.GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)))
275 //do not crossfade when playing consecutive albumtracks
276 m_upcomingCrossfadeMS = 0;
281 bool PAPlayer::QueueNextFile(const CFileItem &file)
284 std::unique_lock<CCriticalSection> lock(m_streamsLock);
285 m_jobCounter++;
287 CServiceBroker::GetJobManager()->Submit([this, file]() { QueueNextFileEx(file, true); }, this,
288 CJob::PRIORITY_NORMAL);
290 return true;
293 bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn)
295 if (m_currentStream)
297 // check if we advance a track of a CUE sheet
298 // if this is the case we don't need to open a new stream
299 std::string newURL = file.GetDynURL().GetFileName();
300 std::string oldURL = m_currentStream->m_fileItem.GetDynURL().GetFileName();
301 if (newURL.compare(oldURL) == 0 && file.GetStartOffset() &&
302 file.GetStartOffset() == m_currentStream->m_fileItem.GetEndOffset() && m_currentStream &&
303 m_currentStream->m_prepareTriggered)
305 m_currentStream->m_nextFileItem.reset(new CFileItem(file));
306 m_upcomingCrossfadeMS = 0;
307 return true;
309 m_currentStream->m_nextFileItem.reset();
312 StreamInfo *si = new StreamInfo();
313 si->m_fileItem = file;
315 // Start stream at zero offset
316 si->m_startOffset = 0;
317 //File item start offset defines where in song to resume
318 double starttime = CUtil::ConvertMilliSecsToSecs(si->m_fileItem.GetStartOffset());
320 // Music from cuesheet => "item_start" and offset match
321 // Start offset defines where this song starts in file of multiple songs
322 if (si->m_fileItem.HasProperty("item_start") &&
323 (si->m_fileItem.GetProperty("item_start").asInteger() == si->m_fileItem.GetStartOffset()))
325 // Start stream at offset from cuesheet
326 si->m_startOffset = si->m_fileItem.GetStartOffset();
327 starttime = 0; // No resume point
330 if (!si->m_decoder.Create(file, si->m_startOffset))
332 CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
334 // advance playlist
335 AdvancePlaylistOnError(si->m_fileItem);
336 m_callback.OnQueueNextItem();
338 delete si;
339 return false;
342 /* decode until there is data-available */
343 si->m_decoder.Start();
344 while (si->m_decoder.GetDataSize(true) == 0)
346 int status = si->m_decoder.GetStatus();
347 if (status == STATUS_ENDED ||
348 status == STATUS_NO_FILE ||
349 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
351 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
353 si->m_decoder.Destroy();
354 // advance playlist
355 AdvancePlaylistOnError(si->m_fileItem);
356 m_callback.OnQueueNextItem();
357 delete si;
358 return false;
361 /* yield our time so that the main PAP thread doesn't stall */
362 CThread::Sleep(1ms);
365 // set m_upcomingCrossfadeMS depending on type of file and user settings
366 UpdateCrossfadeTime(si->m_fileItem);
368 /* init the streaminfo struct */
369 si->m_audioFormat = si->m_decoder.GetFormat();
370 // si->m_startOffset already initialized
371 si->m_endOffset = file.GetEndOffset();
372 si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_audioFormat.m_dataFormat) >> 3;
373 si->m_bytesPerFrame = si->m_bytesPerSample * si->m_audioFormat.m_channelLayout.Count();
374 si->m_started = false;
375 si->m_finishing = false;
376 si->m_framesSent = 0;
377 si->m_seekNextAtFrame = 0;
378 si->m_seekFrame = -1;
379 si->m_stream = NULL;
380 si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
381 si->m_fadeOutTriggered = false;
382 si->m_isSlaved = false;
384 si->m_decoderTotal = si->m_decoder.TotalTime();
385 int64_t streamTotalTime = si->m_decoderTotal;
386 if (si->m_endOffset)
387 streamTotalTime = si->m_endOffset - si->m_startOffset;
389 // Seek to a resume point
390 if (si->m_fileItem.HasProperty("StartPercent") &&
391 (si->m_fileItem.GetProperty("StartPercent").asDouble() > 0) &&
392 (si->m_fileItem.GetProperty("StartPercent").asDouble() <= 100))
394 si->m_seekFrame =
395 si->m_audioFormat.m_sampleRate *
396 CUtil::ConvertMilliSecsToSecs(static_cast<int>(+(static_cast<double>(
397 streamTotalTime * (si->m_fileItem.GetProperty("StartPercent").asDouble() / 100.0)))));
399 else if (starttime > 0)
400 si->m_seekFrame = si->m_audioFormat.m_sampleRate * starttime;
401 else if (si->m_fileItem.HasProperty("audiobook_bookmark"))
402 si->m_seekFrame =
403 si->m_audioFormat.m_sampleRate *
404 CUtil::ConvertMilliSecsToSecs(si->m_fileItem.GetProperty("audiobook_bookmark").asInteger());
406 si->m_prepareNextAtFrame = 0;
407 // cd drives don't really like it to be crossfaded or prepared
408 if(!file.IsCDDA())
410 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
411 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f);
414 if (m_currentStream && ((m_currentStream->m_audioFormat.m_dataFormat == AE_FMT_RAW) || (si->m_audioFormat.m_dataFormat == AE_FMT_RAW)))
416 m_currentStream->m_prepareTriggered = false;
417 m_currentStream->m_waitOnDrain = true;
418 m_currentStream->m_prepareNextAtFrame = 0;
419 si->m_decoder.Destroy();
420 delete si;
421 return false;
424 si->m_prepareTriggered = false;
425 si->m_playNextAtFrame = 0;
426 si->m_playNextTriggered = false;
427 si->m_waitOnDrain = false;
429 if (!PrepareStream(si))
431 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
433 si->m_decoder.Destroy();
434 // advance playlist
435 AdvancePlaylistOnError(si->m_fileItem);
436 m_callback.OnQueueNextItem();
437 delete si;
438 return false;
441 /* add the stream to the list */
442 std::unique_lock<CCriticalSection> lock(m_streamsLock);
443 m_streams.push_back(si);
444 //update the current stream to start playing the next track at the correct frame.
445 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
447 return true;
450 void PAPlayer::UpdateStreamInfoPlayNextAtFrame(StreamInfo *si, unsigned int crossFadingTime)
452 // if no crossfading or cue sheet, wait for eof
453 if (si && (crossFadingTime || si->m_endOffset))
455 int64_t streamTotalTime = si->m_decoder.TotalTime();
456 if (si->m_endOffset)
457 streamTotalTime = si->m_endOffset - si->m_startOffset;
458 if (streamTotalTime < crossFadingTime)
459 si->m_playNextAtFrame = (int)((streamTotalTime / 2) * si->m_audioFormat.m_sampleRate / 1000.0f);
460 else
461 si->m_playNextAtFrame = (int)((streamTotalTime - crossFadingTime) * si->m_audioFormat.m_sampleRate / 1000.0f);
465 inline bool PAPlayer::PrepareStream(StreamInfo *si)
467 /* if we have a stream we are already prepared */
468 if (si->m_stream)
469 return true;
471 /* get a paused stream */
472 AEAudioFormat format = si->m_audioFormat;
473 si->m_stream = CServiceBroker::GetActiveAE()->MakeStream(
474 format,
475 AESTREAM_PAUSED
478 if (!si->m_stream)
480 CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
481 return false;
484 si->m_stream->SetVolume(si->m_volume);
485 float peak = 1.0;
486 float gain = si->m_decoder.GetReplayGain(peak);
487 if (peak * gain <= 1.0f)
488 // No clipping protection needed
489 si->m_stream->SetReplayGain(gain);
490 else if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING))
491 // Normalise volume reducing replaygain to avoid needing clipping protection, plays file at lower level
492 si->m_stream->SetReplayGain(1.0f / fabs(peak));
493 else
494 // Clipping protection (when enabled in AE) by audio limiting, applied just where needed
495 si->m_stream->SetAmplification(gain);
497 /* if its not the first stream and crossfade is not enabled */
498 if (m_currentStream && m_currentStream != si && !m_upcomingCrossfadeMS)
500 /* slave the stream for gapless */
501 si->m_isSlaved = true;
502 m_currentStream->m_stream->RegisterSlave(si->m_stream.get());
505 /* fill the stream's buffer */
506 while(si->m_stream->IsBuffering())
508 int status = si->m_decoder.GetStatus();
509 if (status == STATUS_ENDED ||
510 status == STATUS_NO_FILE ||
511 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
513 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
514 break;
517 if (!QueueData(si))
518 break;
520 /* yield our time so that the main PAP thread doesn't stall */
521 CThread::Sleep(1ms);
524 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
526 return true;
529 bool PAPlayer::CloseFile(bool reopen)
531 if (reopen)
532 CServiceBroker::GetActiveAE()->KeepConfiguration(3000);
534 if (!m_isPaused)
535 SoftStop(true, true);
536 CloseAllStreams(false);
538 /* wait for the thread to terminate */
539 StopThread(true);//true - wait for end of thread
541 // wait for any pending jobs to complete
543 std::unique_lock<CCriticalSection> lock(m_streamsLock);
544 while (m_jobCounter > 0)
546 lock.unlock();
547 m_jobEvent.Wait(100ms);
548 lock.lock();
552 return true;
555 void PAPlayer::Process()
557 if (!m_startEvent.Wait(100ms))
559 CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
560 return;
563 CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");
564 while(m_isPlaying && !m_bStop)
566 /* this needs to happen outside of any locks to prevent deadlocks */
567 if (m_signalSpeedChange)
569 m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
570 m_signalSpeedChange = false;
573 double freeBufferTime = 0.0;
574 ProcessStreams(freeBufferTime);
576 // if none of our streams wants at least 10ms of data, we sleep
577 if (freeBufferTime < 0.01)
579 CThread::Sleep(10ms);
582 if (m_newForcedPlayerTime != -1)
584 if (SetTimeInternal(m_newForcedPlayerTime))
586 m_newForcedPlayerTime = -1;
590 if (m_newForcedTotalTime != -1)
592 if (SetTotalTimeInternal(m_newForcedTotalTime))
594 m_newForcedTotalTime = -1;
598 GetTimeInternal(); //update for GUI
600 m_isPlaying = false;
603 inline void PAPlayer::ProcessStreams(double &freeBufferTime)
605 std::unique_lock<CCriticalSection> sharedLock(m_streamsLock);
606 if (m_isFinished && m_streams.empty() && m_finishing.empty())
608 m_isPlaying = false;
609 freeBufferTime = 1.0;
610 return;
613 /* destroy any drained streams */
614 for (auto itt = m_finishing.begin(); itt != m_finishing.end();)
616 StreamInfo* si = *itt;
617 if (si->m_stream->IsDrained())
619 itt = m_finishing.erase(itt);
620 CloseFileCB(*si);
621 delete si;
622 CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
624 else
625 ++itt;
628 sharedLock.unlock();
629 std::unique_lock<CCriticalSection> lock(m_streamsLock);
631 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
633 StreamInfo* si = *itt;
634 if (!m_currentStream && !si->m_started)
636 m_currentStream = si;
637 UpdateGUIData(si); //update for GUI
639 /* if the stream is finishing */
640 if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, freeBufferTime))
642 if (!si->m_prepareTriggered)
644 if (si->m_waitOnDrain)
646 si->m_stream->Drain(true);
647 si->m_waitOnDrain = false;
649 si->m_prepareTriggered = true;
650 m_callback.OnQueueNextItem();
653 /* remove the stream */
654 itt = m_streams.erase(itt);
655 /* if its the current stream */
656 if (si == m_currentStream)
658 /* if it was the last stream */
659 if (itt == m_streams.end())
661 /* if it didn't trigger the next queue item */
662 if (!si->m_prepareTriggered)
664 if (si->m_waitOnDrain)
666 si->m_stream->Drain(true);
667 si->m_waitOnDrain = false;
669 m_callback.OnQueueNextItem();
670 si->m_prepareTriggered = true;
672 m_currentStream = NULL;
674 else
676 m_currentStream = *itt;
677 UpdateGUIData(*itt); //update for GUI
681 /* unregister the audio callback */
682 si->m_stream->UnRegisterAudioCallback();
683 si->m_decoder.Destroy();
684 si->m_stream->Drain(false);
685 m_finishing.push_back(si);
686 return;
689 if (!si->m_started)
690 continue;
692 // is it time to prepare the next stream?
693 if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
695 si->m_prepareTriggered = true;
696 m_callback.OnQueueNextItem();
699 // it is time to start playing the next stream?
700 if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && !si->m_nextFileItem && si->m_framesSent >= si->m_playNextAtFrame)
702 if (!si->m_prepareTriggered)
704 si->m_prepareTriggered = true;
705 m_callback.OnQueueNextItem();
708 if (!m_isFinished)
710 if (m_upcomingCrossfadeMS)
712 si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS);
713 si->m_fadeOutTriggered = true;
715 m_currentStream = NULL;
717 /* unregister the audio callback */
718 si->m_stream->UnRegisterAudioCallback();
721 si->m_playNextTriggered = true;
726 inline bool PAPlayer::ProcessStream(StreamInfo *si, double &freeBufferTime)
728 /* if playback needs to start on this stream, do it */
729 if (si == m_currentStream && !si->m_started)
731 si->m_started = true;
732 si->m_stream->RegisterAudioCallback(m_audioCallback);
733 if (!si->m_isSlaved)
734 si->m_stream->Resume();
735 si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS);
736 if (m_signalStarted)
737 m_callback.OnPlayBackStarted(si->m_fileItem);
738 m_signalStarted = true;
739 if (m_fullScreen)
741 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SWITCHTOFULLSCREEN);
742 m_fullScreen = false;
744 m_callback.OnAVStarted(si->m_fileItem);
747 /* if we have not started yet and the stream has been primed */
748 unsigned int space = si->m_stream->GetSpace();
749 if (!si->m_started && !space)
750 return true;
752 if (!m_playbackSpeed)
753 return true;
755 /* see if it is time yet to FF/RW or a direct seek */
756 if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
758 int64_t time = (int64_t)0;
759 /* if its a direct seek */
760 if (si->m_seekFrame > -1)
762 time = (int64_t)((float)si->m_seekFrame / (float)si->m_audioFormat.m_sampleRate * 1000.0f);
763 si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_audioFormat.m_sampleRate) / 1000.0f);
764 si->m_seekFrame = -1;
765 m_playerGUIData.m_time = time; //update for GUI
766 si->m_seekNextAtFrame = 0;
767 CDataCacheCore::GetInstance().SetPlayTimes(0, time, 0, m_playerGUIData.m_totalTime);
769 /* if its FF/RW */
770 else
772 si->m_framesSent += si->m_audioFormat.m_sampleRate * (m_playbackSpeed - 1);
773 si->m_seekNextAtFrame = si->m_framesSent + si->m_audioFormat.m_sampleRate / 2;
774 time = (int64_t)(((float)si->m_framesSent / (float)si->m_audioFormat.m_sampleRate * 1000.0f) + (float)si->m_startOffset);
777 /* if we are seeking back before the start of the track start normal playback */
778 if (time < si->m_startOffset || si->m_framesSent < 0)
780 time = si->m_startOffset;
781 si->m_framesSent = 0;
782 si->m_seekNextAtFrame = 0;
783 SetSpeed(1);
786 si->m_decoder.Seek(time);
789 int status = si->m_decoder.GetStatus();
790 if (status == STATUS_ENDED ||
791 status == STATUS_NO_FILE ||
792 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR ||
793 ((si->m_endOffset) && (si->m_framesSent / si->m_audioFormat.m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000)))
795 if (si == m_currentStream && si->m_nextFileItem)
797 CloseFileCB(*si);
799 // update current stream with info of next track
800 si->m_startOffset = si->m_nextFileItem->GetStartOffset();
801 if (si->m_nextFileItem->GetEndOffset())
802 si->m_endOffset = si->m_nextFileItem->GetEndOffset();
803 else
804 si->m_endOffset = 0;
805 si->m_framesSent = 0;
807 si->m_fileItem = *si->m_nextFileItem;
808 si->m_nextFileItem.reset();
810 int64_t streamTotalTime = si->m_decoder.TotalTime() - si->m_startOffset;
811 if (si->m_endOffset)
812 streamTotalTime = si->m_endOffset - si->m_startOffset;
814 // calculate time when to prepare next stream
815 si->m_prepareNextAtFrame = 0;
816 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
817 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_audioFormat.m_sampleRate / 1000.0f);
819 si->m_prepareTriggered = false;
820 si->m_playNextAtFrame = 0;
821 si->m_playNextTriggered = false;
822 si->m_seekNextAtFrame = 0;
824 //update the current stream to start playing the next track at the correct frame.
825 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
827 UpdateGUIData(si);
828 if (m_signalStarted)
829 m_callback.OnPlayBackStarted(si->m_fileItem);
830 m_signalStarted = true;
831 m_callback.OnAVStarted(si->m_fileItem);
833 else
835 CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
836 return false;
840 if (!QueueData(si))
841 return false;
843 /* update free buffer time if we are running */
844 if (si->m_started)
846 if (si->m_stream->IsBuffering())
847 freeBufferTime = 1.0;
848 else
850 double free_space;
851 if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW)
852 free_space = (double)(si->m_stream->GetSpace() / si->m_bytesPerSample) / si->m_audioFormat.m_sampleRate;
853 else
854 free_space = (double)si->m_stream->GetSpace() *
855 si->m_audioFormat.m_streamInfo.GetDuration(true) / 1000;
857 freeBufferTime = std::max(freeBufferTime , free_space);
861 return true;
864 bool PAPlayer::QueueData(StreamInfo *si)
866 unsigned int space = si->m_stream->GetSpace();
868 if (si->m_audioFormat.m_dataFormat != AE_FMT_RAW)
870 unsigned int samples = std::min(si->m_decoder.GetDataSize(false), space / si->m_bytesPerSample);
871 if (!samples)
872 return true;
874 // we want complete frames
875 samples -= samples % si->m_audioFormat.m_channelLayout.Count();
877 uint8_t* data = (uint8_t*)si->m_decoder.GetData(samples);
878 if (!data)
880 CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
881 return false;
884 unsigned int frames = samples/si->m_audioFormat.m_channelLayout.Count();
885 unsigned int added = si->m_stream->AddData(&data, 0, frames, nullptr);
886 si->m_framesSent += added;
888 else
890 if (!space)
891 return true;
893 int size;
894 uint8_t *data = si->m_decoder.GetRawData(size);
895 if (data && size)
897 int added = si->m_stream->AddData(&data, 0, size, nullptr);
898 if (added != size)
900 CLog::Log(LOGERROR, "PAPlayer::QueueData - unknown error");
901 return false;
903 si->m_framesSent += si->m_audioFormat.m_streamInfo.GetDuration(true) / 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)
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()
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);