2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "CodecFactory.h"
24 #include "settings/AdvancedSettings.h"
25 #include "settings/Settings.h"
26 #include "music/tags/MusicInfoTag.h"
27 #include "utils/TimeUtils.h"
28 #include "utils/log.h"
29 #include "utils/MathUtils.h"
31 #include "threads/SingleLock.h"
32 #include "cores/AudioEngine/AEFactory.h"
33 #include "cores/AudioEngine/Utils/AEUtil.h"
34 #include "cores/AudioEngine/Interfaces/AEStream.h"
36 #define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
37 #define FAST_XFADE_TIME 80 /* 80 milliseconds */
38 #define MAX_SKIP_XFADE_TIME 2000 /* max 2 seconds crossfade on track skip */
40 CAEChannelInfo ICodec::GetChannelInfo()
42 return CAEUtil::GuessChLayout(m_Channels);
45 // PAP: Psycho-acoustic Audio Player
46 // Supporting all open audio codec standards.
47 // First one being nullsoft's nsv audio decoder format
49 PAPlayer::PAPlayer(IPlayerCallback& callback) :
52 m_signalSpeedChange (false),
57 m_defaultCrossfadeMS (0),
58 m_upcomingCrossfadeMS(0),
59 m_currentStream (NULL ),
60 m_audioCallback (NULL ),
61 m_FileItem (new CFileItem())
63 memset(&m_playerGUIData, 0, sizeof(m_playerGUIData));
72 bool PAPlayer::HandlesType(const CStdString &type)
74 ICodec* codec = CodecFactory::CreateCodec(type);
75 if (codec && codec->CanInit())
84 void PAPlayer::SoftStart(bool wait/* = false */)
86 CSharedLock lock(m_streamsLock);
87 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
89 StreamInfo* si = *itt;
90 if (si->m_fadeOutTriggered)
93 si->m_stream->Resume();
94 si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
99 /* wait for them to fade in */
101 Sleep(FAST_XFADE_TIME);
104 /* be sure they have faded in */
108 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
110 StreamInfo* si = *itt;
111 if (si->m_stream->IsFading())
124 void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
126 /* fade all the streams out fast for a nice soft stop */
127 CSharedLock lock(m_streamsLock);
128 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
130 StreamInfo* si = *itt;
132 si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
136 si->m_prepareTriggered = true;
137 si->m_playNextTriggered = true;
138 si->m_fadeOutTriggered = true;
142 /* if we are going to wait for them to finish fading */
145 /* wait for them to fade out */
147 Sleep(FAST_XFADE_TIME);
150 /* be sure they have faded out */
151 while(wait && !CAEFactory::IsSuspended())
154 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
156 StreamInfo* si = *itt;
157 if (si->m_stream && si->m_stream->IsFading())
168 /* if we are not closing the streams, pause them */
171 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
173 StreamInfo* si = *itt;
174 si->m_stream->Pause();
180 void PAPlayer::CloseAllStreams(bool fade/* = true */)
184 CExclusiveLock lock(m_streamsLock);
185 while(!m_streams.empty())
187 StreamInfo* si = m_streams.front();
188 m_streams.pop_front();
192 CAEFactory::FreeStream(si->m_stream);
196 si->m_decoder.Destroy();
200 while(!m_finishing.empty())
202 StreamInfo* si = m_finishing.front();
203 m_finishing.pop_front();
207 CAEFactory::FreeStream(si->m_stream);
211 si->m_decoder.Destroy();
214 m_currentStream = NULL;
218 SoftStop(false, true);
219 CExclusiveLock lock(m_streamsLock);
220 m_currentStream = NULL;
224 bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
226 m_defaultCrossfadeMS = CSettings::Get().GetInt("musicplayer.crossfade") * 1000;
228 if (m_streams.size() > 1 || !m_defaultCrossfadeMS || m_isPaused)
230 CloseAllStreams(!m_isPaused);
232 m_isPaused = false; // Make sure to reset the pause state
235 if (!QueueNextFileEx(file, false))
238 CSharedLock lock(m_streamsLock);
239 if (m_streams.size() == 2)
241 //do a short crossfade on trackskip, set to max 2 seconds for these prev/next transitions
242 m_upcomingCrossfadeMS = std::min(m_defaultCrossfadeMS, (unsigned int)MAX_SKIP_XFADE_TIME);
244 //start transition to next track
245 StreamInfo* si = m_streams.front();
246 si->m_playNextAtFrame = si->m_framesSent; //start next track at current frame
247 si->m_prepareTriggered = true; //next track is ready to go
254 /* trigger playback start */
260 void PAPlayer::UpdateCrossfadeTime(const CFileItem& file)
262 m_upcomingCrossfadeMS = m_defaultCrossfadeMS = CSettings::Get().GetInt("musicplayer.crossfade") * 1000;
263 if (m_upcomingCrossfadeMS)
265 if (m_streams.size() == 0 ||
267 file.HasMusicInfoTag() && !CSettings::Get().GetBool("musicplayer.crossfadealbumtracks") &&
268 m_FileItem->HasMusicInfoTag() &&
269 (m_FileItem->GetMusicInfoTag()->GetAlbum() != "") &&
270 (m_FileItem->GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
271 (m_FileItem->GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
272 (m_FileItem->GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)
276 //do not crossfade when playing consecutive albumtracks
277 m_upcomingCrossfadeMS = 0;
282 bool PAPlayer::QueueNextFile(const CFileItem &file)
284 return QueueNextFileEx(file);
287 bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */)
289 StreamInfo *si = new StreamInfo();
291 if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75))
293 CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
296 m_callback.OnQueueNextItem();
300 /* decode until there is data-available */
301 si->m_decoder.Start();
302 while(si->m_decoder.GetDataSize() == 0)
304 int status = si->m_decoder.GetStatus();
305 if (status == STATUS_ENDED ||
306 status == STATUS_NO_FILE ||
307 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
309 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
311 si->m_decoder.Destroy();
313 m_callback.OnQueueNextItem();
317 /* yield our time so that the main PAP thread doesnt stall */
321 UpdateCrossfadeTime(file);
323 /* init the streaminfo struct */
324 si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat);
325 si->m_startOffset = file.m_lStartOffset * 1000 / 75;
326 si->m_endOffset = file.m_lEndOffset * 1000 / 75;
327 si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3;
328 si->m_bytesPerFrame = si->m_bytesPerSample * si->m_channelInfo.Count();
329 si->m_started = false;
330 si->m_finishing = false;
331 si->m_framesSent = 0;
332 si->m_seekNextAtFrame = 0;
333 si->m_seekFrame = -1;
335 si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
336 si->m_fadeOutTriggered = false;
337 si->m_isSlaved = false;
339 int64_t streamTotalTime = si->m_decoder.TotalTime();
341 streamTotalTime = si->m_endOffset - si->m_startOffset;
343 si->m_prepareNextAtFrame = 0;
344 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
345 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f);
347 si->m_prepareTriggered = false;
349 si->m_playNextAtFrame = 0;
350 si->m_playNextTriggered = false;
352 if (!PrepareStream(si))
354 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
356 si->m_decoder.Destroy();
358 m_callback.OnQueueNextItem();
362 /* add the stream to the list */
363 CExclusiveLock lock(m_streamsLock);
364 m_streams.push_back(si);
365 //update the current stream to start playing the next track at the correct frame.
366 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
373 void PAPlayer::UpdateStreamInfoPlayNextAtFrame(StreamInfo *si, unsigned int crossFadingTime)
377 int64_t streamTotalTime = si->m_decoder.TotalTime();
379 streamTotalTime = si->m_endOffset - si->m_startOffset;
380 if (streamTotalTime < crossFadingTime)
381 si->m_playNextAtFrame = (int)((streamTotalTime / 2) * si->m_sampleRate / 1000.0f);
383 si->m_playNextAtFrame = (int)((streamTotalTime - crossFadingTime) * si->m_sampleRate / 1000.0f);
387 inline bool PAPlayer::PrepareStream(StreamInfo *si)
389 /* if we have a stream we are already prepared */
393 /* get a paused stream */
394 si->m_stream = CAEFactory::MakeStream(
397 si->m_encodedSampleRate,
404 CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
408 si->m_stream->SetVolume (si->m_volume);
409 si->m_stream->SetReplayGain(si->m_decoder.GetReplayGain());
411 /* if its not the first stream and crossfade is not enabled */
412 if (m_currentStream && m_currentStream != si && !m_upcomingCrossfadeMS)
414 /* slave the stream for gapless */
415 si->m_isSlaved = true;
416 m_currentStream->m_stream->RegisterSlave(si->m_stream);
419 /* fill the stream's buffer */
420 while(si->m_stream->IsBuffering())
422 int status = si->m_decoder.GetStatus();
423 if (status == STATUS_ENDED ||
424 status == STATUS_NO_FILE ||
425 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
427 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
434 /* yield our time so that the main PAP thread doesnt stall */
438 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
443 bool PAPlayer::CloseFile()
446 SoftStop(true, true);
447 CloseAllStreams(false);
449 /* wait for the thread to terminate */
450 StopThread(true);//true - wait for end of thread
454 void PAPlayer::Process()
456 if (!m_startEvent.WaitMSec(100))
458 CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
462 CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");
463 while(m_isPlaying && !m_bStop)
465 /* this needs to happen outside of any locks to prevent deadlocks */
466 if (m_signalSpeedChange)
468 m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
469 m_signalSpeedChange = false;
472 double delay = 100.0;
473 double buffer = 100.0;
474 ProcessStreams(delay, buffer);
476 double watermark = buffer * 0.5;
477 #if defined(TARGET_DARWIN)
478 // In CoreAudio the delay can be bigger then the buffer
479 // because of delay from the HAL/Hardware
480 // This is the case when the buffer is full (e.x. 1 sec)
481 // and there is a HAL-Delay. In that case we would never sleep
482 // but load one cpu core up to 100% (happens on osx/ios whenever
483 // the first stream is finished and a prebuffered second stream
484 // starts to play. A BIG FIXME HERE.
485 if ((delay < buffer || buffer == 1) && delay > watermark)
487 if ((delay < buffer) && delay > watermark)
489 CThread::Sleep(MathUtils::round_int((delay - watermark) * 1000.0));
491 GetTimeInternal(); //update for GUI
494 if(m_isFinished && !m_bStop)
495 m_callback.OnPlayBackEnded();
497 m_callback.OnPlayBackStopped();
500 inline void PAPlayer::ProcessStreams(double &delay, double &buffer)
502 CSharedLock sharedLock(m_streamsLock);
503 if (m_isFinished && m_streams.empty() && m_finishing.empty())
510 /* destroy any drained streams */
511 for(StreamList::iterator itt = m_finishing.begin(); itt != m_finishing.end();)
513 StreamInfo* si = *itt;
514 if (si->m_stream->IsDrained())
516 itt = m_finishing.erase(itt);
517 CAEFactory::FreeStream(si->m_stream);
519 CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
526 CExclusiveLock lock(m_streamsLock);
528 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
530 StreamInfo* si = *itt;
531 if (!m_currentStream && !si->m_started)
533 m_currentStream = si;
534 UpdateGUIData(si); //update for GUI
536 /* if the stream is finishing */
537 if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, delay, buffer))
539 if (!si->m_prepareTriggered)
541 si->m_prepareTriggered = true;
542 m_callback.OnQueueNextItem();
545 /* remove the stream */
546 itt = m_streams.erase(itt);
547 /* if its the current stream */
548 if (si == m_currentStream)
550 /* if it was the last stream */
551 if (itt == m_streams.end())
553 /* if it didnt trigger the next queue item */
554 if (!si->m_prepareTriggered)
556 m_callback.OnQueueNextItem();
557 si->m_prepareTriggered = true;
559 m_currentStream = NULL;
563 m_currentStream = *itt;
564 UpdateGUIData(*itt); //update for GUI
568 /* unregister the audio callback */
569 si->m_stream->UnRegisterAudioCallback();
570 si->m_decoder.Destroy();
571 si->m_stream->Drain();
572 m_finishing.push_back(si);
579 /* is it time to prepare the next stream? */
580 if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
582 si->m_prepareTriggered = true;
583 m_callback.OnQueueNextItem();
586 /* it is time to start playing the next stream? */
587 if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && si->m_framesSent >= si->m_playNextAtFrame)
589 if (!si->m_prepareTriggered)
591 si->m_prepareTriggered = true;
592 m_callback.OnQueueNextItem();
597 if (m_upcomingCrossfadeMS)
599 si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS);
600 si->m_fadeOutTriggered = true;
602 m_currentStream = NULL;
604 /* unregister the audio callback */
605 si->m_stream->UnRegisterAudioCallback();
608 si->m_playNextTriggered = true;
613 inline bool PAPlayer::ProcessStream(StreamInfo *si, double &delay, double &buffer)
615 /* if playback needs to start on this stream, do it */
616 if (si == m_currentStream && !si->m_started)
618 si->m_started = true;
619 si->m_stream->RegisterAudioCallback(m_audioCallback);
621 si->m_stream->Resume();
622 si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS);
623 m_callback.OnPlayBackStarted();
626 /* if we have not started yet and the stream has been primed */
627 unsigned int space = si->m_stream->GetSpace();
628 if (!si->m_started && !space)
631 /* see if it is time yet to FF/RW or a direct seek */
632 if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
634 int64_t time = (int64_t)0;
635 /* if its a direct seek */
636 if (si->m_seekFrame > -1)
638 time = (int64_t)((float)si->m_seekFrame / (float)si->m_sampleRate * 1000.0f);
639 si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_sampleRate) / 1000.0f);
640 si->m_seekFrame = -1;
641 m_playerGUIData.m_time = time; //update for GUI
646 si->m_framesSent += si->m_sampleRate * (m_playbackSpeed - 1);
647 si->m_seekNextAtFrame = si->m_framesSent + si->m_sampleRate / 2;
648 time = (int64_t)(((float)si->m_framesSent / (float)si->m_sampleRate * 1000.0f) + (float)si->m_startOffset);
651 /* if we are seeking back before the start of the track start normal playback */
652 if (time < si->m_startOffset || si->m_framesSent < 0)
654 time = si->m_startOffset;
655 si->m_framesSent = (int)(si->m_startOffset * si->m_sampleRate / 1000);
656 si->m_seekNextAtFrame = 0;
660 si->m_decoder.Seek(time);
663 int status = si->m_decoder.GetStatus();
664 if (status == STATUS_ENDED ||
665 status == STATUS_NO_FILE ||
666 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR ||
667 ((si->m_endOffset) && (si->m_framesSent / si->m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000)))
669 CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
676 /* update the delay time if we are running */
679 if (si->m_stream->IsBuffering())
682 delay = std::min(delay , si->m_stream->GetDelay());
683 buffer = std::min(buffer, si->m_stream->GetCacheTotal());
689 bool PAPlayer::QueueData(StreamInfo *si)
691 unsigned int space = si->m_stream->GetSpace();
692 unsigned int samples = std::min(si->m_decoder.GetDataSize(), space / si->m_bytesPerSample);
696 void* data = si->m_decoder.GetData(samples);
699 CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
703 unsigned int added = si->m_stream->AddData(data, samples * si->m_bytesPerSample);
704 si->m_framesSent += added / si->m_bytesPerFrame;
706 const ICodec* codec = si->m_decoder.GetCodec();
707 m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0; //update for GUI
712 void PAPlayer::OnExit()
717 void PAPlayer::RegisterAudioCallback(IAudioCallback* pCallback)
719 CSharedLock lock(m_streamsLock);
720 m_audioCallback = pCallback;
721 if (m_currentStream && m_currentStream->m_stream)
722 m_currentStream->m_stream->RegisterAudioCallback(pCallback);
725 void PAPlayer::UnRegisterAudioCallback()
727 CSharedLock lock(m_streamsLock);
728 /* only one stream should have the callback, but we do it to all just incase */
729 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
730 if ((*itt)->m_stream)
731 (*itt)->m_stream->UnRegisterAudioCallback();
732 m_audioCallback = NULL;
735 void PAPlayer::OnNothingToQueueNotify()
740 bool PAPlayer::IsPlaying() const
745 bool PAPlayer::IsPaused() const
750 void PAPlayer::Pause()
756 m_callback.OnPlayBackResumed();
761 SoftStop(true, false);
762 m_callback.OnPlayBackPaused();
766 void PAPlayer::SetVolume(float volume)
771 void PAPlayer::SetDynamicRangeCompression(long drc)
776 void PAPlayer::ToFFRW(int iSpeed)
778 m_playbackSpeed = iSpeed;
779 m_signalSpeedChange = true;
782 int64_t PAPlayer::GetTimeInternal()
784 CSharedLock lock(m_streamsLock);
785 if (!m_currentStream)
788 double time = ((double)m_currentStream->m_framesSent / (double)m_currentStream->m_sampleRate);
789 if (m_currentStream->m_stream)
790 time -= m_currentStream->m_stream->GetDelay();
791 time = time * 1000.0;
793 m_playerGUIData.m_time = (int64_t)time; //update for GUI
795 return (int64_t)time;
798 int64_t PAPlayer::GetTime()
800 return m_playerGUIData.m_time;
803 int64_t PAPlayer::GetTotalTime64()
805 CSharedLock lock(m_streamsLock);
806 if (!m_currentStream)
809 int64_t total = m_currentStream->m_decoder.TotalTime();
810 if (m_currentStream->m_endOffset)
811 total = m_currentStream->m_endOffset;
812 total -= m_currentStream->m_startOffset;
816 int64_t PAPlayer::GetTotalTime()
818 return m_playerGUIData.m_totalTime;
821 int PAPlayer::GetCacheLevel() const
823 return m_playerGUIData.m_cacheLevel;
826 int PAPlayer::GetBitsPerSample()
828 return m_playerGUIData.m_bitsPerSample;
831 int PAPlayer::GetSampleRate()
833 return m_playerGUIData.m_sampleRate;
836 void PAPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
838 info.bitrate = m_playerGUIData.m_audioBitrate;
839 info.channels = m_playerGUIData.m_channelCount;
840 info.audioCodecName = m_playerGUIData.m_codec;
843 bool PAPlayer::CanSeek()
845 return m_playerGUIData.m_canSeek;
848 void PAPlayer::Seek(bool bPlus, bool bLargeStep)
850 if (!CanSeek()) return;
853 if (g_advancedSettings.m_musicUseTimeSeeking && GetTotalTime() > 2 * g_advancedSettings.m_musicTimeSeekForwardBig)
856 seek = bPlus ? g_advancedSettings.m_musicTimeSeekForwardBig : g_advancedSettings.m_musicTimeSeekBackwardBig;
858 seek = bPlus ? g_advancedSettings.m_musicTimeSeekForward : g_advancedSettings.m_musicTimeSeekBackward;
866 percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForwardBig : (float)g_advancedSettings.m_musicPercentSeekBackwardBig;
868 percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForward : (float)g_advancedSettings.m_musicPercentSeekBackward;
869 seek = (__int64)(GetTotalTime64() * (GetPercentage() + percent) / 100);
875 void PAPlayer::SeekTime(int64_t iTime /*=0*/)
877 if (!CanSeek()) return;
879 CSharedLock lock(m_streamsLock);
880 if (!m_currentStream)
883 int seekOffset = (int)(iTime - GetTimeInternal());
885 if (m_playbackSpeed != 1)
888 m_currentStream->m_seekFrame = (int)((float)m_currentStream->m_sampleRate * ((float)iTime + (float)m_currentStream->m_startOffset) / 1000.0f);
889 m_callback.OnPlayBackSeek((int)iTime, seekOffset);
892 void PAPlayer::SeekPercentage(float fPercent /*=0*/)
894 if (fPercent < 0.0f ) fPercent = 0.0f;
895 if (fPercent > 100.0f) fPercent = 100.0f;
896 SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
899 float PAPlayer::GetPercentage()
901 if (m_playerGUIData.m_totalTime > 0)
902 return m_playerGUIData.m_time * 100.0f / m_playerGUIData.m_totalTime;
907 bool PAPlayer::SkipNext()
912 void PAPlayer::UpdateGUIData(StreamInfo *si)
914 /* Store data need by external threads in member
915 * structure to prevent locking conflicts when
916 * data required by GUI and main application
918 CSharedLock lock(m_streamsLock);
920 m_playerGUIData.m_sampleRate = si->m_sampleRate;
921 m_playerGUIData.m_bitsPerSample = si->m_bytesPerSample << 3;
922 m_playerGUIData.m_channelCount = si->m_channelInfo.Count();
923 m_playerGUIData.m_canSeek = si->m_decoder.CanSeek();
925 const ICodec* codec = si->m_decoder.GetCodec();
927 m_playerGUIData.m_audioBitrate = codec ? codec->m_Bitrate : 0;
928 strncpy(m_playerGUIData.m_codec,codec ? codec->m_CodecName : "",20);
929 m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0;
931 int64_t total = si->m_decoder.TotalTime();
933 total = m_currentStream->m_endOffset;
934 total -= m_currentStream->m_startOffset;
935 m_playerGUIData.m_totalTime = total;