fixed: dvdplayer's audio player could stall if you paused while it was dropping audio...
[xbmc:xbmc-antiquated.git] / xbmc / cores / dvdplayer / DVDPlayerAudio.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21  
22 #include "stdafx.h"
23 #include "DVDPlayerAudio.h"
24 #include "DVDCodecs/Audio/DVDAudioCodec.h"
25 #include "DVDCodecs/DVDCodecs.h"
26 #include "DVDCodecs/DVDFactoryCodec.h"
27 #include "DVDPerformanceCounter.h"
28 #include <sstream>
29 #include <iomanip>
30
31 using namespace std;
32
33 CPTSOutputQueue::CPTSOutputQueue()
34 {
35   Flush();
36 }
37
38 void CPTSOutputQueue::Add(double pts, double delay, double duration)
39 {
40   CSingleLock lock(m_sync);
41   
42   TPTSItem item;
43   item.pts = pts;
44   item.timestamp = CDVDClock::GetAbsoluteClock() + delay;
45   item.duration = duration;
46
47   // first one is applied directly
48   if(m_queue.empty() && m_current.pts == DVD_NOPTS_VALUE)
49     m_current = item;
50   else
51     m_queue.push(item);
52
53   // call function to make sure the queue 
54   // doesn't grow should nobody call it
55   Current();
56 }
57 void CPTSOutputQueue::Flush()
58 {
59   CSingleLock lock(m_sync);
60   
61   while( !m_queue.empty() ) m_queue.pop();
62   m_current.pts = DVD_NOPTS_VALUE;
63   m_current.timestamp = 0.0;
64   m_current.duration = 0.0;
65 }
66
67 double CPTSOutputQueue::Current()
68 {
69   CSingleLock lock(m_sync);
70
71   if(!m_queue.empty() && m_current.pts == DVD_NOPTS_VALUE)
72   {
73     m_current = m_queue.front();
74     m_queue.pop();
75   }
76
77   while( !m_queue.empty() && CDVDClock::GetAbsoluteClock() >= m_queue.front().timestamp )
78   {
79     m_current = m_queue.front();
80     m_queue.pop();
81   }
82
83   if( m_current.timestamp == 0 ) return m_current.pts;
84
85   return m_current.pts + min(m_current.duration, (CDVDClock::GetAbsoluteClock() - m_current.timestamp));
86 }  
87
88 void CPTSInputQueue::Add(__int64 bytes, double pts)
89 {
90   CSingleLock lock(m_sync);
91   
92   m_list.insert(m_list.begin(), make_pair(bytes, pts));
93 }
94
95 void CPTSInputQueue::Flush()
96 {
97   CSingleLock lock(m_sync);
98   
99   m_list.clear();
100 }
101 double CPTSInputQueue::Get(__int64 bytes, bool consume)
102 {
103   CSingleLock lock(m_sync);
104   
105   IT it = m_list.begin();
106   for(; it != m_list.end(); it++)
107   {
108     if(bytes <= it->first)
109     {
110       double pts = it->second;
111       if(consume)
112       {
113         it->second = DVD_NOPTS_VALUE;
114         m_list.erase(++it, m_list.end());        
115       }
116       return pts; 
117     }
118     bytes -= it->first;
119   }
120   return DVD_NOPTS_VALUE;
121 }
122
123 CDVDPlayerAudio::CDVDPlayerAudio(CDVDClock* pClock) 
124 : CThread()
125 , m_dvdAudio((bool&)m_bStop)
126 {
127   m_pClock = pClock;
128   m_pAudioCodec = NULL;  
129   m_audioClock = 0;
130   m_droptime = 0;
131   m_speed = DVD_PLAYSPEED_NORMAL;
132   m_stalled = false;
133
134   InitializeCriticalSection(&m_critCodecSection);
135   m_messageQueue.SetMaxDataSize(10 * 16 * 1024);
136   g_dvdPerformanceCounter.EnableAudioQueue(&m_messageQueue);
137 }
138
139 CDVDPlayerAudio::~CDVDPlayerAudio()
140 {
141   StopThread();
142   g_dvdPerformanceCounter.DisableAudioQueue();
143
144   // close the stream, and don't wait for the audio to be finished
145   // CloseStream(true);
146   DeleteCriticalSection(&m_critCodecSection);
147 }
148
149 bool CDVDPlayerAudio::OpenStream( CDVDStreamInfo &hints )                                 
150 {
151   // should alway's be NULL!!!!, it will probably crash anyway when deleting m_pAudioCodec here.
152   if (m_pAudioCodec)
153   {
154     CLog::Log(LOGFATAL, "CDVDPlayerAudio::OpenStream() m_pAudioCodec != NULL");
155     return false;
156   }
157
158   /* try to open decoder without probing, we could actually allow us to continue here */
159   if( !OpenDecoder(hints) ) return false;
160
161   m_messageQueue.Init();
162
163   m_droptime = 0;
164   m_audioClock = 0;
165   m_stalled = false;
166
167   CLog::Log(LOGNOTICE, "Creating audio thread");
168   Create();
169
170   return true;
171 }
172
173 void CDVDPlayerAudio::CloseStream(bool bWaitForBuffers)
174 {
175   // wait until buffers are empty
176   if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
177
178   // send abort message to the audio queue
179   m_messageQueue.Abort();
180
181   CLog::Log(LOGNOTICE, "waiting for audio thread to exit");
182
183   // shut down the adio_decode thread and wait for it
184   StopThread(); // will set this->m_bStop to true
185   this->WaitForThreadExit(INFINITE);
186
187   // uninit queue
188   m_messageQueue.End();
189
190   CLog::Log(LOGNOTICE, "Deleting audio codec");
191   if (m_pAudioCodec)
192   {
193     m_pAudioCodec->Dispose();
194     delete m_pAudioCodec;
195     m_pAudioCodec = NULL;
196   }
197
198   // flush any remaining pts values
199   m_ptsOutput.Flush();
200 }
201
202 bool CDVDPlayerAudio::OpenDecoder(CDVDStreamInfo &hints, BYTE* buffer /* = NULL*/, unsigned int size /* = 0*/)
203 {
204   EnterCriticalSection(&m_critCodecSection);
205
206   /* close current audio codec */
207   if( m_pAudioCodec )
208   {
209     CLog::Log(LOGNOTICE, "Deleting audio codec");
210     m_pAudioCodec->Dispose();
211     SAFE_DELETE(m_pAudioCodec);
212   }
213
214   /* store our stream hints */
215   m_streaminfo = hints;
216
217   CLog::Log(LOGNOTICE, "Finding audio codec for: %i", m_streaminfo.codec);
218   m_pAudioCodec = CDVDFactoryCodec::CreateAudioCodec( m_streaminfo );
219   if( !m_pAudioCodec )
220   {
221     CLog::Log(LOGERROR, "Unsupported audio codec");
222
223     m_streaminfo.Clear();
224     LeaveCriticalSection(&m_critCodecSection);
225     return false;
226   }
227
228   /* update codec information from what codec gave ut */  
229   m_streaminfo.channels = m_pAudioCodec->GetChannels();
230   m_streaminfo.samplerate = m_pAudioCodec->GetSampleRate();
231   
232   LeaveCriticalSection(&m_critCodecSection);
233
234   return true;
235 }
236
237 // decode one audio frame and returns its uncompressed size
238 int CDVDPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe, bool bDropPacket)
239 {
240   int result = 0;
241   int datatimeout = 1000;
242
243   // make sure the sent frame is clean
244   memset(&audioframe, 0, sizeof(DVDAudioFrame));
245
246   while (!m_bStop)
247   {
248     /* NOTE: the audio packet can contain several frames */
249     while( !m_bStop && m_decode.size > 0 )
250     {
251       if( !m_pAudioCodec ) 
252         return DECODE_FLAG_ERROR;
253
254       /* the packet dts refers to the first audioframe that starts in the packet */      
255       double dts = m_ptsInput.Get(m_decode.size + m_pAudioCodec->GetBufferSize(), true);
256       if (dts != DVD_NOPTS_VALUE)
257         m_audioClock = dts;
258
259       int len = m_pAudioCodec->Decode(m_decode.data, m_decode.size);
260       if (len < 0)
261       {
262         /* if error, we skip the packet */
263         CLog::Log(LOGERROR, "CDVDPlayerAudio::DecodeFrame - Decode Error. Skipping audio packet");
264         m_decode.Release();
265         m_pAudioCodec->Reset();
266         return DECODE_FLAG_ERROR;
267       }
268
269       // fix for fucked up decoders
270       if( len > m_decode.size )
271       {        
272         CLog::Log(LOGERROR, "CDVDPlayerAudio:DecodeFrame - Codec tried to consume more data than available. Potential memory corruption");        
273         m_decode.Release();
274         m_pAudioCodec->Reset();
275         assert(0);
276       }
277
278       m_decode.data += len;
279       m_decode.size -= len;
280
281
282       // get decoded data and the size of it
283       audioframe.size = m_pAudioCodec->GetData(&audioframe.data);
284       audioframe.pts = m_audioClock;
285       audioframe.channels = m_pAudioCodec->GetChannels();
286       audioframe.bits_per_sample = m_pAudioCodec->GetBitsPerSample();
287       audioframe.sample_rate = m_pAudioCodec->GetSampleRate();
288       audioframe.passthrough = m_pAudioCodec->NeedPasstrough();
289
290       if (audioframe.size <= 0) 
291         continue;
292
293       // compute duration.
294       int n = (audioframe.channels * audioframe.bits_per_sample * audioframe.sample_rate)>>3;
295       if (n > 0)
296       {
297         // safety check, if channels == 0, n will result in 0, and that will result in a nice devide exception
298         audioframe.duration = ((double)audioframe.size * DVD_TIME_BASE) / n;
299
300         // increase audioclock to after the packet
301         m_audioClock += audioframe.duration;
302         datatimeout = (unsigned int)(audioframe.duration*2.0);
303       }
304
305       // if demux source want's us to not display this, continue
306       if(m_decode.msg->GetPacketDrop())
307         continue;
308
309       //If we are asked to drop this packet, return a size of zero. then it won't be played
310       //we currently still decode the audio.. this is needed since we still need to know it's 
311       //duration to make sure clock is updated correctly.
312       if( bDropPacket )
313         result |= DECODE_FLAG_DROP;
314
315       return result;
316     }
317     // free the current packet
318     m_decode.Release();
319
320     if (m_messageQueue.RecievedAbortRequest()) return DECODE_FLAG_ABORT;
321     
322     // read next packet and return -1 on error
323     LeaveCriticalSection(&m_critCodecSection); //Leave here as this might stall a while
324     
325     CDVDMsg* pMsg;
326     MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, datatimeout);
327     
328     EnterCriticalSection(&m_critCodecSection);
329     
330     if (ret == MSGQ_TIMEOUT) 
331     {
332       m_stalled = true;
333       continue;
334     }
335
336     if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) 
337       return DECODE_FLAG_ABORT;
338
339     if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
340     {
341       m_stalled = false;
342       m_decode.Attach((CDVDMsgDemuxerPacket*)pMsg);
343       m_ptsInput.Add( m_decode.size, m_decode.dts );
344     }
345     else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
346     {
347       CDVDMsgGeneralStreamChange* pMsgStreamChange = (CDVDMsgGeneralStreamChange*)pMsg;
348       CDVDStreamInfo* hints = pMsgStreamChange->GetStreamInfo();
349
350       /* recieved a stream change, reopen codec. */
351       /* we should really not do this untill first packet arrives, to have a probe buffer */      
352
353       /* try to open decoder, if none is found keep consuming packets */
354       OpenDecoder( *hints );
355
356     }
357     else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
358     {
359       ((CDVDMsgGeneralSynchronize*)pMsg)->Wait( &m_bStop, SYNCSOURCE_AUDIO );
360       CLog::Log(LOGDEBUG, "CDVDPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE");
361     } 
362     else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
363     { //player asked us to set internal clock
364       CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
365
366       if (pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
367         m_audioClock = pMsgGeneralResync->m_timestamp;
368
369       if (pMsgGeneralResync->m_clock)
370       {
371         m_ptsOutput.Add(m_audioClock, m_dvdAudio.GetDelay(), 0);
372         m_pClock->Discontinuity(CLOCK_DISC_NORMAL, m_ptsOutput.Current(), 0);
373       }
374     }
375     else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
376     {
377       m_dvdAudio.Flush();
378       m_ptsOutput.Flush();
379       m_ptsInput.Flush();
380
381       if (m_pAudioCodec)
382         m_pAudioCodec->Reset();
383
384       m_decode.Release();
385     }
386     pMsg->Release();
387   }
388   return 0;
389 }
390
391 void CDVDPlayerAudio::OnStartup()
392 {
393   CThread::SetName("CDVDPlayerAudio");
394
395   m_decode.msg = NULL;
396   m_decode.Release();
397
398   g_dvdPerformanceCounter.EnableAudioDecodePerformance(ThreadHandle());
399 }
400
401 void CDVDPlayerAudio::Process()
402 {
403   CLog::Log(LOGNOTICE, "running thread: CDVDPlayerAudio::Process()");
404
405   int result;
406  
407   DVDAudioFrame audioframe;
408
409   while (!m_bStop)
410   {
411     //make sure player doesn't keep processing data while paused
412     while (!m_bStop && m_speed == DVD_PLAYSPEED_PAUSE && !m_messageQueue.RecievedAbortRequest()) Sleep(5);
413
414     //Don't let anybody mess with our global variables
415     EnterCriticalSection(&m_critCodecSection);
416     result = DecodeFrame(audioframe, m_speed != DVD_PLAYSPEED_NORMAL); // blocks if no audio is available, but leaves critical section before doing so
417     LeaveCriticalSection(&m_critCodecSection);
418
419     if( result & DECODE_FLAG_ERROR ) 
420     { 
421       CLog::Log(LOGDEBUG, "CDVDPlayerAudio::Process - Decode Error");
422       continue;
423     }
424
425     if( result & DECODE_FLAG_ABORT )
426     {
427       CLog::Log(LOGDEBUG, "CDVDPlayerAudio::Process - Abort recieved, exiting thread");
428       break;
429     }
430
431 #ifdef PROFILE /* during profiling we just drop all packets, after having decoded */
432     m_pClock->Discontinuity(CLOCK_DISC_NORMAL, audioframe.pts, 0);
433     continue;
434 #endif
435     
436     if( audioframe.size == 0 )
437       continue;
438
439     // we have succesfully decoded an audio frame, setup renderer to match
440     if (!m_dvdAudio.IsValidFormat(audioframe))
441     {
442       m_dvdAudio.Destroy();
443       if(!m_dvdAudio.Create(audioframe, m_streaminfo.codec))
444         CLog::Log(LOGERROR, "%s - failed to create audio renderer", __FUNCTION__);
445     }
446
447     if( result & DECODE_FLAG_DROP )
448     {
449       //frame should be dropped. Don't let audio move ahead of the current time thou
450       //we need to be able to start playing at any time
451       //when playing backwords, we try to keep as small buffers as possible
452
453       if(m_droptime == 0.0)
454         m_droptime = m_pClock->GetAbsoluteClock();
455       if(m_speed > 0)
456         m_droptime += audioframe.duration * DVD_PLAYSPEED_NORMAL / m_speed;
457       while( !m_bStop && m_droptime > m_pClock->GetAbsoluteClock() ) Sleep(1);
458     } 
459     else
460     {
461       m_droptime = 0.0;
462
463       // add any packets play
464       m_dvdAudio.AddPackets(audioframe);
465     }
466
467     // store the delay for this pts value so we can calculate the current playing
468     if(m_speed != DVD_PLAYSPEED_PAUSE)
469       m_ptsOutput.Add(audioframe.pts, m_dvdAudio.GetDelay() - audioframe.duration, audioframe.duration);
470
471     // don't try to fix a desynced clock, until we played out the full audio buffer
472     if( fabs(m_pClock->DistanceToDisc()) < m_dvdAudio.GetDelay() )
473       continue;
474
475     if( m_ptsOutput.Current() == DVD_NOPTS_VALUE )
476       continue;
477
478     if( m_speed != DVD_PLAYSPEED_NORMAL )
479       continue;
480
481     double clock = m_pClock->GetClock();
482     double error = m_ptsOutput.Current() - clock;
483
484     if( fabs(error) > DVD_MSEC_TO_TIME(10) )
485     {
486       m_pClock->Discontinuity(CLOCK_DISC_NORMAL, clock+error, 0);
487       if(m_speed == DVD_PLAYSPEED_NORMAL)
488         CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Discontinuty - was:%f, should be:%f, error:%f", clock, clock+error, error);
489     }
490   }
491 }
492
493 void CDVDPlayerAudio::OnExit()
494 {
495   g_dvdPerformanceCounter.DisableAudioDecodePerformance();
496   
497   // destroy audio device
498   CLog::Log(LOGNOTICE, "Closing audio device");
499   m_dvdAudio.Destroy();
500
501   CLog::Log(LOGNOTICE, "thread end: CDVDPlayerAudio::OnExit()");
502 }
503
504 void CDVDPlayerAudio::SetSpeed(int speed)
505
506   m_speed = speed;
507   
508   if (m_speed == DVD_PLAYSPEED_PAUSE)
509   {
510     m_ptsOutput.Flush();
511     m_dvdAudio.Pause();
512   }
513   else 
514     m_dvdAudio.Resume();
515 }
516
517 void CDVDPlayerAudio::Flush()
518 {
519   m_messageQueue.Flush();
520   m_messageQueue.Put( new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
521 }
522
523 void CDVDPlayerAudio::WaitForBuffers()
524 {
525   // make sure there are no more packets available
526   m_messageQueue.WaitUntilEmpty();
527   
528   // make sure almost all has been rendered
529   // leave 500ms to avound buffer underruns
530
531   while( m_dvdAudio.GetDelay() > DVD_TIME_BASE/2 )
532   {
533     Sleep(5);
534   }
535 }
536
537 string CDVDPlayerAudio::GetPlayerInfo()
538 {
539   std::ostringstream s;
540   s << "aq:" << std::setw(3) << std::left << min(100,100 * m_messageQueue.GetDataSize() / m_messageQueue.GetMaxDataSize()) << "%";
541   s << ", ";
542   s << "cpu: " << (int)(100 * CThread::GetRelativeUsage()) << "%";
543   return s.str();
544 }
545