Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / cores / paplayer / PAPlayer.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "PAPlayer.h"
22 #include "CodecFactory.h"
23 #include "FileItem.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"
30 #include "utils/JobManager.h"
31
32 #include "threads/SingleLock.h"
33 #include "cores/AudioEngine/AEFactory.h"
34 #include "cores/AudioEngine/Utils/AEUtil.h"
35 #include "cores/AudioEngine/Interfaces/AEStream.h"
36
37 #define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
38 #define FAST_XFADE_TIME           80 /* 80 milliseconds */
39 #define MAX_SKIP_XFADE_TIME     2000 /* max 2 seconds crossfade on track skip */
40
41 CAEChannelInfo ICodec::GetChannelInfo()
42 {
43   return CAEUtil::GuessChLayout(m_Channels);
44 }
45
46 class CQueueNextFileJob : public CJob
47 {
48   CFileItem m_item;
49   PAPlayer &m_player;
50
51 public:
52                 CQueueNextFileJob(const CFileItem& item, PAPlayer &player)
53                   : m_item(item), m_player(player) {}
54   virtual       ~CQueueNextFileJob() {}
55   virtual bool  DoWork()
56   {
57     return m_player.QueueNextFileEx(m_item, true, true);
58   }
59 };
60
61 // PAP: Psycho-acoustic Audio Player
62 // Supporting all open  audio codec standards.
63 // First one being nullsoft's nsv audio decoder format
64
65 PAPlayer::PAPlayer(IPlayerCallback& callback) :
66   IPlayer              (callback),
67   CThread              ("PAPlayer"),
68   m_signalSpeedChange  (false),
69   m_playbackSpeed      (1    ),
70   m_isPlaying          (false),
71   m_isPaused           (false),
72   m_isFinished         (false),
73   m_defaultCrossfadeMS (0),
74   m_upcomingCrossfadeMS(0),
75   m_currentStream      (NULL ),
76   m_audioCallback      (NULL ),
77   m_FileItem           (new CFileItem()),
78   m_jobCounter         (0),
79   m_continueStream     (false)
80 {
81   memset(&m_playerGUIData, 0, sizeof(m_playerGUIData));
82 }
83
84 PAPlayer::~PAPlayer()
85 {
86   CloseFile();
87   delete m_FileItem;
88 }
89
90 bool PAPlayer::HandlesType(const CStdString &type)
91 {
92   ICodec* codec = CodecFactory::CreateCodec(type);
93   if (codec && codec->CanInit())
94   {
95     delete codec;
96     return true;
97   }
98
99   return false;
100 }
101
102 void PAPlayer::SoftStart(bool wait/* = false */)
103 {
104   CSharedLock lock(m_streamsLock);
105   for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
106   {
107     StreamInfo* si = *itt;
108     if (si->m_fadeOutTriggered)
109       continue;
110
111     si->m_stream->Resume();
112     si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
113   }
114   
115   if (wait)
116   {
117     /* wait for them to fade in */
118     lock.Leave();
119     Sleep(FAST_XFADE_TIME);
120     lock.Enter();
121
122     /* be sure they have faded in */
123     while(wait)
124     {
125       wait = false;
126       for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
127       {
128         StreamInfo* si = *itt;
129         if (si->m_stream->IsFading())
130         {
131           lock.Leave();
132           wait = true;
133           Sleep(1);
134           lock.Enter();
135           break;
136         }
137       }
138     }
139   }
140 }
141
142 void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
143 {
144   /* fade all the streams out fast for a nice soft stop */
145   CSharedLock lock(m_streamsLock);
146   for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
147   {
148     StreamInfo* si = *itt;
149     if (si->m_stream)
150       si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
151
152     if (close)
153     {
154       si->m_prepareTriggered  = true;
155       si->m_playNextTriggered = true;
156       si->m_fadeOutTriggered  = true;
157     }
158   }
159
160   /* if we are going to wait for them to finish fading */
161   if(wait)
162   {
163     /* wait for them to fade out */
164     lock.Leave();
165     Sleep(FAST_XFADE_TIME);
166     lock.Enter();
167
168     /* be sure they have faded out */
169     while(wait && !CAEFactory::IsSuspended())
170     {
171       wait = false;
172       for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
173       {
174         StreamInfo* si = *itt;
175         if (si->m_stream && si->m_stream->IsFading())
176         {
177           lock.Leave();
178           wait = true;
179           Sleep(1);
180           lock.Enter();
181           break;
182         }
183       }
184     }
185
186     /* if we are not closing the streams, pause them */
187     if (!close)
188     {
189       for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
190       {
191         StreamInfo* si = *itt;
192         si->m_stream->Pause();
193       }
194     }
195   }
196 }
197
198 void PAPlayer::CloseAllStreams(bool fade/* = true */)
199 {
200   if (!fade) 
201   {
202     CExclusiveLock lock(m_streamsLock);
203     while(!m_streams.empty())
204     {
205       StreamInfo* si = m_streams.front();
206       m_streams.pop_front();
207       
208       if (si->m_stream)
209       {
210         CAEFactory::FreeStream(si->m_stream);
211         si->m_stream = NULL;
212       }
213
214       si->m_decoder.Destroy();
215       delete si;
216     }
217
218     while(!m_finishing.empty())
219     {
220       StreamInfo* si = m_finishing.front();
221       m_finishing.pop_front();
222
223       if (si->m_stream)
224       {
225         CAEFactory::FreeStream(si->m_stream);
226         si->m_stream = NULL;
227       }
228
229       si->m_decoder.Destroy();
230       delete si;
231     }
232     m_currentStream = NULL;
233   }
234   else
235   {
236     SoftStop(false, true);
237     CExclusiveLock lock(m_streamsLock);
238     m_currentStream = NULL;
239   }  
240 }
241
242 bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
243 {
244   m_defaultCrossfadeMS = CSettings::Get().GetInt("musicplayer.crossfade") * 1000;
245
246   if (m_streams.size() > 1 || !m_defaultCrossfadeMS || m_isPaused)
247   {
248     CloseAllStreams(!m_isPaused);
249     StopThread();
250     m_isPaused = false; // Make sure to reset the pause state
251   }
252
253   if (!QueueNextFileEx(file, false))
254     return false;
255
256   CSharedLock lock(m_streamsLock);
257   if (m_streams.size() == 2)
258   {
259     //do a short crossfade on trackskip, set to max 2 seconds for these prev/next transitions
260     m_upcomingCrossfadeMS = std::min(m_defaultCrossfadeMS, (unsigned int)MAX_SKIP_XFADE_TIME);
261
262     //start transition to next track
263     StreamInfo* si = m_streams.front();
264     si->m_playNextAtFrame  = si->m_framesSent; //start next track at current frame
265     si->m_prepareTriggered = true; //next track is ready to go
266   }
267   lock.Leave();
268
269   if (!IsRunning())
270     Create();
271
272   /* trigger playback start */
273   m_isPlaying = true;
274   m_startEvent.Set();
275   return true;
276 }
277
278 void PAPlayer::UpdateCrossfadeTime(const CFileItem& file)
279 {
280   // we explicitely disable crossfading for audio cds
281   if(file.IsCDDA())
282    m_upcomingCrossfadeMS = 0;
283   else
284     m_upcomingCrossfadeMS = m_defaultCrossfadeMS = CSettings::Get().GetInt("musicplayer.crossfade") * 1000;
285   if (m_upcomingCrossfadeMS)
286   {
287     if (m_streams.size() == 0 ||
288          (
289             file.HasMusicInfoTag() && !CSettings::Get().GetBool("musicplayer.crossfadealbumtracks") &&
290             m_FileItem->HasMusicInfoTag() &&
291             (m_FileItem->GetMusicInfoTag()->GetAlbum() != "") &&
292             (m_FileItem->GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
293             (m_FileItem->GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
294             (m_FileItem->GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)
295          )
296        )
297     {
298       //do not crossfade when playing consecutive albumtracks
299       m_upcomingCrossfadeMS = 0;
300     }
301   }
302 }
303
304 bool PAPlayer::QueueNextFile(const CFileItem &file)
305 {
306   {
307     CExclusiveLock lock(m_streamsLock);
308     m_jobCounter++;
309   }
310   CJobManager::GetInstance().AddJob(new CQueueNextFileJob(file, *this), this, CJob::PRIORITY_NORMAL);
311   return true;
312 }
313
314 bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */, bool job /* = false */)
315 {
316   StreamInfo *si = new StreamInfo();
317
318   // check if we advance a track of a CUE sheet
319   // if this is the case we don't need to open a new stream
320   std::string newURL = file.GetMusicInfoTag() ? file.GetMusicInfoTag()->GetURL() : file.GetPath();
321   std::string oldURL = m_FileItem->GetMusicInfoTag() ? m_FileItem->GetMusicInfoTag()->GetURL() : m_FileItem->GetPath();
322   if (newURL.compare(oldURL) == 0 &&
323       file.m_lStartOffset &&
324       file.m_lStartOffset == m_FileItem->m_lEndOffset &&
325       m_currentStream && m_currentStream->m_prepareTriggered)
326   {
327     m_continueStream = true;
328     m_upcomingCrossfadeMS = 0;
329     *m_FileItem = file;
330     return true;
331   }
332   else
333   {
334     m_continueStream = false;
335   }
336
337   if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75))
338   {
339     CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
340
341     delete si;
342     // advance playlist
343     if (job)
344       m_callback.OnPlayBackStarted();
345     m_callback.OnQueueNextItem();
346     return false;
347   }
348
349   /* decode until there is data-available */
350   si->m_decoder.Start();
351   while(si->m_decoder.GetDataSize() == 0)
352   {
353     int status = si->m_decoder.GetStatus();
354     if (status == STATUS_ENDED   ||
355         status == STATUS_NO_FILE ||
356         si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
357     {
358       CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
359
360       si->m_decoder.Destroy();
361       delete si;
362       // advance playlist
363       if (job)
364         m_callback.OnPlayBackStarted();
365       m_callback.OnQueueNextItem();
366       return false;
367     }
368
369     /* yield our time so that the main PAP thread doesnt stall */
370     CThread::Sleep(1);
371   }
372
373   /* init the streaminfo struct */
374   si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat);
375   si->m_startOffset        = file.m_lStartOffset * 1000 / 75;
376   si->m_endOffset          = file.m_lEndOffset   * 1000 / 75;
377   si->m_bytesPerSample     = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3;
378   si->m_bytesPerFrame      = si->m_bytesPerSample * si->m_channelInfo.Count();
379   si->m_started            = false;
380   si->m_finishing          = false;
381   si->m_framesSent         = 0;
382   si->m_seekNextAtFrame    = 0;
383   si->m_seekFrame          = -1;
384   si->m_stream             = NULL;
385   si->m_volume             = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
386   si->m_fadeOutTriggered   = false;
387   si->m_isSlaved           = false;
388
389   int64_t streamTotalTime = si->m_decoder.TotalTime();
390   if (si->m_endOffset)
391     streamTotalTime = si->m_endOffset - si->m_startOffset;
392   
393   si->m_prepareNextAtFrame = 0;
394   // cd drives don't really like it to be crossfaded or prepared
395   if(!file.IsCDDA())
396   {
397     if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
398       si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f);
399   }
400
401   if (m_currentStream && (AE_IS_RAW(m_currentStream->m_dataFormat) || AE_IS_RAW(si->m_dataFormat)))
402   {
403     m_currentStream->m_prepareTriggered = false;
404     m_currentStream->m_waitOnDrain = true;
405     m_currentStream->m_prepareNextAtFrame = 0;
406     si->m_decoder.Destroy();
407     delete si;
408     return false;
409   }
410
411   UpdateCrossfadeTime(file);
412
413   si->m_prepareTriggered = false;
414   si->m_playNextAtFrame = 0;
415   si->m_playNextTriggered = false;
416   si->m_waitOnDrain = false;
417
418   if (!PrepareStream(si))
419   {
420     CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
421     
422     si->m_decoder.Destroy();
423     delete si;
424     // advance playlist
425     if (job)
426       m_callback.OnPlayBackStarted();
427     m_callback.OnQueueNextItem();
428     return false;
429   }
430
431   /* add the stream to the list */
432   CExclusiveLock lock(m_streamsLock);
433   m_streams.push_back(si);
434   //update the current stream to start playing the next track at the correct frame.
435   UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
436
437   *m_FileItem = file;
438
439   return true;
440 }
441
442 void PAPlayer::UpdateStreamInfoPlayNextAtFrame(StreamInfo *si, unsigned int crossFadingTime)
443 {
444   if (si)
445   {
446     int64_t streamTotalTime = si->m_decoder.TotalTime();
447     if (si->m_endOffset)
448       streamTotalTime = si->m_endOffset - si->m_startOffset;
449     if (streamTotalTime < crossFadingTime)
450       si->m_playNextAtFrame = (int)((streamTotalTime / 2) * si->m_sampleRate / 1000.0f);
451     else
452       si->m_playNextAtFrame = (int)((streamTotalTime - crossFadingTime) * si->m_sampleRate / 1000.0f);
453   }
454 }
455
456 inline bool PAPlayer::PrepareStream(StreamInfo *si)
457 {
458   /* if we have a stream we are already prepared */
459   if (si->m_stream)
460     return true;
461
462   /* get a paused stream */
463   si->m_stream = CAEFactory::MakeStream(
464     si->m_dataFormat,
465     si->m_sampleRate,
466     si->m_encodedSampleRate,
467     si->m_channelInfo,
468     AESTREAM_PAUSED
469   );
470
471   if (!si->m_stream)
472   {
473     CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
474     return false;
475   }
476
477   si->m_stream->SetVolume    (si->m_volume);
478   si->m_stream->SetReplayGain(si->m_decoder.GetReplayGain());
479
480   /* if its not the first stream and crossfade is not enabled */
481   if (m_currentStream && m_currentStream != si && !m_upcomingCrossfadeMS)
482   {
483     /* slave the stream for gapless */
484     si->m_isSlaved = true;
485     m_currentStream->m_stream->RegisterSlave(si->m_stream);
486   }
487
488   /* fill the stream's buffer */
489   while(si->m_stream->IsBuffering())
490   {
491     int status = si->m_decoder.GetStatus();
492     if (status == STATUS_ENDED   ||
493         status == STATUS_NO_FILE ||
494         si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
495     {
496       CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
497       break;
498     }
499
500     if (!QueueData(si))
501       break;
502
503     /* yield our time so that the main PAP thread doesnt stall */
504     CThread::Sleep(1);
505   }
506
507   CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
508
509   return true;
510 }
511
512 bool PAPlayer::CloseFile(bool reopen)
513 {
514   if (reopen)
515     CAEFactory::KeepConfiguration(3000);
516
517   if (!m_isPaused)
518     SoftStop(true, true);
519   CloseAllStreams(false);
520
521   /* wait for the thread to terminate */
522   StopThread(true);//true - wait for end of thread
523
524   // wait for any pending jobs to complete
525   {
526     CSharedLock lock(m_streamsLock);
527     while (m_jobCounter > 0)
528     {
529       lock.Leave();
530       m_jobEvent.WaitMSec(100);
531       lock.Enter();
532     }
533   }
534
535   return true;
536 }
537
538 void PAPlayer::Process()
539 {
540   if (!m_startEvent.WaitMSec(100))
541   {
542     CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
543     return;
544   }
545
546   CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");  
547   while(m_isPlaying && !m_bStop)
548   {
549     /* this needs to happen outside of any locks to prevent deadlocks */
550     if (m_signalSpeedChange)
551     {
552       m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
553       m_signalSpeedChange = false;
554     }
555
556     double freeBufferTime = 0.0;
557     ProcessStreams(freeBufferTime);
558
559     // if none of our streams wants at least 10ms of data, we sleep
560     if (freeBufferTime < 0.01)
561     {
562       CThread::Sleep(10);
563     }
564
565     GetTimeInternal(); //update for GUI
566   }
567
568   if(m_isFinished && !m_bStop)
569     m_callback.OnPlayBackEnded();
570   else
571     m_callback.OnPlayBackStopped();
572 }
573
574 inline void PAPlayer::ProcessStreams(double &freeBufferTime)
575 {
576   CSharedLock sharedLock(m_streamsLock);
577   if (m_isFinished && m_streams.empty() && m_finishing.empty())
578   {
579     m_isPlaying = false;
580     freeBufferTime = 1.0;
581     return;
582   }
583
584   /* destroy any drained streams */
585   for(StreamList::iterator itt = m_finishing.begin(); itt != m_finishing.end();)
586   {
587     StreamInfo* si = *itt;
588     if (si->m_stream->IsDrained())
589     {      
590       itt = m_finishing.erase(itt);
591       CAEFactory::FreeStream(si->m_stream);
592       delete si;
593       CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
594     }
595     else
596       ++itt;
597   }
598
599   sharedLock.Leave();
600   CExclusiveLock lock(m_streamsLock);
601
602   for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
603   {
604     StreamInfo* si = *itt;
605     if (!m_currentStream && !si->m_started)
606     {
607       m_currentStream = si;
608       UpdateGUIData(si); //update for GUI
609     }
610     /* if the stream is finishing */
611     if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, freeBufferTime))
612     {
613       if (!si->m_prepareTriggered)
614       {
615         if (si->m_waitOnDrain)
616         {
617           si->m_stream->Drain(true);
618           si->m_waitOnDrain = false;
619         }
620         si->m_prepareTriggered = true;
621         m_callback.OnQueueNextItem();
622       }
623
624       /* remove the stream */
625       itt = m_streams.erase(itt);
626       /* if its the current stream */
627       if (si == m_currentStream)
628       {
629         /* if it was the last stream */
630         if (itt == m_streams.end())
631         {
632           /* if it didnt trigger the next queue item */
633           if (!si->m_prepareTriggered)
634           {
635             if (si->m_waitOnDrain)
636             {
637               si->m_stream->Drain(true);
638               si->m_waitOnDrain = false;
639             }
640             m_callback.OnQueueNextItem();
641             si->m_prepareTriggered = true;
642           }
643           m_currentStream = NULL;
644         }
645         else
646         {
647           m_currentStream = *itt;
648           UpdateGUIData(*itt); //update for GUI
649         }
650       }
651
652       /* unregister the audio callback */
653       si->m_stream->UnRegisterAudioCallback();
654       si->m_decoder.Destroy();      
655       si->m_stream->Drain(false);
656       m_finishing.push_back(si);
657       return;
658     }
659
660     if (!si->m_started)
661       continue;
662
663     /* is it time to prepare the next stream? */
664     if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
665     {
666       si->m_prepareTriggered = true;
667       m_callback.OnQueueNextItem();
668     }
669
670     /* it is time to start playing the next stream? */
671     if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && !m_continueStream && si->m_framesSent >= si->m_playNextAtFrame)
672     {
673       if (!si->m_prepareTriggered)
674       {
675         si->m_prepareTriggered = true;
676         m_callback.OnQueueNextItem();
677       }
678
679       if (!m_isFinished)
680       {
681         if (m_upcomingCrossfadeMS)
682         {
683           si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS);
684           si->m_fadeOutTriggered = true;
685         }
686         m_currentStream = NULL;
687
688         /* unregister the audio callback */
689         si->m_stream->UnRegisterAudioCallback();
690       }
691
692       si->m_playNextTriggered = true;
693     }
694   }
695 }
696
697 inline bool PAPlayer::ProcessStream(StreamInfo *si, double &freeBufferTime)
698 {
699   /* if playback needs to start on this stream, do it */
700   if (si == m_currentStream && !si->m_started)
701   {
702     si->m_started = true;
703     si->m_stream->RegisterAudioCallback(m_audioCallback);
704     if (!si->m_isSlaved)
705       si->m_stream->Resume();
706     si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS);
707     m_callback.OnPlayBackStarted();
708   }
709
710   /* if we have not started yet and the stream has been primed */
711   unsigned int space = si->m_stream->GetSpace();
712   if (!si->m_started && !space)
713     return true;
714
715   /* see if it is time yet to FF/RW or a direct seek */
716   if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
717   {
718     int64_t time = (int64_t)0;
719     /* if its a direct seek */
720     if (si->m_seekFrame > -1)
721     {
722       time = (int64_t)((float)si->m_seekFrame / (float)si->m_sampleRate * 1000.0f);
723       si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_sampleRate) / 1000.0f);
724       si->m_seekFrame  = -1;
725       m_playerGUIData.m_time = time; //update for GUI
726       si->m_seekNextAtFrame = 0;
727     }
728     /* if its FF/RW */
729     else
730     {
731       si->m_framesSent      += si->m_sampleRate * (m_playbackSpeed  - 1);
732       si->m_seekNextAtFrame  = si->m_framesSent + si->m_sampleRate / 2;
733       time = (int64_t)(((float)si->m_framesSent / (float)si->m_sampleRate * 1000.0f) + (float)si->m_startOffset);
734     }
735
736     /* if we are seeking back before the start of the track start normal playback */
737     if (time < si->m_startOffset || si->m_framesSent < 0)
738     {
739       time = si->m_startOffset;
740       si->m_framesSent      = 0;
741       si->m_seekNextAtFrame = 0;
742       ToFFRW(1);
743     }
744
745     si->m_decoder.Seek(time);
746   }
747
748   int status = si->m_decoder.GetStatus();
749   if (status == STATUS_ENDED   ||
750       status == STATUS_NO_FILE ||
751       si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR ||
752       ((si->m_endOffset) && (si->m_framesSent / si->m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000)))
753   {
754     if (si == m_currentStream && m_continueStream)
755     {
756       // update current stream with info of next track
757       si->m_startOffset = m_FileItem->m_lStartOffset * 1000 / 75;
758       if (m_FileItem->m_lEndOffset)
759         si->m_endOffset = m_FileItem->m_lEndOffset * 1000 / 75;
760       else
761         si->m_endOffset = 0;
762       si->m_framesSent = 0;
763
764       int64_t streamTotalTime = si->m_decoder.TotalTime() - si->m_startOffset;
765       if (si->m_endOffset)
766         streamTotalTime = si->m_endOffset - si->m_startOffset;
767
768       // calculate time when to prepare next stream
769       si->m_prepareNextAtFrame = 0;
770       if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
771         si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f);
772
773       si->m_prepareTriggered = false;
774       si->m_playNextAtFrame = 0;
775       si->m_playNextTriggered = false;
776       si->m_seekNextAtFrame = 0;
777
778       //update the current stream to start playing the next track at the correct frame.
779       UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
780
781       UpdateGUIData(si);
782       m_callback.OnPlayBackStarted();
783       m_continueStream = false;
784     }
785     else
786     {
787       CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
788       return false;
789     }
790   }
791
792   if (!QueueData(si))
793     return false;
794
795   /* update free buffer time if we are running */
796   if (si->m_started)
797   {
798     if (si->m_stream->IsBuffering())
799       freeBufferTime = 1.0;
800     else
801     {
802       double free_space = (double)(si->m_stream->GetSpace() / si->m_bytesPerSample) / si->m_sampleRate;
803       freeBufferTime = std::max(freeBufferTime , free_space);
804     }
805   }
806
807   return true;
808 }
809
810 bool PAPlayer::QueueData(StreamInfo *si)
811 {
812   unsigned int space   = si->m_stream->GetSpace();
813   unsigned int samples = std::min(si->m_decoder.GetDataSize(), space / si->m_bytesPerSample);
814   if (!samples)
815     return true;
816
817   void* data = si->m_decoder.GetData(samples);
818   if (!data)
819   {
820     CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
821     return false;
822   }
823
824   unsigned int added = si->m_stream->AddData(data, samples * si->m_bytesPerSample);
825   si->m_framesSent += added / si->m_bytesPerFrame;
826
827   const ICodec* codec = si->m_decoder.GetCodec();
828   m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0; //update for GUI
829
830   return true;
831 }
832
833 void PAPlayer::OnExit()
834 {
835
836 }
837
838 void PAPlayer::RegisterAudioCallback(IAudioCallback* pCallback)
839 {
840   CSharedLock lock(m_streamsLock);
841   m_audioCallback = pCallback;
842   if (m_currentStream && m_currentStream->m_stream)
843     m_currentStream->m_stream->RegisterAudioCallback(pCallback);
844 }
845
846 void PAPlayer::UnRegisterAudioCallback()
847 {
848   CSharedLock lock(m_streamsLock);
849   /* only one stream should have the callback, but we do it to all just incase */
850   for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
851     if ((*itt)->m_stream)
852       (*itt)->m_stream->UnRegisterAudioCallback();
853   m_audioCallback = NULL;
854 }
855
856 void PAPlayer::OnNothingToQueueNotify()
857 {
858   m_isFinished = true;
859 }
860
861 bool PAPlayer::IsPlaying() const
862 {
863   return m_isPlaying;
864 }
865
866 bool PAPlayer::IsPaused() const
867 {
868   return m_isPaused;
869 }
870
871 void PAPlayer::Pause()
872 {
873   if (m_isPaused)
874   {
875     m_isPaused = false;
876     SoftStart();
877     m_callback.OnPlayBackResumed();
878   }
879   else
880   {
881     m_isPaused = true;    
882     SoftStop(true, false);
883     m_callback.OnPlayBackPaused();
884   }
885 }
886
887 void PAPlayer::SetVolume(float volume)
888 {
889
890 }
891
892 void PAPlayer::SetDynamicRangeCompression(long drc)
893 {
894
895 }
896
897 void PAPlayer::ToFFRW(int iSpeed)
898 {
899   m_playbackSpeed     = iSpeed;
900   m_signalSpeedChange = true;
901 }
902
903 int64_t PAPlayer::GetTimeInternal()
904 {
905   CSharedLock lock(m_streamsLock);
906   if (!m_currentStream)
907     return 0;
908
909   double time = ((double)m_currentStream->m_framesSent / (double)m_currentStream->m_sampleRate);
910   if (m_currentStream->m_stream)
911     time -= m_currentStream->m_stream->GetDelay();
912   time = time * 1000.0;
913
914   m_playerGUIData.m_time = (int64_t)time; //update for GUI
915
916   return (int64_t)time;
917 }
918
919 int64_t PAPlayer::GetTime()
920 {
921   return m_playerGUIData.m_time;
922 }
923
924 int64_t PAPlayer::GetTotalTime64()
925 {
926   CSharedLock lock(m_streamsLock);
927   if (!m_currentStream)
928     return 0;
929
930   int64_t total = m_currentStream->m_decoder.TotalTime();
931   if (m_currentStream->m_endOffset)
932     total = m_currentStream->m_endOffset;
933   total -= m_currentStream->m_startOffset;
934   return total;
935 }
936
937 int64_t PAPlayer::GetTotalTime()
938 {
939   return m_playerGUIData.m_totalTime;
940 }
941
942 int PAPlayer::GetCacheLevel() const
943 {
944   return m_playerGUIData.m_cacheLevel;
945 }
946
947 void PAPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
948 {
949   info.bitrate = m_playerGUIData.m_audioBitrate;
950   info.channels = m_playerGUIData.m_channelCount;
951   info.audioCodecName = m_playerGUIData.m_codec;
952   info.samplerate = m_playerGUIData.m_sampleRate;
953   info.bitspersample = m_playerGUIData.m_bitsPerSample;
954 }
955
956 bool PAPlayer::CanSeek()
957 {
958   return m_playerGUIData.m_canSeek;
959 }
960
961 void PAPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
962 {
963   if (!CanSeek()) return;
964
965   __int64 seek;
966   if (g_advancedSettings.m_musicUseTimeSeeking && GetTotalTime() > 2 * g_advancedSettings.m_musicTimeSeekForwardBig)
967   {
968     if (bLargeStep)
969       seek = bPlus ? g_advancedSettings.m_musicTimeSeekForwardBig : g_advancedSettings.m_musicTimeSeekBackwardBig;
970     else
971       seek = bPlus ? g_advancedSettings.m_musicTimeSeekForward : g_advancedSettings.m_musicTimeSeekBackward;
972     seek *= 1000;
973     seek += GetTime();
974   }
975   else
976   {
977     float percent;
978     if (bLargeStep)
979       percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForwardBig : (float)g_advancedSettings.m_musicPercentSeekBackwardBig;
980     else
981       percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForward : (float)g_advancedSettings.m_musicPercentSeekBackward;
982     seek = (__int64)(GetTotalTime64() * (GetPercentage() + percent) / 100);
983   }
984
985   SeekTime(seek);
986 }
987
988 void PAPlayer::SeekTime(int64_t iTime /*=0*/)
989 {
990   if (!CanSeek()) return;
991
992   CSharedLock lock(m_streamsLock);
993   if (!m_currentStream)
994     return;
995
996   int seekOffset = (int)(iTime - GetTimeInternal());
997
998   if (m_playbackSpeed != 1)
999     ToFFRW(1);
1000
1001   m_currentStream->m_seekFrame = (int)((float)m_currentStream->m_sampleRate * ((float)iTime + (float)m_currentStream->m_startOffset) / 1000.0f);
1002   m_callback.OnPlayBackSeek((int)iTime, seekOffset);
1003 }
1004
1005 void PAPlayer::SeekPercentage(float fPercent /*=0*/)
1006 {
1007   if (fPercent < 0.0f  ) fPercent = 0.0f;
1008   if (fPercent > 100.0f) fPercent = 100.0f;
1009   SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
1010 }
1011
1012 float PAPlayer::GetPercentage()
1013 {
1014   if (m_playerGUIData.m_totalTime > 0)
1015     return m_playerGUIData.m_time * 100.0f / m_playerGUIData.m_totalTime;
1016
1017   return 0.0f;
1018 }
1019
1020 bool PAPlayer::SkipNext()
1021 {
1022   return false;
1023 }
1024
1025 void PAPlayer::UpdateGUIData(StreamInfo *si)
1026 {
1027   /* Store data need by external threads in member
1028    * structure to prevent locking conflicts when
1029    * data required by GUI and main application
1030    */
1031   CSharedLock lock(m_streamsLock);
1032
1033   m_playerGUIData.m_sampleRate    = si->m_sampleRate;
1034   m_playerGUIData.m_bitsPerSample = si->m_bytesPerSample << 3;
1035   m_playerGUIData.m_channelCount  = si->m_channelInfo.Count();
1036   m_playerGUIData.m_canSeek       = si->m_decoder.CanSeek();
1037
1038   const ICodec* codec = si->m_decoder.GetCodec();
1039
1040   m_playerGUIData.m_audioBitrate = codec ? codec->m_Bitrate : 0;
1041   strncpy(m_playerGUIData.m_codec,codec ? codec->m_CodecName : "",20);
1042   m_playerGUIData.m_cacheLevel   = codec ? codec->GetCacheLevel() : 0;
1043
1044   int64_t total = si->m_decoder.TotalTime();
1045   if (si->m_endOffset)
1046     total = m_currentStream->m_endOffset;
1047   total -= m_currentStream->m_startOffset;
1048   m_playerGUIData.m_totalTime = total;
1049 }
1050
1051 void PAPlayer::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1052 {
1053   CExclusiveLock lock(m_streamsLock);
1054   m_jobCounter--;
1055   m_jobEvent.Set();
1056 }