2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "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"
45 #include "guilib/GraphicContext.h"
46 #include "utils/log.h"
49 using namespace RenderManager;
51 class CPulldownCorrection
59 m_next = m_pattern.end();
62 void init(double fps, int *begin, int *end)
64 std::copy(begin, end, std::back_inserter(m_pattern));
65 m_duration = DVD_TIME_BASE / fps;
67 m_total = std::accumulate(m_pattern.begin(), m_pattern.end(), 0);
68 m_next = m_pattern.begin();
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;
80 return m_duration * m_pattern.size() * *m_next / m_total;
86 if(++m_next == m_pattern.end())
88 m_next = m_pattern.begin();
95 return !m_pattern.empty();
101 std::vector<int> m_pattern;
102 std::vector<int>::iterator m_next;
106 class CDVDMsgVideoCodecChange : public CDVDMsg
109 CDVDMsgVideoCodecChange(const CDVDStreamInfo &hints, CDVDVideoCodec* codec)
110 : CDVDMsg(GENERAL_STREAMCHANGE)
114 ~CDVDMsgVideoCodecChange()
118 CDVDVideoCodec* m_codec;
119 CDVDStreamInfo m_hints;
123 CDVDPlayerVideo::CDVDPlayerVideo( CDVDClock* pClock
124 , CDVDOverlayContainer* pOverlayContainer
125 , CDVDMessageQueue& parent)
126 : CThread("DVDPlayerVideo")
127 , m_messageQueue("video")
128 , m_messageParent(parent)
131 m_pOverlayContainer = pOverlayContainer;
132 m_pTempOverlayPicture = NULL;
133 m_pVideoCodec = NULL;
134 m_pOverlayCodecCC = NULL;
135 m_speed = DVD_PLAYSPEED_NORMAL;
137 m_bRenderSubs = false;
141 m_iSubtitleDelay = 0;
142 m_FlipTimeStamp = 0.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);
150 m_iCurrentPts = DVD_NOPTS_VALUE;
151 m_iDroppedFrames = 0;
153 m_bCalcFrameRate = false;
154 m_fStableFrameRate = 0.0;
155 m_iFrameRateCount = 0;
156 m_bAllowDrop = false;
158 m_iFrameRateLength = 0;
159 m_bFpsInvalid = false;
160 m_bAllowFullscreen = false;
161 memset(&m_output, 0, sizeof(m_output));
164 CDVDPlayerVideo::~CDVDPlayerVideo()
167 g_VideoReferenceClock.StopThread();
170 double CDVDPlayerVideo::GetOutputDelay()
172 double time = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET);
174 time = (time * DVD_TIME_BASE) / m_fFrameRate;
179 time = time * DVD_PLAYSPEED_NORMAL / abs(m_speed);
184 bool CDVDPlayerVideo::OpenStream( CDVDStreamInfo &hint )
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();
194 CLog::Log(LOGNOTICE, "Creating video codec with codec id: %i", hint.codec);
195 CDVDVideoCodec* codec = CDVDFactoryCodec::CreateVideoCodec(hint, surfaces, formats);
198 CLog::Log(LOGERROR, "Unsupported video codec");
202 if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") && !g_VideoReferenceClock.IsRunning())
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");
210 if(m_messageQueue.IsInited())
211 m_messageQueue.Put(new CDVDMsgVideoCodecChange(hint, codec), 0);
214 OpenStream(hint, codec);
215 CLog::Log(LOGNOTICE, "Creating video thread");
216 m_messageQueue.Init();
222 void CDVDPlayerVideo::OpenStream(CDVDStreamInfo &hint, CDVDVideoCodec* codec)
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);
230 m_bFpsInvalid = (hint.fpsrate == 0 || hint.fpsscale == 0);
232 m_bCalcFrameRate = CSettings::Get().GetBool("videoplayer.usedisplayasclock") ||
233 CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF;
234 ResetFrameRateCalc();
236 m_iDroppedRequest = 0;
239 if( m_fFrameRate > 100 || m_fFrameRate < 5 )
241 CLog::Log(LOGERROR, "CDVDPlayerVideo::OpenStream - Invalid framerate %d, using forced 25fps and just trust timestamps", (int)m_fFrameRate);
245 // use aspect in stream if available
246 if(hint.forced_aspect)
247 m_fForcedAspectRatio = hint.aspect;
249 m_fForcedAspectRatio = 0.0;
252 delete m_pVideoCodec;
254 m_pVideoCodec = codec;
256 m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0;
258 m_codecname = m_pVideoCodec->GetName();
262 void CDVDPlayerVideo::CloseStream(bool bWaitForBuffers)
264 // wait until buffers are empty
265 if (bWaitForBuffers && m_speed > 0) m_messageQueue.WaitUntilEmpty();
267 m_messageQueue.Abort();
269 // wait for decode_video thread to end
270 CLog::Log(LOGNOTICE, "waiting for video thread to exit");
272 StopThread(); // will set this->m_bStop to true
274 m_messageQueue.End();
276 CLog::Log(LOGNOTICE, "deleting video codec");
279 m_pVideoCodec->Dispose();
280 delete m_pVideoCodec;
281 m_pVideoCodec = NULL;
284 if (m_pTempOverlayPicture)
286 CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
287 m_pTempOverlayPicture = NULL;
290 //tell the clock we stopped playing video
291 m_pClock->UpdateFramerate(0.0);
294 void CDVDPlayerVideo::OnStartup()
296 m_iDroppedFrames = 0;
298 m_crop.x1 = m_crop.x2 = 0.0f;
299 m_crop.y1 = m_crop.y2 = 0.0f;
301 m_iCurrentPts = DVD_NOPTS_VALUE;
302 m_FlipTimeStamp = m_pClock->GetAbsoluteClock();
306 void CDVDPlayerVideo::Process()
308 CLog::Log(LOGNOTICE, "running thread: video_thread");
310 DVDVideoPicture picture;
311 CPulldownCorrection pulldown;
312 CDVDVideoPPFFmpeg mPostProcess("");
313 CStdString sPostProcessType;
314 bool bPostProcessDeint = false;
316 memset(&picture, 0, sizeof(DVDVideoPicture));
319 double frametime = (double)DVD_TIME_BASE / m_fFrameRate;
321 int iDropped = 0; //frames dropped in a row
322 bool bRequestDrop = false;
324 m_videoStats.Start();
328 int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000;
329 int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0;
332 MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority);
334 if (MSGQ_IS_ERROR(ret))
336 CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true");
339 else if (ret == MSGQ_TIMEOUT)
341 // if we only wanted priority messages, this isn't a stall
345 //Okey, start rendering at stream fps now instead, we are likely in a stillframe
349 CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate);
354 //Waiting timed out, output last picture
355 if( picture.iFlags & DVP_FLAG_ALLOCATED )
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);
368 if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE))
370 if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO))
372 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE");
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;
379 m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */
385 else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))
387 CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;
389 if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE)
390 pts = pMsgGeneralResync->m_timestamp;
392 double delay = m_FlipTimeStamp - m_pClock->GetAbsoluteClock();
393 if( delay > frametime ) delay = frametime;
394 else if( delay < 0 ) delay = 0;
396 if(pMsgGeneralResync->m_clock)
398 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts);
399 m_pClock->Discontinuity(pts - delay);
402 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts);
404 pMsgGeneralResync->Release();
407 else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY))
409 if (m_speed != DVD_PLAYSPEED_PAUSE)
411 double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value;
413 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout);
415 timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed);
416 timeout += CDVDClock::GetAbsoluteClock();
418 while(!m_bStop && CDVDClock::GetAbsoluteClock() < timeout)
422 else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT))
424 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT");
425 m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg);
427 else if (pMsg->IsType(CDVDMsg::GENERAL_RESET))
430 m_pVideoCodec->Reset();
431 picture.iFlags &= ~DVP_FLAG_ALLOCATED;
435 else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush())
438 m_pVideoCodec->Reset();
439 picture.iFlags &= ~DVP_FLAG_ALLOCATED;
442 m_pullupCorrection.Flush();
443 //we need to recalculate the framerate
444 //TODO: this needs to be set on a streamchange instead
445 ResetFrameRateCalc();
450 else if (pMsg->IsType(CDVDMsg::VIDEO_NOSKIP))
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;
458 else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
460 m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
461 if(m_speed == DVD_PLAYSPEED_PAUSE)
462 m_iNrOfPicturesNotToSkip = 0;
464 m_pVideoCodec->SetSpeed(m_speed);
466 else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
469 m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
471 else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME))
473 CDVDPlayer::SPlayerState& state = ((CDVDMsgType<CDVDPlayer::SPlayerState>*)pMsg)->m_value;
475 if(state.time_src == CDVDPlayer::ETIMESOURCE_CLOCK)
476 state.time = DVD_TIME_TO_MSEC(m_pClock->GetClock(state.timestamp) + state.time_offset);
478 state.timestamp = CDVDClock::GetAbsoluteClock();
479 state.player = DVDPLAYER_VIDEO;
480 m_messageParent.Put(pMsg->Acquire());
482 else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE))
484 CDVDMsgVideoCodecChange* msg(static_cast<CDVDMsgVideoCodecChange*>(pMsg));
485 OpenStream(msg->m_hints, msg->m_codec);
487 picture.iFlags &= ~DVP_FLAG_ALLOCATED;
490 if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))
492 DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
493 bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
497 CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe left, switching to normal playback");
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
503 m_iNrOfPicturesNotToSkip = 5;
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;
510 if (m_messageQueue.GetDataSize() == 0
513 bRequestDrop = false;
514 m_iDroppedRequest = 0;
518 // if player want's us to drop this packet, do so nomatter what
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);
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);
532 unsigned int mFilters = 0;
534 if (mDeintMode != VS_DEINTERLACEMODE_OFF)
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;
541 if (mDeintMode == VS_DEINTERLACEMODE_AUTO && mFilters)
542 mFilters |= CDVDVideoCodec::FILTER_DEINTERLACE_FLAGGED;
545 if (!g_renderManager.Supports(RENDERFEATURE_ROTATION))
546 mFilters |= CDVDVideoCodec::FILTER_ROTATE;
548 mFilters = m_pVideoCodec->SetFilters(mFilters);
550 int iDecoderState = m_pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts);
552 // buffer packets so we can recover should decoder flush for some reason
553 if(m_pVideoCodec->GetConvergeCount() > 0)
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();
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))
571 // reset the request, the following while loop may break before
572 // setting the flag to a new value
573 bRequestDrop = false;
575 // loop while no error
579 // if decoder was flushed, we need to seek back again to resume rendering
580 if (iDecoderState & VC_FLUSHED)
582 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder was flushed");
583 while(!m_packets.empty())
585 CDVDMsgDemuxerPacket* msg = (CDVDMsgDemuxerPacket*)m_packets.front().message->Acquire();
586 m_packets.pop_front();
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);
594 m_pVideoCodec->Reset();
596 picture.iFlags &= ~DVP_FLAG_ALLOCATED;
597 g_renderManager.DiscardBuffer();
601 // if decoder had an error, tell it to reset to avoid more problems
602 if (iDecoderState & VC_ERROR)
604 CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder returned error");
608 // check for a new picture
609 if (iDecoderState & VC_PICTURE)
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))
616 sPostProcessType.clear();
618 if(picture.iDuration == 0.0)
619 picture.iDuration = frametime;
622 picture.iFlags |= DVP_FLAG_DROPPED;
624 if (m_iNrOfPicturesNotToSkip > 0)
626 picture.iFlags |= DVP_FLAG_NOSKIP;
627 m_iNrOfPicturesNotToSkip--;
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)
635 else if (picture.pts == DVD_NOPTS_VALUE)
636 picture.pts = picture.dts;
638 /* use forced aspect if any */
639 if( m_fForcedAspectRatio != 0.0f )
640 picture.iDisplayWidth = (int) (picture.iDisplayHeight * m_fForcedAspectRatio);
642 //Deinterlace if codec said format was interlaced or if we have selected we want to deinterlace
644 if ((mDeintMode == VS_DEINTERLACEMODE_AUTO && (picture.iFlags & DVP_FLAG_INTERLACED)) || mDeintMode == VS_DEINTERLACEMODE_FORCE)
646 if(mInt == VS_INTERLACEMETHOD_SW_BLEND)
648 if (!sPostProcessType.empty())
649 sPostProcessType += ",";
650 sPostProcessType += g_advancedSettings.m_videoPPFFmpegDeint;
651 bPostProcessDeint = true;
655 if (CMediaSettings::Get().GetCurrentVideoSettings().m_PostProcess)
657 if (!sPostProcessType.empty())
658 sPostProcessType += ",";
659 // This is what mplayer uses for its "high-quality filter combination"
660 sPostProcessType += g_advancedSettings.m_videoPPFFmpegPostProc;
663 if (!sPostProcessType.empty())
665 mPostProcess.SetType(sPostProcessType, bPostProcessDeint);
666 if (mPostProcess.Process(&picture))
667 mPostProcess.GetPicture(&picture);
670 /* if frame has a pts (usually originiating from demux packet), use that */
671 if(picture.pts != DVD_NOPTS_VALUE)
673 if(pulldown.enabled())
674 picture.pts += pulldown.pts();
679 if(pulldown.enabled())
681 picture.iDuration = pulldown.dur();
685 if (picture.iRepeatPicture)
686 picture.iDuration *= picture.iRepeatPicture + 1;
688 int iResult = OutputPicture(&picture, pts);
690 if(m_started == false)
692 m_codecname = m_pVideoCodec->GetName();
694 m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO));
697 // guess next frame pts. iDuration is always valid
699 pts += picture.iDuration * m_speed / abs(m_speed);
701 if( iResult & EOS_ABORT )
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);
711 if( (iResult & EOS_DROPPED) && !bPacketDrop )
719 bRequestDrop = (iResult & EOS_VERYLATE) == EOS_VERYLATE;
723 CLog::Log(LOGWARNING, "Decoder Error getting videoPicture.");
724 m_pVideoCodec->Reset();
729 if (iDecoderState & VC_USERDATA)
731 // found some userdata while decoding a frame
732 // could be closed captioning
733 DVDVideoUserData videoUserData;
734 if (m_pVideoCodec->GetUserData(&videoUserData))
736 ProcessVideoUserData(&videoUserData, pts);
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)
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);
751 // all data is used by the decoder, we can safely free it now
755 // we need to let decoder release any picture retained resources.
756 m_pVideoCodec->ClearPicture(&picture);
759 void CDVDPlayerVideo::OnExit()
761 if (m_pOverlayCodecCC)
763 m_pOverlayCodecCC->Dispose();
764 m_pOverlayCodecCC = NULL;
767 CLog::Log(LOGNOTICE, "thread end: video_thread");
770 void CDVDPlayerVideo::ProcessVideoUserData(DVDVideoUserData* pVideoUserData, double pts)
772 // check userdata type
773 uint8_t* data = pVideoUserData->data;
774 int size = pVideoUserData->size;
778 if (data[0] == 'C' && data[1] == 'C')
784 if (!m_pOverlayCodecCC)
786 m_pOverlayCodecCC = new CDVDOverlayCodecCC();
787 CDVDCodecOptions options;
789 if (!m_pOverlayCodecCC->Open(info, options))
791 delete m_pOverlayCodecCC;
792 m_pOverlayCodecCC = NULL;
796 if (m_pOverlayCodecCC)
801 packet.pts = DVD_NOPTS_VALUE;
802 packet.dts = DVD_NOPTS_VALUE;
803 m_pOverlayCodecCC->Decode(&packet);
805 CDVDOverlay* overlay;
806 while((overlay = m_pOverlayCodecCC->GetOverlay()) != NULL)
808 overlay->iPTSStartTime += pts;
809 if(overlay->iPTSStopTime != 0.0)
810 overlay->iPTSStopTime += pts;
812 m_pOverlayContainer->Add(overlay);
820 bool CDVDPlayerVideo::InitializedOutputDevice()
822 #ifdef HAS_VIDEO_PLAYBACK
823 return g_renderManager.IsStarted();
829 void CDVDPlayerVideo::SetSpeed(int speed)
831 if(m_messageQueue.IsInited())
832 m_messageQueue.Put( new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed), 1 );
837 void CDVDPlayerVideo::StepFrame()
839 m_iNrOfPicturesNotToSkip++;
842 void CDVDPlayerVideo::Flush()
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);
851 int CDVDPlayerVideo::GetLevel()
853 int level = m_messageQueue.GetLevel();
855 // fast exit, if the message queue is full, we do not care about the codec queue.
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())
863 int datasize = m_messageQueue.GetDataSize();
865 datasize += m_pVideoCodec->GetDataSize();
866 return min(100, (int)(100 * datasize / (m_messageQueue.GetMaxDataSize() * m_messageQueue.GetMaxTimeSize())));
870 double timesize = m_messageQueue.GetTimeSize();
872 timesize += m_pVideoCodec->GetTimeSize();
873 return min(100, MathUtils::round_int(100.0 * m_messageQueue.GetMaxTimeSize() * timesize));
879 #ifdef HAS_VIDEO_PLAYBACK
880 void CDVDPlayerVideo::ProcessOverlays(DVDVideoPicture* pSource, double pts)
882 // remove any overlays that are out of time
884 m_pOverlayContainer->CleanUp(pts - m_iSubtitleDelay);
887 { OVERLAY_AUTO // select mode auto
888 , OVERLAY_GPU // render osd using gpu
889 , OVERLAY_BUF // render osd on buffer
890 } render = OVERLAY_AUTO;
892 if(pSource->format == RENDER_FMT_YUV420P)
894 if(g_Windowing.GetRenderQuirks() & RENDER_QUIRKS_MAJORMEMLEAK_OVERLAYRENDERER)
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
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;
906 if(render == OVERLAY_BUF)
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!.
913 if(m_pTempOverlayPicture && ( m_pTempOverlayPicture->iWidth != pSource->iWidth
914 || m_pTempOverlayPicture->iHeight != pSource->iHeight))
916 CDVDCodecUtils::FreePicture(m_pTempOverlayPicture);
917 m_pTempOverlayPicture = NULL;
920 if(!m_pTempOverlayPicture)
921 m_pTempOverlayPicture = CDVDCodecUtils::AllocatePicture(pSource->iWidth, pSource->iHeight);
922 if(!m_pTempOverlayPicture)
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));
931 if(render == OVERLAY_AUTO)
932 render = OVERLAY_GPU;
934 VecOverlays overlays;
937 CSingleLock lock(*m_pOverlayContainer);
939 VecOverlays* pVecOverlays = m_pOverlayContainer->GetOverlays();
940 VecOverlaysIter it = pVecOverlays->begin();
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())
946 CDVDOverlay* pOverlay = *it++;
947 if(!pOverlay->bForced && !m_bRenderSubs)
950 double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay;
952 if((pOverlay->iPTSStartTime <= pts2 && (pOverlay->iPTSStopTime > pts2 || pOverlay->iPTSStopTime == 0LL)))
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());
958 overlays.push_back(pOverlay);
963 for(it = overlays.begin(); it != overlays.end(); ++it)
965 double pts2 = (*it)->bForced ? pts : pts - m_iSubtitleDelay;
967 if (render == OVERLAY_GPU)
968 g_renderManager.AddOverlay(*it, pts2);
970 if (render == OVERLAY_BUF)
971 CDVDOverlayRenderer::Render(pSource, *it, pts2);
979 static std::string GetRenderFormatName(ERenderFormat format)
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";
1003 std::string CDVDPlayerVideo::GetStereoMode()
1005 std::string stereo_mode;
1007 switch(CMediaSettings::Get().GetCurrentVideoSettings().m_StereoMode)
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;
1014 if(CMediaSettings::Get().GetCurrentVideoSettings().m_StereoInvert)
1015 stereo_mode = GetStereoModeInvert(stereo_mode);
1019 int CDVDPlayerVideo::OutputPicture(const DVDVideoPicture* src, double pts)
1021 /* picture buffer is not allowed to be modified in this call */
1022 DVDVideoPicture picture(*src);
1023 DVDVideoPicture* pPicture = &picture;
1025 /* grab stereo mode from image if available */
1026 if(src->stereo_mode[0])
1027 m_hints.stereo_mode = src->stereo_mode;
1029 /* figure out steremode expected based on user settings and hints */
1030 unsigned int stereo_flags = GetStereoModeFlags(GetStereoMode());
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))
1050 CLog::Log(LOGNOTICE, " fps: %f, pwidth: %i, pheight: %i, dwidth: %i, dheight: %i"
1054 , pPicture->iDisplayWidth
1055 , pPicture->iDisplayHeight);
1058 if(pPicture->color_range == 1)
1059 flags |= CONF_FLAGS_YUV_FULLRANGE;
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);
1066 CStdString formatstr = GetRenderFormatName(pPicture->format);
1068 if(m_bAllowFullscreen)
1070 flags |= CONF_FLAGS_FULLSCREEN;
1071 m_bAllowFullscreen = false; // only allow on first configure
1074 flags |= stereo_flags;
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
1079 , pPicture->iDisplayWidth
1080 , pPicture->iDisplayHeight
1084 , pPicture->extended_format
1085 , m_hints.orientation
1086 , m_pVideoCodec->GetAllowedReferences()))
1088 CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__);
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;
1109 if (!g_renderManager.IsStarted()) {
1110 CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__);
1114 //correct any pattern in the timestamps
1115 if (m_output.color_format != RENDER_FMT_BYPASS)
1117 m_pullupCorrection.Add(pts);
1118 pts += m_pullupCorrection.GetCorrection();
1121 //try to calculate the framerate
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
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;
1133 if (m_output.color_format != RENDER_FMT_BYPASS)
1135 // Correct pts by user set delay and rendering delay
1136 pts += m_iVideoDelay - DVD_SEC_TO_TIME(g_renderManager.GetDisplayLatency());
1139 // calculate the time we need to delay this picture before displaying
1140 double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration;
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;
1147 // correct sleep times based on speed
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);
1160 if( m_started == false )
1162 else if( m_stalled )
1163 iSleepTime = iFrameSleep;
1165 iSleepTime = iClockSleep;
1167 // present the current pts of this frame to user, and include the actual
1168 // presentation delay, to allow him to adjust for it
1170 m_iCurrentPts = DVD_NOPTS_VALUE;
1172 m_iCurrentPts = pts - max(0.0, iSleepTime);
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;
1179 if (iSleepTime <= 0 && m_speed)
1184 // ask decoder to drop frames next round, as we are very late
1185 if(m_iLateFrames > 10)
1187 if (!(pPicture->iFlags & DVP_FLAG_NOSKIP))
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)
1193 result |= EOS_VERYLATE;
1194 m_pullupCorrection.Flush(); //dropped frames mess up the pattern, so just flush it
1196 m_iDroppedRequest++;
1201 m_iDroppedRequest = 0;
1204 if( (pPicture->iFlags & DVP_FLAG_DROPPED) )
1205 return result | EOS_DROPPED;
1207 // set fieldsync if picture is interlaced
1208 EFIELDSYNC mDisplayField = FS_NONE;
1209 if( pPicture->iFlags & DVP_FLAG_INTERLACED )
1211 if( pPicture->iFlags & DVP_FLAG_TOP_FIELD_FIRST )
1212 mDisplayField = FS_TOP;
1214 mDisplayField = FS_BOT;
1219 int buffer = g_renderManager.WaitForBuffer(m_bStop, std::max(DVD_TIME_TO_MSEC(iSleepTime) + 500, 1));
1223 ProcessOverlays(pPicture, pts);
1225 int index = g_renderManager.AddVideoPicture(*pPicture);
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) )
1232 index = g_renderManager.AddVideoPicture(*pPicture);
1238 g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, mDisplayField);
1242 // no video renderer, let's mark it as dropped
1247 void CDVDPlayerVideo::AutoCrop(DVDVideoPicture *pPicture)
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))
1256 if (CMediaSettings::Get().GetCurrentVideoSettings().m_Crop)
1257 AutoCrop(pPicture, crop);
1259 { // reset to defaults
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;
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);
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))
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);
1293 void CDVDPlayerVideo::AutoCrop(DVDVideoPicture *pPicture, RECT &crop)
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;
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
1304 int last, detect, black2;
1306 // top and bottom levels
1307 black2 = black * pPicture->iWidth;
1308 detect = level * pPicture->iWidth + black2;
1310 //YV12 and NV12 have planar Y plane
1311 //YUY2 and UYVY have Y packed with U and V
1314 if (pPicture->format == RENDER_FMT_YUYV422)
1316 else if (pPicture->format == RENDER_FMT_UYVY422)
1323 s = pPicture->data[0];
1325 for (unsigned int y = 0; y < pPicture->iHeight/2; y++)
1328 for (unsigned int x = xstart; x < pPicture->iWidth * xspacing; x += xspacing)
1330 s += pPicture->iLineSize[0];
1334 if (total - black2 > (last - black2) * multi)
1342 s = pPicture->data[0] + (pPicture->iHeight-1) * pPicture->iLineSize[0];
1344 for (unsigned int y = (int)pPicture->iHeight; y > pPicture->iHeight/2; y--)
1347 for (unsigned int x = xstart; x < pPicture->iWidth * xspacing; x += xspacing)
1349 s -= pPicture->iLineSize[0];
1353 if (total - black2 > (last - black2) * multi)
1354 crop.bottom = pPicture->iHeight - y;
1360 // left and right levels
1361 black2 = black * pPicture->iHeight;
1362 detect = level * pPicture->iHeight + black2;
1366 s = pPicture->data[0];
1368 for (unsigned int x = xstart; x < pPicture->iWidth/2*xspacing; x += xspacing)
1371 for (unsigned int y = 0; y < pPicture->iHeight; y++)
1372 total += s[y * pPicture->iLineSize[0]];
1376 if (total - black2 > (last - black2) * multi)
1377 crop.left = x / xspacing;
1384 s = pPicture->data[0] + (pPicture->iWidth-1);
1386 for (unsigned int x = (int)pPicture->iWidth*xspacing-1; x > pPicture->iWidth/2*xspacing; x -= xspacing)
1389 for (unsigned int y = 0; y < pPicture->iHeight; y++)
1390 total += s[y * pPicture->iLineSize[0]];
1395 if (total - black2 > (last - black2) * multi)
1396 crop.right = pPicture->iWidth - (x / xspacing);
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
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;
1413 crop.left = crop.right = min;
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;
1420 crop.top = crop.bottom = min;
1423 std::string CDVDPlayerVideo::GetPlayerInfo()
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();
1433 int pc = m_pullupCorrection.GetPatternLength();
1442 int CDVDPlayerVideo::GetVideoBitrate()
1444 return (int)m_videoStats.GetBitrate();
1447 void CDVDPlayerVideo::ResetFrameRateCalc()
1449 m_fStableFrameRate = 0.0;
1450 m_iFrameRateCount = 0;
1451 m_iFrameRateLength = 1;
1452 m_iFrameRateErr = 0;
1454 m_bAllowDrop = (!m_bCalcFrameRate && CMediaSettings::Get().GetCurrentVideoSettings().m_ScalingMethod != VS_SCALINGMETHOD_AUTO) ||
1455 g_advancedSettings.m_videoFpsDetect == 0;
1458 #define MAXFRAMERATEDIFF 0.01
1459 #define MAXFRAMESERR 1000
1461 void CDVDPlayerVideo::CalcFrameRate()
1463 if (m_iFrameRateLength >= 128 || g_advancedSettings.m_videoFpsDetect == 0)
1464 return; //don't calculate the fps
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)
1470 ResetFrameRateCalc();
1474 if (!m_pullupCorrection.HasFullBuffer())
1475 return; //we can only calculate the frameduration if m_pullupCorrection has a full buffer
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();
1481 if (frameduration == DVD_NOPTS_VALUE ||
1482 (g_advancedSettings.m_videoFpsDetect == 1 && m_pullupCorrection.GetPatternLength() > 1))
1484 //reset the stored framerates if no good framerate was detected
1485 m_fStableFrameRate = 0.0;
1486 m_iFrameRateCount = 0;
1489 if (m_iFrameRateErr == MAXFRAMESERR && m_iFrameRateLength == 1)
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;
1498 double framerate = DVD_TIME_BASE / frameduration;
1500 //store the current calculated framerate if we don't have any yet
1501 if (m_iFrameRateCount == 0)
1503 m_fStableFrameRate = framerate;
1504 m_iFrameRateCount++;
1506 //check if the current detected framerate matches with the stored ones
1507 else if (fabs(m_fStableFrameRate / m_iFrameRateCount - framerate) <= MAXFRAMERATEDIFF)
1509 m_fStableFrameRate += framerate; //store the calculated framerate
1510 m_iFrameRateCount++;
1512 //if we've measured m_iFrameRateLength seconds of framerates,
1513 if (m_iFrameRateCount >= MathUtils::round_int(framerate) * m_iFrameRateLength)
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)
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;
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
1528 //we're allowed to drop frames because we calculated a good framerate
1529 m_bAllowDrop = true;
1532 else //the calculated framerate didn't match, reset the stored ones
1534 m_fStableFrameRate = 0.0;
1535 m_iFrameRateCount = 0;