Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDPlayerVideo.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 "system.h"
22 #include "cores/VideoRenderers/RenderFlags.h"
23 #include "windowing/WindowingFactory.h"
24 #include "settings/AdvancedSettings.h"
25 #include "settings/MediaSettings.h"
26 #include "settings/Settings.h"
27 #include "video/VideoReferenceClock.h"
28 #include "utils/MathUtils.h"
29 #include "DVDPlayer.h"
30 #include "DVDPlayerVideo.h"
31 #include "DVDCodecs/DVDFactoryCodec.h"
32 #include "DVDCodecs/DVDCodecUtils.h"
33 #include "DVDCodecs/Video/DVDVideoPPFFmpeg.h"
34 #include "DVDCodecs/Video/DVDVideoCodecFFmpeg.h"
35 #include "DVDDemuxers/DVDDemux.h"
36 #include "DVDDemuxers/DVDDemuxUtils.h"
37 #include "DVDOverlayRenderer.h"
38 #include "DVDCodecs/DVDCodecs.h"
39 #include "DVDCodecs/Overlay/DVDOverlayCodecCC.h"
40 #include "DVDCodecs/Overlay/DVDOverlaySSA.h"
41 #include <sstream>
42 #include <iomanip>
43 #include <numeric>
44 #include <iterator>
45 #include "guilib/GraphicContext.h"
46 #include "utils/log.h"
47
48 using namespace std;
49 using namespace RenderManager;
50
51 class CPulldownCorrection
52 {
53 public:
54   CPulldownCorrection()
55   {
56     m_duration = 0.0;
57     m_accum    = 0;
58     m_total    = 0;
59     m_next     = m_pattern.end();
60   }
61
62   void init(double fps, int *begin, int *end)
63   {
64     std::copy(begin, end, std::back_inserter(m_pattern));
65     m_duration = DVD_TIME_BASE / fps;
66     m_accum    = 0;
67     m_total    = std::accumulate(m_pattern.begin(), m_pattern.end(), 0);
68     m_next     = m_pattern.begin();
69   }
70
71   double pts()
72   {
73     double input  = m_duration * std::distance(m_pattern.begin(), m_next);
74     double output = m_duration * m_accum / m_total;
75     return output - input;
76   }
77
78   double dur()
79   {
80     return m_duration * m_pattern.size() * *m_next / m_total;
81   }
82
83   void next()
84   {
85     m_accum += *m_next;
86     if(++m_next == m_pattern.end())
87     {
88       m_next  = m_pattern.begin();
89       m_accum = 0;
90     }
91   }
92
93   bool enabled()
94   {
95     return !m_pattern.empty();
96   }
97 private:
98   double                     m_duration;
99   int                        m_total;
100   int                        m_accum;
101   std::vector<int>           m_pattern;
102   std::vector<int>::iterator m_next;
103 };
104
105
106 class CDVDMsgVideoCodecChange : public CDVDMsg
107 {
108 public:
109   CDVDMsgVideoCodecChange(const CDVDStreamInfo &hints, CDVDVideoCodec* codec)
110     : CDVDMsg(GENERAL_STREAMCHANGE)
111     , m_codec(codec)
112     , m_hints(hints)
113   {}
114  ~CDVDMsgVideoCodecChange()
115   {
116     delete m_codec;
117   }
118   CDVDVideoCodec* m_codec;
119   CDVDStreamInfo  m_hints;
120 };
121
122
123 CDVDPlayerVideo::CDVDPlayerVideo( CDVDClock* pClock
124                                 , CDVDOverlayContainer* pOverlayContainer
125                                 , CDVDMessageQueue& parent)
126 : CThread("DVDPlayerVideo")
127 , m_messageQueue("video")
128 , m_messageParent(parent)
129 {
130   m_pClock = pClock;
131   m_pOverlayContainer = pOverlayContainer;
132   m_pTempOverlayPicture = NULL;
133   m_pVideoCodec = NULL;
134   m_pOverlayCodecCC = NULL;
135   m_speed = DVD_PLAYSPEED_NORMAL;
136
137   m_bRenderSubs = false;
138   m_stalled = false;
139   m_started = false;
140   m_iVideoDelay = 0;
141   m_iSubtitleDelay = 0;
142   m_FlipTimeStamp = 0.0;
143   m_iLateFrames = 0;
144   m_iDroppedRequest = 0;
145   m_fForcedAspectRatio = 0;
146   m_iNrOfPicturesNotToSkip = 0;
147   m_messageQueue.SetMaxDataSize(40 * 1024 * 1024);
148   m_messageQueue.SetMaxTimeSize(8.0);
149
150   m_iCurrentPts = DVD_NOPTS_VALUE;
151   m_iDroppedFrames = 0;
152   m_fFrameRate = 25;
153   m_bCalcFrameRate = false;
154   m_fStableFrameRate = 0.0;
155   m_iFrameRateCount = 0;
156   m_bAllowDrop = false;
157   m_iFrameRateErr = 0;
158   m_iFrameRateLength = 0;
159   m_bFpsInvalid = false;
160   m_bAllowFullscreen = false;
161   memset(&m_output, 0, sizeof(m_output));
162 }
163
164 CDVDPlayerVideo::~CDVDPlayerVideo()
165 {
166   StopThread();
167   g_VideoReferenceClock.StopThread();
168 }
169
170 double CDVDPlayerVideo::GetOutputDelay()
171 {
172     double time = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET);
173     if( m_fFrameRate )
174       time = (time * DVD_TIME_BASE) / m_fFrameRate;
175     else
176       time = 0.0;
177
178     if( m_speed != 0 )
179       time = time * DVD_PLAYSPEED_NORMAL / abs(m_speed);
180
181     return time;
182 }
183
184 bool CDVDPlayerVideo::OpenStream( CDVDStreamInfo &hint )
185 {
186   unsigned int surfaces = 0;
187   std::vector<ERenderFormat> formats;
188 #ifdef HAS_VIDEO_PLAYBACK
189   surfaces = g_renderManager.GetProcessorSize();
190   formats  = g_renderManager.SupportedFormats();
191 #endif
192
193
194   CLog::Log(LOGNOTICE, "Creating video codec with codec id: %i", hint.codec);
195   CDVDVideoCodec* codec = CDVDFactoryCodec::CreateVideoCodec(hint, surfaces, formats);
196   if(!codec)
197   {
198     CLog::Log(LOGERROR, "Unsupported video codec");
199     return false;
200   }
201
202   if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") && !g_VideoReferenceClock.IsRunning())
203   {
204     g_VideoReferenceClock.Create();
205     //we have to wait for the clock to start otherwise alsa can cause trouble
206     if (!g_VideoReferenceClock.WaitStarted(2000))
207       CLog::Log(LOGDEBUG, "g_VideoReferenceClock didn't start in time");
208   }
209
210   if(m_messageQueue.IsInited())
211     m_messageQueue.Put(new CDVDMsgVideoCodecChange(hint, codec), 0);
212   else
213   {
214     OpenStream(hint, codec);
215     CLog::Log(LOGNOTICE, "Creating video thread");
216     m_messageQueue.Init();
217     Create();
218   }
219   return true;
220 }
221
222 void CDVDPlayerVideo::OpenStream(CDVDStreamInfo &hint, CDVDVideoCodec* codec)
223 {
224   //reported fps is usually not completely correct
225   if (hint.fpsrate && hint.fpsscale)
226     m_fFrameRate = DVD_TIME_BASE / CDVDCodecUtils::NormalizeFrameduration((double)DVD_TIME_BASE * hint.fpsscale / hint.fpsrate);
227   else
228     m_fFrameRate = 25;
229
230   m_bFpsInvalid = (hint.fpsrate == 0 || hint.fpsscale == 0);
231
232   m_bCalcFrameRate = CSettings::Get().GetBool("videoplayer.usedisplayasclock") ||
233                      CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF;
234   ResetFrameRateCalc();
235
236   m_iDroppedRequest = 0;
237   m_iLateFrames = 0;
238
239   if( m_fFrameRate > 100 || m_fFrameRate < 5 )
240   {
241     CLog::Log(LOGERROR, "CDVDPlayerVideo::OpenStream - Invalid framerate %d, using forced 25fps and just trust timestamps", (int)m_fFrameRate);
242     m_fFrameRate = 25;
243   }
244
245   // use aspect in stream if available
246   if(hint.forced_aspect)
247     m_fForcedAspectRatio = hint.aspect;
248   else
249     m_fForcedAspectRatio = 0.0;
250
251   if (m_pVideoCodec)
252     delete m_pVideoCodec;
253
254   m_pVideoCodec = codec;
255   m_hints   = hint;
256   m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
257   m_started = false;
258   m_codecname = m_pVideoCodec->GetName();
259   m_packets.clear();
260 }
261
262 void CDVDPlayerVideo::CloseStream(bool bWaitForBuffers)
263 {
264   // wait until buffers are empty
265   if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
266
267   m_messageQueue.Abort();
268
269   // wait for decode_video thread to end
270   CLog::Log(LOGNOTICE, "waiting for video thread to exit");
271
272   StopThread(); // will set this->m_bStop to true
273
274   m_messageQueue.End();
275
276   CLog::Log(LOGNOTICE, "deleting video codec");
277   if (m_pVideoCodec)
278   {
279     m_pVideoCodec->Dispose();
280     delete m_pVideoCodec;
281     m_pVideoCodec = NULL;
282   }
283
284   if (m_pTempOverlayPicture)
285   {
286     CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
287     m_pTempOverlayPicture = NULL;
288   }
289
290   //tell the clock we stopped playing video
291   m_pClock->UpdateFramerate(0.0);
292 }
293
294 void CDVDPlayerVideo::OnStartup()
295 {
296   m_iDroppedFrames = 0;
297
298   m_crop.x1 = m_crop.x2 = 0.0f;
299   m_crop.y1 = m_crop.y2 = 0.0f;
300
301   m_iCurrentPts = DVD_NOPTS_VALUE;
302   m_FlipTimeStamp = m_pClock->GetAbsoluteClock();
303
304 }
305
306 void CDVDPlayerVideo::Process()
307 {
308   CLog::Log(LOGNOTICE, "running thread: video_thread");
309
310   DVDVideoPicture picture;
311   CPulldownCorrection pulldown;
312   CDVDVideoPPFFmpeg mPostProcess("");
313   CStdString sPostProcessType;
314   bool bPostProcessDeint = false;
315
316   memset(&picture, 0, sizeof(DVDVideoPicture));
317
318   double pts = 0;
319   double frametime = (double)DVD_TIME_BASE / m_fFrameRate;
320
321   int iDropped = 0; //frames dropped in a row
322   bool bRequestDrop = false;
323
324   m_videoStats.Start();
325
326   while (!m_bStop)
327   {
328     int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000;
329     int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
330
331     CDVDMsg* pMsg;
332     MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority);
333
334     if (MSGQ_IS_ERROR(ret))
335     {
336       CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true");
337       break;
338     }
339     else if (ret == MSGQ_TIMEOUT)
340     {
341       // if we only wanted priority messages, this isn't a stall
342       if( iPriority )
343         continue;
344
345       //Okey, start rendering at stream fps now instead, we are likely in a stillframe
346       if( !m_stalled )
347       {
348         if(m_started)
349           CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate);
350         m_stalled = true;
351         pts+= frametime*4;
352       }
353
354       //Waiting timed out, output last picture
355       if( picture.iFlags & DVP_FLAG_ALLOCATED )
356       {
357         //Remove interlaced flag before outputting
358         //no need to output this as if it was interlaced
359         picture.iFlags &= ~DVP_FLAG_INTERLACED;
360         picture.iFlags |= DVP_FLAG_NOSKIP;
361         OutputPicture(&picture, pts);
362         pts+= frametime;
363       }
364
365       continue;
366     }
367
368     if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
369     {
370       if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO))
371       {
372         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE");
373
374         /* we may be very much off correct pts here, but next picture may be a still*/
375         /* make sure it isn't dropped */
376         m_iNrOfPicturesNotToSkip = 5;
377       }
378       else
379         m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */
380
381       pMsg->Release();
382
383       continue;
384     }
385     else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
386     {
387       CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
388
389       if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
390         pts = pMsgGeneralResync->m_timestamp;
391
392       double delay = m_FlipTimeStamp - m_pClock->GetAbsoluteClock();
393       if( delay > frametime ) delay = frametime;
394       else if( delay < 0 )    delay = 0;
395
396       if(pMsgGeneralResync->m_clock)
397       {
398         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts);
399         m_pClock->Discontinuity(pts - delay);
400       }
401       else
402         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts);
403
404       pMsgGeneralResync->Release();
405       continue;
406     }
407     else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
408     {
409       if (m_speed != DVD_PLAYSPEED_PAUSE)
410       {
411         double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
412
413         CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout);
414
415         timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed);
416         timeout += CDVDClock::GetAbsoluteClock();
417
418         while(!m_bStop && CDVDClock::GetAbsoluteClock() < timeout)
419           Sleep(1);
420       }
421     }
422     else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT))
423     {
424       CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT");
425       m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg);
426     }
427     else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
428     {
429       if(m_pVideoCodec)
430         m_pVideoCodec->Reset();
431       picture.iFlags &= ~DVP_FLAG_ALLOCATED;
432       m_packets.clear();
433       m_started = false;
434     }
435     else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush())
436     {
437       if(m_pVideoCodec)
438         m_pVideoCodec->Reset();
439       picture.iFlags &= ~DVP_FLAG_ALLOCATED;
440       m_packets.clear();
441
442       m_pullupCorrection.Flush();
443       //we need to recalculate the framerate
444       //TODO: this needs to be set on a streamchange instead
445       ResetFrameRateCalc();
446
447       m_stalled = true;
448       m_started = false;
449     }
450     else if (pMsg->IsType(CDVDMsg::VIDEO_NOSKIP))
451     {
452       // libmpeg2 is also returning incomplete frames after a dvd cell change
453       // so the first few pictures are not the correct ones to display in some cases
454       // just display those together with the correct one.
455       // (setting it to 2 will skip some menu stills, 5 is working ok for me).
456       m_iNrOfPicturesNotToSkip = 5;
457     }
458     else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
459     {
460       m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
461       if(m_speed == DVD_PLAYSPEED_PAUSE)
462         m_iNrOfPicturesNotToSkip = 0;
463       if (m_pVideoCodec)
464         m_pVideoCodec->SetSpeed(m_speed);
465     }
466     else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
467     {
468       if(m_started)
469         m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
470     }
471     else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME))
472     {
473       CDVDPlayer::SPlayerState& state = ((CDVDMsgType<CDVDPlayer::SPlayerState>*)pMsg)->m_value;
474
475       if(state.time_src == CDVDPlayer::ETIMESOURCE_CLOCK)
476         state.time      = DVD_TIME_TO_MSEC(m_pClock->GetClock(state.timestamp) + state.time_offset);
477       else
478         state.timestamp = CDVDClock::GetAbsoluteClock();
479       state.player    = DVDPLAYER_VIDEO;
480       m_messageParent.Put(pMsg->Acquire());
481     }
482     else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
483     {
484       CDVDMsgVideoCodecChange* msg(static_cast<CDVDMsgVideoCodecChange*>(pMsg));
485       OpenStream(msg->m_hints, msg->m_codec);
486       msg->m_codec = NULL;
487       picture.iFlags &= ~DVP_FLAG_ALLOCATED;
488     }
489
490     if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
491     {
492       DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
493       bool bPacketDrop     = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
494
495       if (m_stalled)
496       {
497         CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe left, switching to normal playback");
498         m_stalled = false;
499
500         //don't allow the first frames after a still to be dropped
501         //sometimes we get multiple stills (long duration frames) after each other
502         //in normal mpegs
503         m_iNrOfPicturesNotToSkip = 5;
504       }
505       else if( iDropped*frametime > DVD_MSEC_TO_TIME(100) && m_iNrOfPicturesNotToSkip == 0 )
506       { // if we dropped too many pictures in a row, insert a forced picture
507         m_iNrOfPicturesNotToSkip = 1;
508       }
509
510       if (m_messageQueue.GetDataSize() == 0
511       ||  m_speed < 0)
512       {
513         bRequestDrop = false;
514         m_iDroppedRequest = 0;
515         m_iLateFrames     = 0;
516       }
517
518       // if player want's us to drop this packet, do so nomatter what
519       if(bPacketDrop)
520         bRequestDrop = true;
521
522       // tell codec if next frame should be dropped
523       // problem here, if one packet contains more than one frame
524       // both frames will be dropped in that case instead of just the first
525       // decoder still needs to provide an empty image structure, with correct flags
526       m_pVideoCodec->SetDropState(bRequestDrop);
527
528       // ask codec to do deinterlacing if possible
529       EDEINTERLACEMODE mDeintMode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
530       EINTERLACEMETHOD mInt       = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
531
532       unsigned int     mFilters = 0;
533
534       if (mDeintMode != VS_DEINTERLACEMODE_OFF)
535       {
536         if (mInt == VS_INTERLACEMETHOD_DEINTERLACE)
537           mFilters = CDVDVideoCodec::FILTER_DEINTERLACE_ANY;
538         else if(mInt == VS_INTERLACEMETHOD_DEINTERLACE_HALF)
539           mFilters = CDVDVideoCodec::FILTER_DEINTERLACE_ANY | CDVDVideoCodec::FILTER_DEINTERLACE_HALFED;
540
541         if (mDeintMode == VS_DEINTERLACEMODE_AUTO && mFilters)
542           mFilters |=  CDVDVideoCodec::FILTER_DEINTERLACE_FLAGGED;
543       }
544
545       if (!g_renderManager.Supports(RENDERFEATURE_ROTATION))
546         mFilters |= CDVDVideoCodec::FILTER_ROTATE;
547
548       mFilters = m_pVideoCodec->SetFilters(mFilters);
549
550       int iDecoderState = m_pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts);
551
552       // buffer packets so we can recover should decoder flush for some reason
553       if(m_pVideoCodec->GetConvergeCount() > 0)
554       {
555         m_packets.push_back(DVDMessageListItem(pMsg, 0));
556         if(m_packets.size() > m_pVideoCodec->GetConvergeCount()
557         || m_packets.size() * frametime > DVD_SEC_TO_TIME(10))
558           m_packets.pop_front();
559       }
560
561       m_videoStats.AddSampleBytes(pPacket->iSize);
562       // assume decoder dropped a picture if it didn't give us any
563       // picture from a demux packet, this should be reasonable
564       // for libavformat as a demuxer as it normally packetizes
565       // pictures when they come from demuxer
566       if(bRequestDrop && !bPacketDrop && (iDecoderState & VC_BUFFER) && !(iDecoderState & VC_PICTURE))
567       {
568         m_iDroppedFrames++;
569         iDropped++;
570       }
571       // reset the request, the following while loop may break before
572       // setting the flag to a new value
573       bRequestDrop = false;
574
575       // loop while no error
576       while (!m_bStop)
577       {
578
579         // if decoder was flushed, we need to seek back again to resume rendering
580         if (iDecoderState & VC_FLUSHED)
581         {
582           CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder was flushed");
583           while(!m_packets.empty())
584           {
585             CDVDMsgDemuxerPacket* msg = (CDVDMsgDemuxerPacket*)m_packets.front().message->Acquire();
586             m_packets.pop_front();
587
588             // all packets except the last one should be dropped
589             // if prio packets and current packet should be dropped, this is likely a new reset
590             msg->m_drop = !m_packets.empty() || (iPriority > 0 && bPacketDrop);
591             m_messageQueue.Put(msg, iPriority + 10);
592           }
593
594           m_pVideoCodec->Reset();
595           m_packets.clear();
596           picture.iFlags &= ~DVP_FLAG_ALLOCATED;
597           g_renderManager.DiscardBuffer();
598           break;
599         }
600
601         // if decoder had an error, tell it to reset to avoid more problems
602         if (iDecoderState & VC_ERROR)
603         {
604           CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder returned error");
605           break;
606         }
607
608         // check for a new picture
609         if (iDecoderState & VC_PICTURE)
610         {
611
612           // try to retrieve the picture (should never fail!), unless there is a demuxer bug ofcours
613           m_pVideoCodec->ClearPicture(&picture);
614           if (m_pVideoCodec->GetPicture(&picture))
615           {
616             sPostProcessType.clear();
617
618             if(picture.iDuration == 0.0)
619               picture.iDuration = frametime;
620
621             if(bPacketDrop)
622               picture.iFlags |= DVP_FLAG_DROPPED;
623
624             if (m_iNrOfPicturesNotToSkip > 0)
625             {
626               picture.iFlags |= DVP_FLAG_NOSKIP;
627               m_iNrOfPicturesNotToSkip--;
628             }
629
630             // validate picture timing,
631             // if both dts/pts invalid, use pts calulated from picture.iDuration
632             // if pts invalid use dts, else use picture.pts as passed
633             if (picture.dts == DVD_NOPTS_VALUE && picture.pts == DVD_NOPTS_VALUE)
634               picture.pts = pts;
635             else if (picture.pts == DVD_NOPTS_VALUE)
636               picture.pts = picture.dts;
637
638             /* use forced aspect if any */
639             if( m_fForcedAspectRatio != 0.0f )
640               picture.iDisplayWidth = (int) (picture.iDisplayHeight * m_fForcedAspectRatio);
641
642             //Deinterlace if codec said format was interlaced or if we have selected we want to deinterlace
643             //this video
644             if ((mDeintMode == VS_DEINTERLACEMODE_AUTO && (picture.iFlags & DVP_FLAG_INTERLACED)) || mDeintMode == VS_DEINTERLACEMODE_FORCE)
645             {
646               if(mInt == VS_INTERLACEMETHOD_SW_BLEND)
647               {
648                 if (!sPostProcessType.empty())
649                   sPostProcessType += ",";
650                 sPostProcessType += g_advancedSettings.m_videoPPFFmpegDeint;
651                 bPostProcessDeint = true;
652               }
653             }
654
655             if (CMediaSettings::Get().GetCurrentVideoSettings().m_PostProcess)
656             {
657               if (!sPostProcessType.empty())
658                 sPostProcessType += ",";
659               // This is what mplayer uses for its "high-quality filter combination"
660               sPostProcessType += g_advancedSettings.m_videoPPFFmpegPostProc;
661             }
662
663             if (!sPostProcessType.empty())
664             {
665               mPostProcess.SetType(sPostProcessType, bPostProcessDeint);
666               if (mPostProcess.Process(&picture))
667                 mPostProcess.GetPicture(&picture);
668             }
669
670             /* if frame has a pts (usually originiating from demux packet), use that */
671             if(picture.pts != DVD_NOPTS_VALUE)
672             {
673               if(pulldown.enabled())
674                 picture.pts += pulldown.pts();
675
676               pts = picture.pts;
677             }
678
679             if(pulldown.enabled())
680             {
681               picture.iDuration = pulldown.dur();
682               pulldown.next();
683             }
684
685             if (picture.iRepeatPicture)
686               picture.iDuration *= picture.iRepeatPicture + 1;
687
688             int iResult = OutputPicture(&picture, pts);
689
690             if(m_started == false)
691             {
692               m_codecname = m_pVideoCodec->GetName();
693               m_started = true;
694               m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
695             }
696
697             // guess next frame pts. iDuration is always valid
698             if (m_speed != 0)
699               pts += picture.iDuration * m_speed / abs(m_speed);
700
701             if( iResult & EOS_ABORT )
702             {
703               //if we break here and we directly try to decode again wihout
704               //flushing the video codec things break for some reason
705               //i think the decoder (libmpeg2 atleast) still has a pointer
706               //to the data, and when the packet is freed that will fail.
707               iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE);
708               break;
709             }
710
711             if( (iResult & EOS_DROPPED) && !bPacketDrop )
712             {
713               m_iDroppedFrames++;
714               iDropped++;
715             }
716             else
717               iDropped = 0;
718
719             bRequestDrop = (iResult & EOS_VERYLATE) == EOS_VERYLATE;
720           }
721           else
722           {
723             CLog::Log(LOGWARNING, "Decoder Error getting videoPicture.");
724             m_pVideoCodec->Reset();
725           }
726         }
727
728         /*
729         if (iDecoderState & VC_USERDATA)
730         {
731           // found some userdata while decoding a frame
732           // could be closed captioning
733           DVDVideoUserData videoUserData;
734           if (m_pVideoCodec->GetUserData(&videoUserData))
735           {
736             ProcessVideoUserData(&videoUserData, pts);
737           }
738         }
739         */
740
741         // if the decoder needs more data, we just break this loop
742         // and try to get more data from the videoQueue
743         if (iDecoderState & VC_BUFFER)
744           break;
745
746         // the decoder didn't need more data, flush the remaning buffer
747         iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE);
748       }
749     }
750
751     // all data is used by the decoder, we can safely free it now
752     pMsg->Release();
753   }
754
755   // we need to let decoder release any picture retained resources.
756   m_pVideoCodec->ClearPicture(&picture);
757 }
758
759 void CDVDPlayerVideo::OnExit()
760 {
761   if (m_pOverlayCodecCC)
762   {
763     m_pOverlayCodecCC->Dispose();
764     m_pOverlayCodecCC = NULL;
765   }
766
767   CLog::Log(LOGNOTICE, "thread end: video_thread");
768 }
769
770 void CDVDPlayerVideo::ProcessVideoUserData(DVDVideoUserData* pVideoUserData, double pts)
771 {
772   // check userdata type
773   uint8_t* data = pVideoUserData->data;
774   int size = pVideoUserData->size;
775
776   if (size >= 2)
777   {
778     if (data[0] == 'C' && data[1] == 'C')
779     {
780       data += 2;
781       size -= 2;
782
783       // closed captioning
784       if (!m_pOverlayCodecCC)
785       {
786         m_pOverlayCodecCC = new CDVDOverlayCodecCC();
787         CDVDCodecOptions options;
788         CDVDStreamInfo info;
789         if (!m_pOverlayCodecCC->Open(info, options))
790         {
791           delete m_pOverlayCodecCC;
792           m_pOverlayCodecCC = NULL;
793         }
794       }
795
796       if (m_pOverlayCodecCC)
797       {
798         DemuxPacket packet;
799         packet.pData = data;
800         packet.iSize = size;
801         packet.pts = DVD_NOPTS_VALUE;
802         packet.dts = DVD_NOPTS_VALUE;
803         m_pOverlayCodecCC->Decode(&packet);
804
805         CDVDOverlay* overlay;
806         while((overlay = m_pOverlayCodecCC->GetOverlay()) != NULL)
807         {
808           overlay->iPTSStartTime += pts;
809           if(overlay->iPTSStopTime != 0.0)
810             overlay->iPTSStopTime += pts;
811
812           m_pOverlayContainer->Add(overlay);
813           overlay->Release();
814         }
815       }
816     }
817   }
818 }
819
820 bool CDVDPlayerVideo::InitializedOutputDevice()
821 {
822 #ifdef HAS_VIDEO_PLAYBACK
823   return g_renderManager.IsStarted();
824 #else
825   return false;
826 #endif
827 }
828
829 void CDVDPlayerVideo::SetSpeed(int speed)
830 {
831   if(m_messageQueue.IsInited())
832     m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
833   else
834     m_speed = speed;
835 }
836
837 void CDVDPlayerVideo::StepFrame()
838 {
839   m_iNrOfPicturesNotToSkip++;
840 }
841
842 void CDVDPlayerVideo::Flush()
843 {
844   /* flush using message as this get's called from dvdplayer thread */
845   /* and any demux packet that has been taken out of queue need to */
846   /* be disposed of before we flush */
847   m_messageQueue.Flush();
848   m_messageQueue.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH), 1);
849 }
850
851 int CDVDPlayerVideo::GetLevel()
852 {
853   int level = m_messageQueue.GetLevel();
854
855   // fast exit, if the message queue is full, we do not care about the codec queue.
856   if (level == 100)
857     return level;
858
859   // Now for the harder choices, the message queue could be time or size based.
860   // In order to return the proper summed level, we need to know which.
861   if (m_messageQueue.IsDataBased())
862   {
863     int datasize = m_messageQueue.GetDataSize();
864     if (m_pVideoCodec)
865       datasize += m_pVideoCodec->GetDataSize();
866     return min(100, (int)(100 * datasize / (m_messageQueue.GetMaxDataSize() * m_messageQueue.GetMaxTimeSize())));
867   }
868   else
869   {
870     double timesize = m_messageQueue.GetTimeSize();
871     if (m_pVideoCodec)
872       timesize += m_pVideoCodec->GetTimeSize();
873     return min(100, MathUtils::round_int(100.0 * m_messageQueue.GetMaxTimeSize() * timesize));
874   }
875
876   return level;
877 }
878
879 #ifdef HAS_VIDEO_PLAYBACK
880 void CDVDPlayerVideo::ProcessOverlays(DVDVideoPicture* pSource, double pts)
881 {
882   // remove any overlays that are out of time
883   if (m_started)
884     m_pOverlayContainer->CleanUp(pts - m_iSubtitleDelay);
885
886   enum EOverlay
887   { OVERLAY_AUTO // select mode auto
888   , OVERLAY_GPU  // render osd using gpu
889   , OVERLAY_BUF  // render osd on buffer
890   } render = OVERLAY_AUTO;
891
892   if(pSource->format == RENDER_FMT_YUV420P)
893   {
894     if(g_Windowing.GetRenderQuirks() & RENDER_QUIRKS_MAJORMEMLEAK_OVERLAYRENDERER)
895     {
896       // for now use cpu for ssa overlays as it currently allocates and
897       // frees textures for each frame this causes a hugh memory leak
898       // on some mesa intel drivers
899
900       if(m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SPU)
901       || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_IMAGE)
902       || m_pOverlayContainer->ContainsOverlayType(DVDOVERLAY_TYPE_SSA) )
903         render = OVERLAY_BUF;
904     }
905
906     if(render == OVERLAY_BUF)
907     {
908       // rendering spu overlay types directly on video memory costs a lot of processing power.
909       // thus we allocate a temp picture, copy the original to it (needed because the same picture can be used more than once).
910       // then do all the rendering on that temp picture and finaly copy it to video memory.
911       // In almost all cases this is 5 or more times faster!.
912
913       if(m_pTempOverlayPicture && ( m_pTempOverlayPicture->iWidth  != pSource->iWidth
914                                  || m_pTempOverlayPicture->iHeight != pSource->iHeight))
915       {
916         CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
917         m_pTempOverlayPicture = NULL;
918       }
919
920       if(!m_pTempOverlayPicture)
921         m_pTempOverlayPicture = CDVDCodecUtils::AllocatePicture(pSource->iWidth, pSource->iHeight);
922       if(!m_pTempOverlayPicture)
923         return;
924
925       CDVDCodecUtils::CopyPicture(m_pTempOverlayPicture, pSource);
926       memcpy(pSource->data     , m_pTempOverlayPicture->data     , sizeof(pSource->data));
927       memcpy(pSource->iLineSize, m_pTempOverlayPicture->iLineSize, sizeof(pSource->iLineSize));
928     }
929   }
930
931   if(render == OVERLAY_AUTO)
932     render = OVERLAY_GPU;
933
934   VecOverlays overlays;
935
936   {
937     CSingleLock lock(*m_pOverlayContainer);
938
939     VecOverlays* pVecOverlays = m_pOverlayContainer->GetOverlays();
940     VecOverlaysIter it = pVecOverlays->begin();
941
942     //Check all overlays and render those that should be rendered, based on time and forced
943     //Both forced and subs should check timing
944     while (it != pVecOverlays->end())
945     {
946       CDVDOverlay* pOverlay = *it++;
947       if(!pOverlay->bForced && !m_bRenderSubs)
948         continue;
949
950       double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay;
951
952       if((pOverlay->iPTSStartTime <= pts2 && (pOverlay->iPTSStopTime > pts2 || pOverlay->iPTSStopTime == 0LL)))
953       {
954         if(pOverlay->IsOverlayType(DVDOVERLAY_TYPE_GROUP))
955           overlays.insert(overlays.end(), static_cast<CDVDOverlayGroup*>(pOverlay)->m_overlays.begin()
956                                         , static_cast<CDVDOverlayGroup*>(pOverlay)->m_overlays.end());
957         else
958           overlays.push_back(pOverlay);
959
960       }
961     }
962
963     for(it = overlays.begin(); it != overlays.end(); ++it)
964     {
965       double pts2 = (*it)->bForced ? pts : pts - m_iSubtitleDelay;
966
967       if (render == OVERLAY_GPU)
968         g_renderManager.AddOverlay(*it, pts2);
969
970       if (render == OVERLAY_BUF)
971         CDVDOverlayRenderer::Render(pSource, *it, pts2);
972     }
973   }
974
975
976 }
977 #endif
978
979 static std::string GetRenderFormatName(ERenderFormat format)
980 {
981   switch(format)
982   {
983     case RENDER_FMT_YUV420P:   return "YV12";
984     case RENDER_FMT_YUV420P16: return "YV12P16";
985     case RENDER_FMT_YUV420P10: return "YV12P10";
986     case RENDER_FMT_NV12:      return "NV12";
987     case RENDER_FMT_UYVY422:   return "UYVY";
988     case RENDER_FMT_YUYV422:   return "YUY2";
989     case RENDER_FMT_VDPAU:     return "VDPAU";
990     case RENDER_FMT_VDPAU_420: return "VDPAU_420";
991     case RENDER_FMT_DXVA:      return "DXVA";
992     case RENDER_FMT_VAAPI:     return "VAAPI";
993     case RENDER_FMT_OMXEGL:    return "OMXEGL";
994     case RENDER_FMT_CVBREF:    return "BGRA";
995     case RENDER_FMT_EGLIMG:    return "EGLIMG";
996     case RENDER_FMT_BYPASS:    return "BYPASS";
997     case RENDER_FMT_MEDIACODEC:return "MEDIACODEC";
998     case RENDER_FMT_NONE:      return "NONE";
999   }
1000   return "UNKNOWN";
1001 }
1002
1003 std::string CDVDPlayerVideo::GetStereoMode()
1004 {
1005   std::string  stereo_mode;
1006
1007   switch(CMediaSettings::Get().GetCurrentVideoSettings().m_StereoMode)
1008   {
1009     case RENDER_STEREO_MODE_SPLIT_VERTICAL:   stereo_mode = "left_right"; break;
1010     case RENDER_STEREO_MODE_SPLIT_HORIZONTAL: stereo_mode = "top_bottom"; break;
1011     default:                                  stereo_mode = m_hints.stereo_mode; break;
1012   }
1013
1014   if(CMediaSettings::Get().GetCurrentVideoSettings().m_StereoInvert)
1015     stereo_mode = GetStereoModeInvert(stereo_mode);
1016   return stereo_mode;
1017 }
1018
1019 int CDVDPlayerVideo::OutputPicture(const DVDVideoPicture* src, double pts)
1020 {
1021   /* picture buffer is not allowed to be modified in this call */
1022   DVDVideoPicture picture(*src);
1023   DVDVideoPicture* pPicture = &picture;
1024
1025   /* grab stereo mode from image if available */
1026   if(src->stereo_mode[0])
1027     m_hints.stereo_mode = src->stereo_mode;
1028
1029   /* figure out steremode expected based on user settings and hints */
1030   unsigned int stereo_flags = GetStereoModeFlags(GetStereoMode());
1031
1032 #ifdef HAS_VIDEO_PLAYBACK
1033   double config_framerate = m_bFpsInvalid ? 0.0 : m_fFrameRate;
1034   /* check so that our format or aspect has changed. if it has, reconfigure renderer */
1035   if (!g_renderManager.IsConfigured()
1036    || ( m_output.width           != pPicture->iWidth )
1037    || ( m_output.height          != pPicture->iHeight )
1038    || ( m_output.dwidth          != pPicture->iDisplayWidth )
1039    || ( m_output.dheight         != pPicture->iDisplayHeight )
1040    || ( m_output.framerate       != config_framerate )
1041    || ( m_output.color_format    != (unsigned int)pPicture->format )
1042    || ( m_output.extended_format != pPicture->extended_format )
1043    || ( m_output.color_matrix    != pPicture->color_matrix    && pPicture->color_matrix    != 0 ) // don't reconfigure on unspecified
1044    || ( m_output.chroma_position != pPicture->chroma_position && pPicture->chroma_position != 0 )
1045    || ( m_output.color_primaries != pPicture->color_primaries && pPicture->color_primaries != 0 )
1046    || ( m_output.color_transfer  != pPicture->color_transfer  && pPicture->color_transfer  != 0 )
1047    || ( m_output.color_range     != pPicture->color_range )
1048    || ( m_output.stereo_flags    != stereo_flags))
1049   {
1050     CLog::Log(LOGNOTICE, " fps: %f, pwidth: %i, pheight: %i, dwidth: %i, dheight: %i"
1051                        , config_framerate
1052                        , pPicture->iWidth
1053                        , pPicture->iHeight
1054                        , pPicture->iDisplayWidth
1055                        , pPicture->iDisplayHeight);
1056
1057     unsigned flags = 0;
1058     if(pPicture->color_range == 1)
1059       flags |= CONF_FLAGS_YUV_FULLRANGE;
1060
1061     flags |= GetFlagsChromaPosition(pPicture->chroma_position)
1062           |  GetFlagsColorMatrix(pPicture->color_matrix, pPicture->iWidth, pPicture->iHeight)
1063           |  GetFlagsColorPrimaries(pPicture->color_primaries)
1064           |  GetFlagsColorTransfer(pPicture->color_transfer);
1065
1066     CStdString formatstr = GetRenderFormatName(pPicture->format);
1067
1068     if(m_bAllowFullscreen)
1069     {
1070       flags |= CONF_FLAGS_FULLSCREEN;
1071       m_bAllowFullscreen = false; // only allow on first configure
1072     }
1073
1074     flags |= stereo_flags;
1075
1076     CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: %s",__FUNCTION__,pPicture->iWidth, pPicture->iHeight, config_framerate, formatstr.c_str());
1077     if(!g_renderManager.Configure(pPicture->iWidth
1078                                 , pPicture->iHeight
1079                                 , pPicture->iDisplayWidth
1080                                 , pPicture->iDisplayHeight
1081                                 , config_framerate
1082                                 , flags
1083                                 , pPicture->format
1084                                 , pPicture->extended_format
1085                                 , m_hints.orientation
1086                                 , m_pVideoCodec->GetAllowedReferences()))
1087     {
1088       CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__);
1089       return EOS_ABORT;
1090     }
1091
1092     m_output.width           = pPicture->iWidth;
1093     m_output.height          = pPicture->iHeight;
1094     m_output.dwidth          = pPicture->iDisplayWidth;
1095     m_output.dheight         = pPicture->iDisplayHeight;
1096     m_output.framerate       = config_framerate;
1097     m_output.color_format    = pPicture->format;
1098     m_output.extended_format = pPicture->extended_format;
1099     m_output.color_matrix    = pPicture->color_matrix;
1100     m_output.chroma_position = pPicture->chroma_position;
1101     m_output.color_primaries = pPicture->color_primaries;
1102     m_output.color_transfer  = pPicture->color_transfer;
1103     m_output.color_range     = pPicture->color_range;
1104     m_output.stereo_flags    = stereo_flags;
1105   }
1106
1107   int    result  = 0;
1108
1109   if (!g_renderManager.IsStarted()) {
1110     CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__);
1111     return EOS_ABORT;
1112   }
1113
1114   //correct any pattern in the timestamps
1115   if (m_output.color_format != RENDER_FMT_BYPASS)
1116   {
1117     m_pullupCorrection.Add(pts);
1118     pts += m_pullupCorrection.GetCorrection();
1119   }
1120
1121   //try to calculate the framerate
1122   CalcFrameRate();
1123
1124   // signal to clock what our framerate is, it may want to adjust it's
1125   // speed to better match with our video renderer's output speed
1126   double interval;
1127   int refreshrate = m_pClock->UpdateFramerate(m_fFrameRate, &interval);
1128   if (refreshrate > 0) //refreshrate of -1 means the videoreferenceclock is not running
1129   {//when using the videoreferenceclock, a frame is always presented half a vblank interval too late
1130     pts -= DVD_TIME_BASE * interval;
1131   }
1132
1133   if (m_output.color_format != RENDER_FMT_BYPASS)
1134   {
1135     // Correct pts by user set delay and rendering delay
1136     pts += m_iVideoDelay - DVD_SEC_TO_TIME(g_renderManager.GetDisplayLatency());
1137   }
1138
1139   // calculate the time we need to delay this picture before displaying
1140   double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration;
1141
1142   iPlayingClock = m_pClock->GetClock(iCurrentClock, false); // snapshot current clock
1143   iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison
1144   iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame
1145   iFrameDuration = pPicture->iDuration;
1146
1147   // correct sleep times based on speed
1148   if(m_speed)
1149   {
1150     iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed;
1151     iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed);
1152     iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed);
1153   }
1154   else
1155   {
1156     iClockSleep = 0;
1157     iFrameSleep = 0;
1158   }
1159
1160   if( m_started == false )
1161     iSleepTime = 0.0;
1162   else if( m_stalled )
1163     iSleepTime = iFrameSleep;
1164   else
1165     iSleepTime = iClockSleep;
1166
1167   // present the current pts of this frame to user, and include the actual
1168   // presentation delay, to allow him to adjust for it
1169   if( m_stalled )
1170     m_iCurrentPts = DVD_NOPTS_VALUE;
1171   else
1172     m_iCurrentPts = pts - max(0.0, iSleepTime);
1173
1174   // timestamp when we think next picture should be displayed based on current duration
1175   m_FlipTimeStamp  = iCurrentClock;
1176   m_FlipTimeStamp += max(0.0, iSleepTime);
1177   m_FlipTimeStamp += iFrameDuration;
1178
1179   if (iSleepTime <= 0 && m_speed)
1180     m_iLateFrames++;
1181   else
1182     m_iLateFrames = 0;
1183
1184   // ask decoder to drop frames next round, as we are very late
1185   if(m_iLateFrames > 10)
1186   {
1187     if (!(pPicture->iFlags & DVP_FLAG_NOSKIP))
1188     {
1189       //if we're calculating the framerate,
1190       //don't drop frames until we've calculated a stable framerate
1191       if (m_bAllowDrop || m_speed != DVD_PLAYSPEED_NORMAL)
1192       {
1193         result |= EOS_VERYLATE;
1194         m_pullupCorrection.Flush(); //dropped frames mess up the pattern, so just flush it
1195       }
1196       m_iDroppedRequest++;
1197     }
1198   }
1199   else
1200   {
1201     m_iDroppedRequest = 0;
1202   }
1203
1204   if( (pPicture->iFlags & DVP_FLAG_DROPPED) )
1205     return result | EOS_DROPPED;
1206
1207   // set fieldsync if picture is interlaced
1208   EFIELDSYNC mDisplayField = FS_NONE;
1209   if( pPicture->iFlags & DVP_FLAG_INTERLACED )
1210   {
1211     if( pPicture->iFlags & DVP_FLAG_TOP_FIELD_FIRST )
1212       mDisplayField = FS_TOP;
1213     else
1214       mDisplayField = FS_BOT;
1215   }
1216
1217   AutoCrop(pPicture);
1218
1219   int buffer = g_renderManager.WaitForBuffer(m_bStop, std::max(DVD_TIME_TO_MSEC(iSleepTime) + 500, 1));
1220   if (buffer < 0)
1221     return EOS_DROPPED;
1222
1223   ProcessOverlays(pPicture, pts);
1224
1225   int index = g_renderManager.AddVideoPicture(*pPicture);
1226
1227   // video device might not be done yet
1228   while (index < 0 && !CThread::m_bStop &&
1229          CDVDClock::GetAbsoluteClock(false) < iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500) )
1230   {
1231     Sleep(1);
1232     index = g_renderManager.AddVideoPicture(*pPicture);
1233   }
1234
1235   if (index < 0)
1236     return EOS_DROPPED;
1237
1238   g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, mDisplayField);
1239
1240   return result;
1241 #else
1242   // no video renderer, let's mark it as dropped
1243   return EOS_DROPPED;
1244 #endif
1245 }
1246
1247 void CDVDPlayerVideo::AutoCrop(DVDVideoPicture *pPicture)
1248 {
1249   if ((pPicture->format == RENDER_FMT_YUV420P) ||
1250      (pPicture->format == RENDER_FMT_NV12) ||
1251      (pPicture->format == RENDER_FMT_YUYV422) ||
1252      (pPicture->format == RENDER_FMT_UYVY422))
1253   {
1254     RECT crop;
1255
1256     if (CMediaSettings::Get().GetCurrentVideoSettings().m_Crop)
1257       AutoCrop(pPicture, crop);
1258     else
1259     { // reset to defaults
1260       crop.left   = 0;
1261       crop.right  = 0;
1262       crop.top    = 0;
1263       crop.bottom = 0;
1264     }
1265
1266     m_crop.x1 += ((float)crop.left   - m_crop.x1) * 0.1;
1267     m_crop.x2 += ((float)crop.right  - m_crop.x2) * 0.1;
1268     m_crop.y1 += ((float)crop.top    - m_crop.y1) * 0.1;
1269     m_crop.y2 += ((float)crop.bottom - m_crop.y2) * 0.1;
1270
1271     crop.left   = MathUtils::round_int(m_crop.x1);
1272     crop.right  = MathUtils::round_int(m_crop.x2);
1273     crop.top    = MathUtils::round_int(m_crop.y1);
1274     crop.bottom = MathUtils::round_int(m_crop.y2);
1275
1276     //compare with hysteresis
1277 # define HYST(n, o) ((n) > (o) || (n) + 1 < (o))
1278     if(HYST(CMediaSettings::Get().GetCurrentVideoSettings().m_CropLeft  , crop.left)
1279     || HYST(CMediaSettings::Get().GetCurrentVideoSettings().m_CropRight , crop.right)
1280     || HYST(CMediaSettings::Get().GetCurrentVideoSettings().m_CropTop   , crop.top)
1281     || HYST(CMediaSettings::Get().GetCurrentVideoSettings().m_CropBottom, crop.bottom))
1282     {
1283       CMediaSettings::Get().GetCurrentVideoSettings().m_CropLeft   = crop.left;
1284       CMediaSettings::Get().GetCurrentVideoSettings().m_CropRight  = crop.right;
1285       CMediaSettings::Get().GetCurrentVideoSettings().m_CropTop    = crop.top;
1286       CMediaSettings::Get().GetCurrentVideoSettings().m_CropBottom = crop.bottom;
1287       g_renderManager.SetViewMode(CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode);
1288     }
1289 # undef HYST
1290   }
1291 }
1292
1293 void CDVDPlayerVideo::AutoCrop(DVDVideoPicture *pPicture, RECT &crop)
1294 {
1295   crop.left   = CMediaSettings::Get().GetCurrentVideoSettings().m_CropLeft;
1296   crop.right  = CMediaSettings::Get().GetCurrentVideoSettings().m_CropRight;
1297   crop.top    = CMediaSettings::Get().GetCurrentVideoSettings().m_CropTop;
1298   crop.bottom = CMediaSettings::Get().GetCurrentVideoSettings().m_CropBottom;
1299
1300   int black  = 16; // what is black in the image
1301   int level  = 8;  // how high above this should we detect
1302   int multi  = 4;  // what multiple of last line should failing line be to accept
1303   uint8_t *s;
1304   int last, detect, black2;
1305
1306   // top and bottom levels
1307   black2 = black * pPicture->iWidth;
1308   detect = level * pPicture->iWidth + black2;
1309
1310   //YV12 and NV12 have planar Y plane
1311   //YUY2 and UYVY have Y packed with U and V
1312   int xspacing = 1;
1313   int xstart   = 0;
1314   if (pPicture->format == RENDER_FMT_YUYV422)
1315     xspacing = 2;
1316   else if (pPicture->format == RENDER_FMT_UYVY422)
1317   {
1318     xspacing = 2;
1319     xstart   = 1;
1320   }
1321
1322   // Crop top
1323   s      = pPicture->data[0];
1324   last   = black2;
1325   for (unsigned int y = 0; y < pPicture->iHeight/2; y++)
1326   {
1327     int total = 0;
1328     for (unsigned int x = xstart; x < pPicture->iWidth * xspacing; x += xspacing)
1329       total += s[x];
1330     s += pPicture->iLineSize[0];
1331
1332     if (total > detect)
1333     {
1334       if (total - black2 > (last - black2) * multi)
1335         crop.top = y;
1336       break;
1337     }
1338     last = total;
1339   }
1340
1341   // Crop bottom
1342   s    = pPicture->data[0] + (pPicture->iHeight-1) * pPicture->iLineSize[0];
1343   last = black2;
1344   for (unsigned int y = (int)pPicture->iHeight; y > pPicture->iHeight/2; y--)
1345   {
1346     int total = 0;
1347     for (unsigned int x = xstart; x < pPicture->iWidth * xspacing; x += xspacing)
1348       total += s[x];
1349     s -= pPicture->iLineSize[0];
1350
1351     if (total > detect)
1352     {
1353       if (total - black2 > (last - black2) * multi)
1354         crop.bottom = pPicture->iHeight - y;
1355       break;
1356     }
1357     last = total;
1358   }
1359
1360   // left and right levels
1361   black2 = black * pPicture->iHeight;
1362   detect = level * pPicture->iHeight + black2;
1363
1364
1365   // Crop left
1366   s    = pPicture->data[0];
1367   last = black2;
1368   for (unsigned int x = xstart; x < pPicture->iWidth/2*xspacing; x += xspacing)
1369   {
1370     int total = 0;
1371     for (unsigned int y = 0; y < pPicture->iHeight; y++)
1372       total += s[y * pPicture->iLineSize[0]];
1373     s++;
1374     if (total > detect)
1375     {
1376       if (total - black2 > (last - black2) * multi)
1377         crop.left = x / xspacing;
1378       break;
1379     }
1380     last = total;
1381   }
1382
1383   // Crop right
1384   s    = pPicture->data[0] + (pPicture->iWidth-1);
1385   last = black2;
1386   for (unsigned int x = (int)pPicture->iWidth*xspacing-1; x > pPicture->iWidth/2*xspacing; x -= xspacing)
1387   {
1388     int total = 0;
1389     for (unsigned int y = 0; y < pPicture->iHeight; y++)
1390       total += s[y * pPicture->iLineSize[0]];
1391     s--;
1392
1393     if (total > detect)
1394     {
1395       if (total - black2 > (last - black2) * multi)
1396         crop.right = pPicture->iWidth - (x / xspacing);
1397       break;
1398     }
1399     last = total;
1400   }
1401
1402   // We always crop equally on each side to get zoom
1403   // effect intead of moving the image. Aslong as the
1404   // max crop isn't much larger than the min crop
1405   // use that.
1406   int min, max;
1407
1408   min = std::min(crop.left, crop.right);
1409   max = std::max(crop.left, crop.right);
1410   if(10 * (max - min) / pPicture->iWidth < 1)
1411     crop.left = crop.right = max;
1412   else
1413     crop.left = crop.right = min;
1414
1415   min = std::min(crop.top, crop.bottom);
1416   max = std::max(crop.top, crop.bottom);
1417   if(10 * (max - min) / pPicture->iHeight < 1)
1418     crop.top = crop.bottom = max;
1419   else
1420     crop.top = crop.bottom = min;
1421 }
1422
1423 std::string CDVDPlayerVideo::GetPlayerInfo()
1424 {
1425   std::ostringstream s;
1426   s << "fr:"     << fixed << setprecision(3) << m_fFrameRate;
1427   s << ", vq:"   << setw(2) << min(99,GetLevel()) << "%";
1428   s << ", dc:"   << m_codecname;
1429   s << ", Mb/s:" << fixed << setprecision(2) << (double)GetVideoBitrate() / (1024.0*1024.0);
1430   s << ", drop:" << m_iDroppedFrames;
1431   s << ", skip:" << g_renderManager.GetSkippedFrames();
1432
1433   int pc = m_pullupCorrection.GetPatternLength();
1434   if (pc > 0)
1435     s << ", pc:" << pc;
1436   else
1437     s << ", pc:none";
1438
1439   return s.str();
1440 }
1441
1442 int CDVDPlayerVideo::GetVideoBitrate()
1443 {
1444   return (int)m_videoStats.GetBitrate();
1445 }
1446
1447 void CDVDPlayerVideo::ResetFrameRateCalc()
1448 {
1449   m_fStableFrameRate = 0.0;
1450   m_iFrameRateCount  = 0;
1451   m_iFrameRateLength = 1;
1452   m_iFrameRateErr    = 0;
1453
1454   m_bAllowDrop       = (!m_bCalcFrameRate && CMediaSettings::Get().GetCurrentVideoSettings().m_ScalingMethod != VS_SCALINGMETHOD_AUTO) ||
1455                         g_advancedSettings.m_videoFpsDetect == 0;
1456 }
1457
1458 #define MAXFRAMERATEDIFF   0.01
1459 #define MAXFRAMESERR    1000
1460
1461 void CDVDPlayerVideo::CalcFrameRate()
1462 {
1463   if (m_iFrameRateLength >= 128 || g_advancedSettings.m_videoFpsDetect == 0)
1464     return; //don't calculate the fps
1465
1466   //only calculate the framerate if sync playback to display is on, adjust refreshrate is on,
1467   //or scaling method is set to auto
1468   if (!m_bCalcFrameRate && CMediaSettings::Get().GetCurrentVideoSettings().m_ScalingMethod != VS_SCALINGMETHOD_AUTO)
1469   {
1470     ResetFrameRateCalc();
1471     return;
1472   }
1473
1474   if (!m_pullupCorrection.HasFullBuffer())
1475     return; //we can only calculate the frameduration if m_pullupCorrection has a full buffer
1476
1477   //see if m_pullupCorrection was able to detect a pattern in the timestamps
1478   //and is able to calculate the correct frame duration from it
1479   double frameduration = m_pullupCorrection.GetFrameDuration();
1480
1481   if (frameduration == DVD_NOPTS_VALUE ||
1482       (g_advancedSettings.m_videoFpsDetect == 1 && m_pullupCorrection.GetPatternLength() > 1))
1483   {
1484     //reset the stored framerates if no good framerate was detected
1485     m_fStableFrameRate = 0.0;
1486     m_iFrameRateCount = 0;
1487     m_iFrameRateErr++;
1488
1489     if (m_iFrameRateErr == MAXFRAMESERR && m_iFrameRateLength == 1)
1490     {
1491       CLog::Log(LOGDEBUG,"%s counted %i frames without being able to calculate the framerate, giving up", __FUNCTION__, m_iFrameRateErr);
1492       m_bAllowDrop = true;
1493       m_iFrameRateLength = 128;
1494     }
1495     return;
1496   }
1497
1498   double framerate = DVD_TIME_BASE / frameduration;
1499
1500   //store the current calculated framerate if we don't have any yet
1501   if (m_iFrameRateCount == 0)
1502   {
1503     m_fStableFrameRate = framerate;
1504     m_iFrameRateCount++;
1505   }
1506   //check if the current detected framerate matches with the stored ones
1507   else if (fabs(m_fStableFrameRate / m_iFrameRateCount - framerate) <= MAXFRAMERATEDIFF)
1508   {
1509     m_fStableFrameRate += framerate; //store the calculated framerate
1510     m_iFrameRateCount++;
1511
1512     //if we've measured m_iFrameRateLength seconds of framerates,
1513     if (m_iFrameRateCount >= MathUtils::round_int(framerate) * m_iFrameRateLength)
1514     {
1515       //store the calculated framerate if it differs too much from m_fFrameRate
1516       if (fabs(m_fFrameRate - (m_fStableFrameRate / m_iFrameRateCount)) > MAXFRAMERATEDIFF || m_bFpsInvalid)
1517       {
1518         CLog::Log(LOGDEBUG,"%s framerate was:%f calculated:%f", __FUNCTION__, m_fFrameRate, m_fStableFrameRate / m_iFrameRateCount);
1519         m_fFrameRate = m_fStableFrameRate / m_iFrameRateCount;
1520         m_bFpsInvalid = false;
1521       }
1522
1523       //reset the stored framerates
1524       m_fStableFrameRate = 0.0;
1525       m_iFrameRateCount = 0;
1526       m_iFrameRateLength *= 2; //double the length we should measure framerates
1527
1528       //we're allowed to drop frames because we calculated a good framerate
1529       m_bAllowDrop = true;
1530     }
1531   }
1532   else //the calculated framerate didn't match, reset the stored ones
1533   {
1534     m_fStableFrameRate = 0.0;
1535     m_iFrameRateCount = 0;
1536   }
1537 }