[XBOX] fixed: Weird A/V desync at start (again)
[xbmc:xbmc-antiquated.git] / xbmc / cores / dvdplayer / DVDPlayerVideo.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 "AdvancedSettings.h"
24 #include "GUISettings.h"
25 #include "Settings.h"
26 #include "DVDPlayer.h"
27 #include "DVDPlayerVideo.h"
28 #include "DVDCodecs/DVDFactoryCodec.h"
29 #include "DVDCodecs/DVDCodecUtils.h"
30 #include "DVDCodecs/Video/DVDVideoPPFFmpeg.h"
31 #include "DVDDemuxers/DVDDemux.h"
32 #include "DVDDemuxers/DVDDemuxUtils.h"
33 #include "../../Util.h"
34 #include "DVDOverlayRenderer.h"
35 #include "DVDPerformanceCounter.h"
36 #include "DVDCodecs/DVDCodecs.h"
37 #include "DVDCodecs/Overlay/DVDOverlayCodecCC.h"
38 #include "DVDCodecs/Overlay/DVDOverlaySSA.h"
39 #include <sstream>
40 #include <iomanip>
41
42 using namespace std;
43
44 CDVDPlayerVideo::CDVDPlayerVideo( CDVDClock* pClock
45                                 , CDVDOverlayContainer* pOverlayContainer
46                                 , CDVDMessageQueue& parent)
47 : CThread()
48 , m_messageQueue("video")
49 , m_messageParent(parent)
50 {
51   m_pClock = pClock;
52   m_pOverlayContainer = pOverlayContainer;
53   m_pTempOverlayPicture = NULL;
54   m_pVideoCodec = NULL;
55   m_pOverlayCodecCC = NULL;
56   m_speed = DVD_PLAYSPEED_NORMAL;
57
58   m_bRenderSubs = false;
59   m_stalled = false;
60   m_started = false;
61   m_iVideoDelay = 0;
62   m_iSubtitleDelay = 0;
63   m_fForcedAspectRatio = 0;
64   m_iNrOfPicturesNotToSkip = 0;
65 #ifdef _XBOX
66   m_messageQueue.SetMaxDataSize(10 * 256 * 1024);
67 #else
68   m_messageQueue.SetMaxDataSize(8 * 1024 * 1024);
69 #endif
70   m_messageQueue.SetMaxTimeSize(8.0);
71   g_dvdPerformanceCounter.EnableVideoQueue(&m_messageQueue);
72
73   m_iCurrentPts = DVD_NOPTS_VALUE;
74   m_iDroppedFrames = 0;
75   m_fFrameRate = 25;
76   m_bAllowFullscreen = false;
77   memset(&m_output, 0, sizeof(m_output));
78 }
79
80 CDVDPlayerVideo::~CDVDPlayerVideo()
81 {
82   StopThread();
83   g_dvdPerformanceCounter.DisableVideoQueue();
84   
85 #ifdef HAS_VIDEO_PLAYBACK 
86   if(m_output.inited) 
87   { 
88     CLog::Log(LOGNOTICE, "%s - uninitting video device", __FUNCTION__); 
89     g_renderManager.UnInit(); 
90   } 
91 #endif 
92 }
93
94 double CDVDPlayerVideo::GetOutputDelay()
95 {
96     double time = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET);
97     if( m_fFrameRate )
98       time = (time * DVD_TIME_BASE) / m_fFrameRate;
99     else
100       time = 0.0;
101
102     if( m_speed != 0 )
103       time = time * DVD_PLAYSPEED_NORMAL / abs(m_speed);
104
105     return time;
106 }
107
108 bool CDVDPlayerVideo::OpenStream( CDVDStreamInfo &hint )
109 {
110
111   if (hint.fpsrate && hint.fpsscale)
112   {
113     m_fFrameRate = (float)hint.fpsrate / hint.fpsscale;
114     m_autosync = 10;
115   }
116   else
117   {
118     m_fFrameRate = 25;
119     m_autosync = 1; // avoid using frame time as we don't know it accurate
120   }
121
122   m_iDroppedRequest = 0;
123   m_iLateFrames = 0;
124
125   if (hint.vfr)
126     m_autosync = 1;
127
128   if( m_fFrameRate > 100 || m_fFrameRate < 5 )
129   {
130     CLog::Log(LOGERROR, "CDVDPlayerVideo::OpenStream - Invalid framerate %d, using forced 25fps and just trust timestamps", (int)m_fFrameRate);
131     m_fFrameRate = 25;
132     m_autosync = 1; // avoid using frame time as we don't know it accurate
133   }
134
135   // use aspect in stream if available
136   m_fForcedAspectRatio = hint.aspect;
137
138   // should alway's be NULL!!!!, it will probably crash anyway when deleting m_pVideoCodec here.
139   if (m_pVideoCodec)
140   {
141     CLog::Log(LOGFATAL, "CDVDPlayerVideo::OpenStream() m_pVideoCodec != NULL");
142     return false;
143   }
144
145   CLog::Log(LOGNOTICE, "Creating video codec with codec id: %i", hint.codec);
146   m_pVideoCodec = CDVDFactoryCodec::CreateVideoCodec( hint );
147
148   if( !m_pVideoCodec )
149   {
150     CLog::Log(LOGERROR, "Unsupported video codec");
151     return false;
152   }
153
154   m_hints   = hint;
155   m_stalled = false;
156   m_started = false;
157   m_codecname = m_pVideoCodec->GetName();
158
159   m_messageQueue.Init();
160
161   CLog::Log(LOGNOTICE, "Creating video thread");
162   Create();
163
164   return true;
165 }
166
167 void CDVDPlayerVideo::CloseStream(bool bWaitForBuffers)
168 {
169   // wait until buffers are empty
170   if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
171
172   m_messageQueue.Abort();
173
174   // wait for decode_video thread to end
175   CLog::Log(LOGNOTICE, "waiting for video thread to exit");
176
177   StopThread(); // will set this->m_bStop to true
178
179   m_messageQueue.End();
180
181   CLog::Log(LOGNOTICE, "deleting video codec");
182   if (m_pVideoCodec)
183   {
184     m_pVideoCodec->Dispose();
185     delete m_pVideoCodec;
186     m_pVideoCodec = NULL;
187   }
188
189   if (m_pTempOverlayPicture)
190   {
191     CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
192     m_pTempOverlayPicture = NULL;
193   }
194 }
195
196 void CDVDPlayerVideo::OnStartup()
197 {
198   CThread::SetName("CDVDPlayerVideo");
199   m_iDroppedFrames = 0;
200   
201   m_iCurrentPts = DVD_NOPTS_VALUE;
202   m_FlipTimeStamp = m_pClock->GetAbsoluteClock();
203
204 #ifdef HAS_VIDEO_PLAYBACK
205   if(!m_output.inited)
206   {
207     g_renderManager.PreInit();
208     m_output.inited = true;
209   }
210 #endif
211   g_dvdPerformanceCounter.EnableVideoDecodePerformance(ThreadHandle());
212 }
213
214 void CDVDPlayerVideo::Process()
215 {
216   CLog::Log(LOGNOTICE, "running thread: video_thread");
217
218   DVDVideoPicture picture;
219   CDVDVideoPPFFmpeg mDeinterlace(g_advancedSettings.m_videoPPFFmpegType);
220
221   memset(&picture, 0, sizeof(DVDVideoPicture));
222
223   double pts = 0;
224   double frametime = (double)DVD_TIME_BASE / m_fFrameRate;
225
226   int iDropped = 0; //frames dropped in a row
227   bool bRequestDrop = false;
228
229   m_videoStats.Start();
230
231   while (!m_bStop)
232   {
233     int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000;
234     int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE /* && m_started */) ? 1 : 0;
235
236     CDVDMsg* pMsg;
237     MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority);
238
239     if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT)
240     {
241       CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true");
242       break;
243     }
244     else if (ret == MSGQ_TIMEOUT)
245     {
246       // if we only wanted priority messages, this isn't a stall
247       if( iPriority )
248         continue;
249
250       //Okey, start rendering at stream fps now instead, we are likely in a stillframe
251       if( !m_stalled )
252       {
253         if(m_started)
254           CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate);
255         m_stalled = true;
256         pts+= frametime*4;
257       }
258
259       //Waiting timed out, output last picture
260       if( picture.iFlags & DVP_FLAG_ALLOCATED )
261       {
262         //Remove interlaced flag before outputting
263         //no need to output this as if it was interlaced
264         picture.iFlags &= ~DVP_FLAG_INTERLACED;
265         picture.iFlags |= DVP_FLAG_NOSKIP;
266         OutputPicture(&picture, pts);
267         pts+= frametime;
268       }
269
270       continue;
271     }
272
273     if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
274     {
275       ((CDVDMsgGeneralSynchronize*)pMsg)->Wait( &m_bStop, SYNCSOURCE_VIDEO );
276       CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE");
277       pMsg->Release();
278
279       /* we may be very much off correct pts here, but next picture may be a still*/
280       /* make sure it isn't dropped */
281       m_iNrOfPicturesNotToSkip = 5;
282       continue;
283     }
284     else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
285     {
286       CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
287
288       if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
289         pts = pMsgGeneralResync->m_timestamp;
290
291       double delay = m_FlipTimeStamp - m_pClock->GetAbsoluteClock();
292       if( delay > frametime ) delay = frametime;
293       else if( delay < 0 )    delay = 0;
294
295       if(pMsgGeneralResync->m_clock)
296       {
297         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts);
298         m_pClock->Discontinuity(CLOCK_DISC_NORMAL, pts, delay);
299       }
300       else
301         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts);
302
303       pMsgGeneralResync->Release();
304       continue;
305     }
306     else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
307     {
308       if (m_speed != DVD_PLAYSPEED_PAUSE)
309       {
310         double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
311
312         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout);
313
314         timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed);
315         timeout += CDVDClock::GetAbsoluteClock();
316
317         while(!m_bStop && CDVDClock::GetAbsoluteClock() < timeout)
318           Sleep(1);
319       }
320     }
321     else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT))
322     {
323       CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT");
324       m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg);
325     }
326     else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
327     {
328       if(m_pVideoCodec)
329         m_pVideoCodec->Reset();
330       m_started = false;
331     }
332     else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush())
333     {
334       if(m_pVideoCodec)
335         m_pVideoCodec->Reset();
336       m_stalled = true;
337       m_started = false;
338     }
339     else if (pMsg->IsType(CDVDMsg::VIDEO_NOSKIP))
340     {
341       // libmpeg2 is also returning incomplete frames after a dvd cell change
342       // so the first few pictures are not the correct ones to display in some cases
343       // just display those together with the correct one.
344       // (setting it to 2 will skip some menu stills, 5 is working ok for me).
345       m_iNrOfPicturesNotToSkip = 5;
346     }
347     else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
348     {
349       m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
350       if(m_speed == DVD_PLAYSPEED_PAUSE)
351         m_iNrOfPicturesNotToSkip = 0;
352     }
353     else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
354     {
355       if(m_started)
356         m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
357     }
358
359     if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
360     {
361       DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
362       bool bPacketDrop     = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
363
364       if (m_stalled)
365       {
366         CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe left, switching to normal playback");
367         m_stalled = false;
368
369         //don't allow the first frames after a still to be dropped
370         //sometimes we get multiple stills (long duration frames) after each other
371         //in normal mpegs
372         m_iNrOfPicturesNotToSkip = 5;
373       }
374       else if( iDropped*frametime > DVD_MSEC_TO_TIME(100) )
375       { // if we dropped too many pictures in a row, insert a forced picture
376         m_iNrOfPicturesNotToSkip++;
377       }
378
379 #ifdef PROFILE
380       bRequestDrop = false;
381 #else
382       if (m_iNrOfPicturesNotToSkip > 0) bRequestDrop = false;
383       if (m_speed < 0)                  bRequestDrop = false;
384 #endif
385
386       // if player want's us to drop this packet, do so nomatter what
387       if(bPacketDrop)
388         bRequestDrop = true;
389
390       // tell codec if next frame should be dropped
391       // problem here, if one packet contains more than one frame
392       // both frames will be dropped in that case instead of just the first
393       // decoder still needs to provide an empty image structure, with correct flags
394       m_pVideoCodec->SetDropState(bRequestDrop);
395
396       int iDecoderState = m_pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts);
397       m_videoStats.AddSampleBytes(pPacket->iSize);
398       // assume decoder dropped a picture if it didn't give us any
399       // picture from a demux packet, this should be reasonable
400       // for libavformat as a demuxer as it normally packetizes
401       // pictures when they come from demuxer
402       if(bRequestDrop && !bPacketDrop && (iDecoderState & VC_BUFFER) && !(iDecoderState & VC_PICTURE))
403       {
404         m_iDroppedFrames++;
405         iDropped++;
406       }
407
408       // loop while no error
409       while (!m_bStop)
410       {
411
412         // if decoder was flushed, we need to seek back again to resume rendering
413         if (iDecoderState & VC_FLUSHED)
414         {
415           CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder was flushed");
416           m_messageParent.Put(new CDVDMsgPlayerSeek(pts/1000, true, true, true));
417           m_pVideoCodec->Reset();
418           break;
419         }
420
421         // if decoder had an error, tell it to reset to avoid more problems
422         if (iDecoderState & VC_ERROR)
423         {
424           CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder returned error");
425           break;
426         }
427
428         // check for a new picture
429         if (iDecoderState & VC_PICTURE)
430         {
431
432           // try to retrieve the picture (should never fail!), unless there is a demuxer bug ofcours
433           memset(&picture, 0, sizeof(DVDVideoPicture));
434           if (m_pVideoCodec->GetPicture(&picture))
435           {
436             picture.iGroupId = pPacket->iGroupId;
437
438             if(picture.iDuration == 0)
439               picture.iDuration = frametime;
440
441             if(bPacketDrop)
442               picture.iFlags |= DVP_FLAG_DROPPED;
443
444             if (m_iNrOfPicturesNotToSkip > 0)
445             {
446               picture.iFlags |= DVP_FLAG_NOSKIP;
447               m_iNrOfPicturesNotToSkip--;
448             }
449
450             // validate picture timing, 
451             // if both dts/pts invalid, use pts calulated from picture.iDuration
452             // if pts invalid use dts, else use picture.pts as passed
453             if (picture.dts == DVD_NOPTS_VALUE && picture.pts == DVD_NOPTS_VALUE)
454               picture.pts = pts;
455             else if (picture.pts == DVD_NOPTS_VALUE)
456               picture.pts = picture.dts;
457
458             /* use forced aspect if any */
459             if( m_fForcedAspectRatio != 0.0f )
460               picture.iDisplayWidth = (int) (picture.iDisplayHeight * m_fForcedAspectRatio);
461
462             //Deinterlace if codec said format was interlaced or if we have selected we want to deinterlace
463             //this video
464             EINTERLACEMETHOD mInt = g_stSettings.m_currentVideoSettings.m_InterlaceMethod;
465             if( mInt == VS_INTERLACEMETHOD_DEINTERLACE )
466             {
467               if(mDeinterlace.Process(&picture))
468                 mDeinterlace.GetPicture(&picture);
469             }
470             else if( mInt == VS_INTERLACEMETHOD_RENDER_WEAVE || mInt == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED )
471             {
472               /* if we are syncing frames, dvdplayer will be forced to play at a given framerate */
473               /* unless we directly sync to the correct pts, we won't get a/v sync as video can never catch up */
474               picture.iFlags |= DVP_FLAG_NOAUTOSYNC;
475             }
476
477             /* if frame has a pts (usually originiating from demux packet), use that */
478             if(picture.pts != DVD_NOPTS_VALUE)
479               pts = picture.pts;
480
481             if (picture.iRepeatPicture)
482               picture.iDuration *= picture.iRepeatPicture + 1;
483
484             int iResult;
485             try 
486             {
487               iResult = OutputPicture(&picture, pts);
488             }
489             catch (...)
490             {
491               CLog::Log(LOGERROR, "%s - Exception caught when outputing picture", __FUNCTION__);
492               iResult = EOS_ABORT;
493             }
494
495
496
497             if(m_started == false)
498             {
499               m_started = true;
500               m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
501             }
502
503             // guess next frame pts. iDuration is always valid
504             if (m_speed != 0 )
505               pts += picture.iDuration * m_speed / abs(m_speed);
506
507             if( iResult & EOS_ABORT )
508             {
509               //if we break here and we directly try to decode again wihout
510               //flushing the video codec things break for some reason
511               //i think the decoder (libmpeg2 atleast) still has a pointer
512               //to the data, and when the packet is freed that will fail.
513               iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE);
514               break;
515             }
516
517             if( (iResult & EOS_DROPPED) && !bPacketDrop )
518             {
519               m_iDroppedFrames++;
520               iDropped++;
521             }
522             else
523               iDropped = 0;
524
525             bRequestDrop = (iResult & EOS_VERYLATE) == EOS_VERYLATE;
526           }
527           else
528           {
529             CLog::Log(LOGWARNING, "Decoder Error getting videoPicture.");
530             m_pVideoCodec->Reset();
531           }
532         }
533
534         /*
535         if (iDecoderState & VC_USERDATA)
536         {
537           // found some userdata while decoding a frame
538           // could be closed captioning
539           DVDVideoUserData videoUserData;
540           if (m_pVideoCodec->GetUserData(&videoUserData))
541           {
542             ProcessVideoUserData(&videoUserData, pts);
543           }
544         }
545         */
546
547         // if the decoder needs more data, we just break this loop
548         // and try to get more data from the videoQueue
549         if (iDecoderState & VC_BUFFER)
550           break;
551
552         // the decoder didn't need more data, flush the remaning buffer
553         iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE);
554       }
555     }
556
557     // all data is used by the decoder, we can safely free it now
558     pMsg->Release();
559   }
560 }
561
562 void CDVDPlayerVideo::OnExit()
563 {
564   g_dvdPerformanceCounter.DisableVideoDecodePerformance();
565
566   if (m_pOverlayCodecCC)
567   {
568     m_pOverlayCodecCC->Dispose();
569     m_pOverlayCodecCC = NULL;
570   }
571
572   CLog::Log(LOGNOTICE, "thread end: video_thread");
573 }
574
575 void CDVDPlayerVideo::ProcessVideoUserData(DVDVideoUserData* pVideoUserData, double pts)
576 {
577   // check userdata type
578   BYTE* data = pVideoUserData->data;
579   int size = pVideoUserData->size;
580
581   if (size >= 2)
582   {
583     if (data[0] == 'C' && data[1] == 'C')
584     {
585       data += 2;
586       size -= 2;
587
588       // closed captioning
589       if (!m_pOverlayCodecCC)
590       {
591         m_pOverlayCodecCC = new CDVDOverlayCodecCC();
592         CDVDCodecOptions options;
593         CDVDStreamInfo info;
594         if (!m_pOverlayCodecCC->Open(info, options))
595         {
596           delete m_pOverlayCodecCC;
597           m_pOverlayCodecCC = NULL;
598         }
599       }
600
601       if (m_pOverlayCodecCC)
602       {
603         m_pOverlayCodecCC->Decode(data, size, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE);
604
605         CDVDOverlay* overlay;
606         while((overlay = m_pOverlayCodecCC->GetOverlay()) != NULL)
607         {
608           overlay->iGroupId = 0;
609           overlay->iPTSStartTime += pts;
610           if(overlay->iPTSStopTime != 0.0)
611             overlay->iPTSStopTime += pts;
612
613           m_pOverlayContainer->Add(overlay);
614           overlay->Release();
615         }
616       }
617     }
618   }
619 }
620
621 bool CDVDPlayerVideo::InitializedOutputDevice()
622 {
623 #ifdef HAS_VIDEO_PLAYBACK
624   return g_renderManager.IsStarted();
625 #else
626   return false;
627 #endif
628 }
629
630 void CDVDPlayerVideo::SetSpeed(int speed)
631 {
632   if(m_messageQueue.IsInited())
633     m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
634   else
635     m_speed = speed;
636 }
637
638 void CDVDPlayerVideo::StepFrame()
639 {
640   m_iNrOfPicturesNotToSkip++;
641 }
642
643 void CDVDPlayerVideo::Flush()
644 {
645   /* flush using message as this get's called from dvdplayer thread */
646   /* and any demux packet that has been taken out of queue need to */
647   /* be disposed of before we flush */
648   m_messageQueue.Flush();
649   m_messageQueue.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1);
650 }
651
652 #ifdef HAS_VIDEO_PLAYBACK
653 void CDVDPlayerVideo::ProcessOverlays(DVDVideoPicture* pSource, YV12Image* pDest, double pts)
654 {
655   // remove any overlays that are out of time
656   m_pOverlayContainer->CleanUp(min(pts, pts - m_iSubtitleDelay));
657
658   if(pSource->format != DVDVideoPicture::FMT_YUV420P)
659     return;
660
661   // rendering spu overlay types directly on video memory costs a lot of processing power.
662   // thus we allocate a temp picture, copy the original to it (needed because the same picture can be used more than once).
663   // then do all the rendering on that temp picture and finaly copy it to video memory.
664   // In almost all cases this is 5 or more times faster!.
665   bool bHasSpecialOverlay = m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SPU) 
666                          || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_IMAGE)
667                          || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SSA);
668   
669   if (bHasSpecialOverlay)
670   {
671     if (m_pTempOverlayPicture && (m_pTempOverlayPicture->iWidth != pSource->iWidth || m_pTempOverlayPicture->iHeight != pSource->iHeight))
672     {
673       CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
674       m_pTempOverlayPicture = NULL;
675     }
676     
677     if (!m_pTempOverlayPicture) m_pTempOverlayPicture = CDVDCodecUtils::AllocatePicture(pSource->iWidth, pSource->iHeight);
678   }
679
680   if (bHasSpecialOverlay && m_pTempOverlayPicture) 
681     CDVDCodecUtils::CopyPicture(m_pTempOverlayPicture, pSource);
682   else 
683     CDVDCodecUtils::CopyPicture(pDest, pSource);
684   
685   m_pOverlayContainer->Lock();
686
687   VecOverlays* pVecOverlays = m_pOverlayContainer->GetOverlays();
688   VecOverlaysIter it = pVecOverlays->begin();
689
690   //Check all overlays and render those that should be rendered, based on time and forced
691   //Both forced and subs should check timeing, pts == 0 in the stillframe case
692   while (it != pVecOverlays->end())
693   {
694     CDVDOverlay* pOverlay = *it++;
695     if(!pOverlay->bForced && !m_bRenderSubs)
696       continue;
697
698     if(pOverlay->iGroupId != pSource->iGroupId)
699       continue;
700
701     double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay;
702
703     if((pOverlay->iPTSStartTime <= pts2 && (pOverlay->iPTSStopTime > pts2 || pOverlay->iPTSStopTime == 0LL)) || pts == 0)
704     {
705       if (bHasSpecialOverlay && m_pTempOverlayPicture) 
706         CDVDOverlayRenderer::Render(m_pTempOverlayPicture, pOverlay, pts2);
707       else 
708         CDVDOverlayRenderer::Render(pDest, pOverlay, pts2);
709     }
710   }
711
712   m_pOverlayContainer->Unlock();
713   
714   if (bHasSpecialOverlay && m_pTempOverlayPicture)
715     CDVDCodecUtils::CopyPicture(pDest, m_pTempOverlayPicture);
716 }
717 #endif
718
719 int CDVDPlayerVideo::OutputPicture(DVDVideoPicture* pPicture, double pts)
720 {
721 #ifdef HAS_VIDEO_PLAYBACK
722   /* check so that our format or aspect has changed. if it has, reconfigure renderer */
723   if (!g_renderManager.IsConfigured()
724    || m_output.width != pPicture->iWidth
725    || m_output.height != pPicture->iHeight
726    || m_output.dwidth != pPicture->iDisplayWidth
727    || m_output.dheight != pPicture->iDisplayHeight
728    || m_output.framerate != m_fFrameRate
729    || ( m_output.color_matrix != pPicture->color_matrix && pPicture->color_matrix != 0 ) // don't reconfigure on unspecified
730    || m_output.color_range != pPicture->color_range)
731   {
732     CLog::Log(LOGNOTICE, " fps: %f, pwidth: %i, pheight: %i, dwidth: %i, dheight: %i",
733       m_fFrameRate, pPicture->iWidth, pPicture->iHeight, pPicture->iDisplayWidth, pPicture->iDisplayHeight);
734     unsigned flags = 0;
735     if(pPicture->color_range == 1)
736       flags |= CONF_FLAGS_YUV_FULLRANGE;
737
738     switch(pPicture->color_matrix)
739     {
740       case 7: // SMPTE 240M (1987)
741         flags |= CONF_FLAGS_YUVCOEF_240M;
742         break;
743       case 6: // SMPTE 170M
744       case 5: // ITU-R BT.470-2
745       case 4: // FCC
746         flags |= CONF_FLAGS_YUVCOEF_BT601;
747         break;
748       case 3: // RESERVED
749       case 2: // UNSPECIFIED
750       case 1: // ITU-R Rec.709 (1990) -- BT.709
751       default:
752         flags |= CONF_FLAGS_YUVCOEF_BT709;
753     }
754
755     if(m_bAllowFullscreen)
756     {
757       flags |= CONF_FLAGS_FULLSCREEN;
758       m_bAllowFullscreen = false; // only allow on first configure
759     }
760
761     CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f",__FUNCTION__,pPicture->iWidth, pPicture->iHeight,m_fFrameRate);
762     if(!g_renderManager.Configure(pPicture->iWidth, pPicture->iHeight, pPicture->iDisplayWidth, pPicture->iDisplayHeight, m_fFrameRate, flags))
763     {
764       CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__);
765       return EOS_ABORT;
766     }
767
768     m_output.width = pPicture->iWidth;
769     m_output.height = pPicture->iHeight;
770     m_output.dwidth = pPicture->iDisplayWidth;
771     m_output.dheight = pPicture->iDisplayHeight;
772     m_output.framerate = m_fFrameRate;
773     m_output.color_matrix = pPicture->color_matrix;
774     m_output.color_range = pPicture->color_range;
775   }
776
777   double maxfps  = 60.0;
778   bool   limited = false;
779   int    result  = 0;
780
781   if (!g_renderManager.IsStarted()) {
782     CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__);
783     return EOS_ABORT;
784   }
785   maxfps = g_renderManager.GetMaximumFPS();
786
787   // check if our output will limit speed
788   if(m_fFrameRate * abs(m_speed) / DVD_PLAYSPEED_NORMAL > maxfps*0.9)
789     limited = true;
790
791   //User set delay
792   pts += m_iVideoDelay;
793
794   // calculate the time we need to delay this picture before displaying
795   double iSleepTime, iClockSleep, iFrameSleep, iCurrentClock, iFrameDuration;
796   
797   iCurrentClock = m_pClock->GetAbsoluteClock(); // snapshot current clock  
798   iClockSleep = pts - m_pClock->GetClock();  //sleep calculated by pts to clock comparison
799   iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame
800   iFrameDuration = pPicture->iDuration;
801
802   // correct sleep times based on speed
803   if(m_speed)
804   {
805     iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed;
806     iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed);
807     iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed);
808   }
809   else
810   {
811     iClockSleep = 0;
812     iFrameSleep = 0;
813   }
814
815   // dropping to a very low framerate is not correct (it should not happen at all)
816   iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500));
817   iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500));
818
819   if( m_stalled )
820   { // when we render a still, we can't sync to clock anyway
821     iSleepTime = iFrameSleep;
822   }
823   else
824   {
825     // try to decide on how to sync framerate
826     if( pPicture->iFlags & DVP_FLAG_NOAUTOSYNC )
827       iSleepTime = iClockSleep;
828     else
829       iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync;
830   }
831
832 #ifdef PROFILE /* during profiling, try to play as fast as possible */
833   iSleepTime = 0;
834 #endif
835
836   // present the current pts of this frame to user, and include the actual
837   // presentation delay, to allow him to adjust for it
838   if( !m_stalled )
839     m_iCurrentPts = pts - max(0.0, iSleepTime);
840
841   // timestamp when we think next picture should be displayed based on current duration
842   m_FlipTimeStamp  = iCurrentClock;
843   m_FlipTimeStamp += max(0.0, iSleepTime);
844   m_FlipTimeStamp += iFrameDuration;
845
846   if (iClockSleep <= 0 && m_speed)
847     m_iLateFrames++;
848   else
849     m_iLateFrames = 0;
850
851   // ask decoder to drop frames next round, as we are very late
852   if(m_iLateFrames > 10)
853   {
854     //if we're calculating the framerate,
855     //don't drop frames until we've calculated a stable framerate
856     if (m_speed != DVD_PLAYSPEED_NORMAL)
857     {
858       result |= EOS_VERYLATE;
859     }
860
861     //if we requested 5 drops in a row and we're still late, drop on output
862     //this keeps a/v sync if the decoder can't drop, or we're still calculating the framerate
863     if (m_iDroppedRequest > 5)
864     {
865       m_iDroppedRequest--; //decrease so we only drop half the frames
866       return result | EOS_DROPPED;
867     }
868     m_iDroppedRequest++;
869   }
870   else
871   {
872     m_iDroppedRequest = 0;
873   }
874
875   if( m_speed < 0 )
876   {
877     if( iClockSleep < -DVD_MSEC_TO_TIME(200)
878     && !(pPicture->iFlags & DVP_FLAG_NOSKIP) )
879       return result | EOS_DROPPED;
880   }
881
882   if( (pPicture->iFlags & DVP_FLAG_DROPPED) )
883     return result | EOS_DROPPED;
884
885   if( m_speed != DVD_PLAYSPEED_NORMAL && limited )
886   {
887     // calculate frame dropping pattern to render at this speed
888     // we do that by deciding if this or next frame is closest
889     // to the flip timestamp
890     double current   = fabs(m_dropbase -  m_droptime);
891     double next      = fabs(m_dropbase - (m_droptime + iFrameDuration));
892     double frametime = (double)DVD_TIME_BASE / maxfps;
893
894     m_droptime += iFrameDuration;
895 #ifndef PROFILE
896     if( next < current && !(pPicture->iFlags & DVP_FLAG_NOSKIP) )
897       return result | EOS_DROPPED;
898 #endif
899
900     while(!m_bStop && m_dropbase < m_droptime)             m_dropbase += frametime;
901     while(!m_bStop && m_dropbase - frametime > m_droptime) m_dropbase -= frametime;
902   } 
903   else
904   {
905     m_droptime = 0.0f;
906     m_dropbase = 0.0f;
907   }
908
909   // set fieldsync if picture is interlaced
910   EFIELDSYNC mDisplayField = FS_NONE;
911   if( pPicture->iFlags & DVP_FLAG_INTERLACED )
912   {
913     if( pPicture->iFlags & DVP_FLAG_TOP_FIELD_FIRST )
914       mDisplayField = FS_ODD;
915     else
916       mDisplayField = FS_EVEN;
917   }
918
919   // copy picture to overlay
920   YV12Image image;
921
922   int index = g_renderManager.GetImage(&image);
923
924   // video device might not be done yet
925   while (index < 0 && !CThread::m_bStop &&
926          CDVDClock::GetAbsoluteClock() < iCurrentClock + iSleepTime )
927   {
928     Sleep(1);
929     index = g_renderManager.GetImage(&image);
930   }
931
932   if (index < 0)
933     return EOS_DROPPED;
934
935   ProcessOverlays(pPicture, &image, pts);
936
937   // tell the renderer that we've finished with the image (so it can do any
938   // post processing before FlipPage() is called.)
939   g_renderManager.ReleaseImage(index);
940
941   // present this image after the given delay, but correct for copy time
942   double delay = iCurrentClock + iSleepTime - m_pClock->GetAbsoluteClock();
943
944   if(delay<0)
945     g_renderManager.FlipPage( 0, -1, mDisplayField);
946   else
947     g_renderManager.FlipPage( (DWORD)(delay * 1000 / DVD_TIME_BASE), -1, mDisplayField);
948
949   return result;
950 #else
951   // no video renderer, let's mark it as dropped
952   return EOS_DROPPED;
953 #endif
954 }
955
956 std::string CDVDPlayerVideo::GetPlayerInfo()
957 {
958   std::ostringstream s;
959   s << "vq:"     << setw(2) << min(99,m_messageQueue.GetLevel()) << "%";
960   s << ", dc:"   << m_codecname;
961   s << ", Mb/s:" << fixed << setprecision(2) << (double)GetVideoBitrate() / (1024.0*1024.0);
962   s << ", drop:" << m_iDroppedFrames;
963
964   return s.str();
965 }
966
967 int CDVDPlayerVideo::GetVideoBitrate()
968 {
969   return (int)m_videoStats.GetBitrate();
970 }
971