diff options
-rw-r--r-- | XBMC-ATV2.xcodeproj/project.pbxproj | 6 | ||||
-rw-r--r-- | XBMC-IOS.xcodeproj/project.pbxproj | 6 | ||||
-rw-r--r-- | XBMC.xcodeproj/project.pbxproj | 12 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj | 4 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj.filters | 8 | ||||
-rw-r--r-- | xbmc/Application.cpp | 57 | ||||
-rw-r--r-- | xbmc/Application.h | 2 | ||||
-rw-r--r-- | xbmc/XBApplicationEx.cpp | 2 | ||||
-rw-r--r-- | xbmc/dialogs/GUIDialogProgress.cpp | 2 | ||||
-rw-r--r-- | xbmc/guilib/GUIWindow.cpp | 1 | ||||
-rw-r--r-- | xbmc/guilib/GUIWindowManager.cpp | 7 | ||||
-rw-r--r-- | xbmc/guilib/IWindowManagerCallback.h | 2 | ||||
-rw-r--r-- | xbmc/utils/Makefile | 1 | ||||
-rw-r--r-- | xbmc/utils/TimeSmoother.cpp | 238 | ||||
-rw-r--r-- | xbmc/utils/TimeSmoother.h | 188 | ||||
-rw-r--r-- | xbmc/utils/TimeUtils.cpp | 12 | ||||
-rw-r--r-- | xbmc/utils/TimeUtils.h | 4 |
17 files changed, 504 insertions, 48 deletions
diff --git a/XBMC-ATV2.xcodeproj/project.pbxproj b/XBMC-ATV2.xcodeproj/project.pbxproj index c9ef42412f..1406b54786 100644 --- a/XBMC-ATV2.xcodeproj/project.pbxproj +++ b/XBMC-ATV2.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 7C89627013B702F3003631FE /* GUIWindowScreensaverDim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C89626E13B702F3003631FE /* GUIWindowScreensaverDim.cpp */; }; 7C99B73F133D372300FC2B16 /* CacheCircular.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B73D133D372300FC2B16 /* CacheCircular.cpp */; }; 7C99B7AA134072CD00FC2B16 /* GUIDialogPlayEject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B7A8134072CD00FC2B16 /* GUIDialogPlayEject.cpp */; }; + 7CEE2E6D13D6B7A8000ABF2A /* TimeSmoother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEE2E6B13D6B7A8000ABF2A /* TimeSmoother.cpp */; }; C807119F135DB842002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C807119D135DB842002F601B /* InputOperations.cpp */; }; C8EC5D51136954E400CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */; }; DF0DF16C13A3AF82008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF16813A3AF82008ED511 /* FileNFS.cpp */; }; @@ -957,6 +958,8 @@ 7C99B73E133D372300FC2B16 /* CacheCircular.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheCircular.h; sourceTree = "<group>"; }; 7C99B7A8134072CD00FC2B16 /* GUIDialogPlayEject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPlayEject.cpp; sourceTree = "<group>"; }; 7C99B7A9134072CD00FC2B16 /* GUIDialogPlayEject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPlayEject.h; sourceTree = "<group>"; }; + 7CEE2E6B13D6B7A8000ABF2A /* TimeSmoother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeSmoother.cpp; sourceTree = "<group>"; }; + 7CEE2E6C13D6B7A8000ABF2A /* TimeSmoother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeSmoother.h; sourceTree = "<group>"; }; 8316267613B670FF004AED87 /* README.ios */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.ios; sourceTree = "<group>"; }; 8D576316048677EA00EA77CD /* XBMC.frappliance */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XBMC.frappliance; sourceTree = BUILT_PRODUCTS_DIR; }; A192FD47135E46C800D92E9B /* IOSAudioRingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOSAudioRingBuffer.h; path = AudioRenderers/IOSAudioRingBuffer.h; sourceTree = "<group>"; }; @@ -5149,6 +5152,8 @@ 18ECC9A913CF17EB00A9ED6C /* StreamUtils.h */, F56C7768131EC154000AD0F6 /* SystemInfo.cpp */, F56C7769131EC154000AD0F6 /* SystemInfo.h */, + 7CEE2E6B13D6B7A8000ABF2A /* TimeSmoother.cpp */, + 7CEE2E6C13D6B7A8000ABF2A /* TimeSmoother.h */, F56C776A131EC154000AD0F6 /* TimeUtils.cpp */, F56C776B131EC154000AD0F6 /* TimeUtils.h */, F56C776C131EC154000AD0F6 /* TuxBoxUtil.cpp */, @@ -6754,6 +6759,7 @@ F5CEE72C13D3F9AC00225F72 /* DVDOverlayCodecTX3G.cpp in Sources */, DFD4D22013D7286E00A47C47 /* Implementation.cpp in Sources */, DFD4D22213D7286E00A47C47 /* SystemClock.cpp in Sources */, + 7CEE2E6D13D6B7A8000ABF2A /* TimeSmoother.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/XBMC-IOS.xcodeproj/project.pbxproj b/XBMC-IOS.xcodeproj/project.pbxproj index ff15fe717d..0ceb49e6b6 100644 --- a/XBMC-IOS.xcodeproj/project.pbxproj +++ b/XBMC-IOS.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 7C89628013B7031E003631FE /* GUIWindowScreensaverDim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C89627E13B7031E003631FE /* GUIWindowScreensaverDim.cpp */; }; 7C99B6E9133D36E200FC2B16 /* CacheCircular.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B6E7133D36E200FC2B16 /* CacheCircular.cpp */; }; 7C99B7BE1340730000FC2B16 /* GUIDialogPlayEject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C99B7BC1340730000FC2B16 /* GUIDialogPlayEject.cpp */; }; + 7CEE2E7F13D6B7D4000ABF2A /* TimeSmoother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */; }; C80711AD135DB85F002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80711AB135DB85F002F601B /* InputOperations.cpp */; }; C8EC5D26136953E100CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */; }; DF0DF17F13A3AF9F008ED511 /* FileNFS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF17B13A3AF9F008ED511 /* FileNFS.cpp */; }; @@ -958,6 +959,8 @@ 7C99B6E8133D36E200FC2B16 /* CacheCircular.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheCircular.h; sourceTree = "<group>"; }; 7C99B7BC1340730000FC2B16 /* GUIDialogPlayEject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPlayEject.cpp; sourceTree = "<group>"; }; 7C99B7BD1340730000FC2B16 /* GUIDialogPlayEject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPlayEject.h; sourceTree = "<group>"; }; + 7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeSmoother.cpp; sourceTree = "<group>"; }; + 7CEE2E7E13D6B7D4000ABF2A /* TimeSmoother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeSmoother.h; sourceTree = "<group>"; }; 83D619BB13C0D25300418A0F /* README.ios */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.ios; sourceTree = "<group>"; }; 8D576316048677EA00EA77CD /* XBMC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XBMC.app; sourceTree = BUILT_PRODUCTS_DIR; }; C80711AB135DB85F002F601B /* InputOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputOperations.cpp; sourceTree = "<group>"; }; @@ -5517,6 +5520,8 @@ 18ECC99C13CF17D200A9ED6C /* StreamUtils.h */, F56C8757131F42EC000AD0F6 /* SystemInfo.cpp */, F56C8758131F42EC000AD0F6 /* SystemInfo.h */, + 7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */, + 7CEE2E7E13D6B7D4000ABF2A /* TimeSmoother.h */, F56C8759131F42EC000AD0F6 /* TimeUtils.cpp */, F56C875A131F42EC000AD0F6 /* TimeUtils.h */, F56C875B131F42EC000AD0F6 /* TuxBoxUtil.cpp */, @@ -6769,6 +6774,7 @@ F5CEE73013D3F9D100225F72 /* DVDOverlayCodecTX3G.cpp in Sources */, DFD4D1E213D725ED00A47C47 /* Implementation.cpp in Sources */, DFD4D1FE13D7283500A47C47 /* SystemClock.cpp in Sources */, + 7CEE2E7F13D6B7D4000ABF2A /* TimeSmoother.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj index 06c329bd81..df04f5e86a 100644 --- a/XBMC.xcodeproj/project.pbxproj +++ b/XBMC.xcodeproj/project.pbxproj @@ -580,6 +580,8 @@ 7CDAEA8E1001EBA70040B25F /* PltConstants.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CDAEA891001EBA70040B25F /* PltConstants.cpp */; }; 7CDAEA8F1001EBA70040B25F /* PltIconsData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CDAEA8B1001EBA70040B25F /* PltIconsData.cpp */; }; 7CEBD8A80F33A0D800CAF6AD /* SpecialProtocolDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEBD8A60F33A0D800CAF6AD /* SpecialProtocolDirectory.cpp */; }; + 7CEE2E5B13D6B71E000ABF2A /* TimeSmoother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEE2E5913D6B71E000ABF2A /* TimeSmoother.cpp */; }; + 7CEE2E5C13D6B71E000ABF2A /* TimeSmoother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEE2E5913D6B71E000ABF2A /* TimeSmoother.cpp */; }; 7CF1FB0B123B1AF000B2CBCB /* Variant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CF1FB09123B1AF000B2CBCB /* Variant.cpp */; }; 7CF1FB0C123B1AF000B2CBCB /* Variant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CF1FB09123B1AF000B2CBCB /* Variant.cpp */; }; 810C9F630D67BD2F0095F5DD /* PltMediaConnect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810C9F600D67BD2F0095F5DD /* PltMediaConnect.cpp */; }; @@ -2475,6 +2477,8 @@ 7CDAEA8B1001EBA70040B25F /* PltIconsData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PltIconsData.cpp; sourceTree = "<group>"; }; 7CEBD8A60F33A0D800CAF6AD /* SpecialProtocolDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpecialProtocolDirectory.cpp; sourceTree = "<group>"; }; 7CEBD8A70F33A0D800CAF6AD /* SpecialProtocolDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpecialProtocolDirectory.h; sourceTree = "<group>"; }; + 7CEE2E5913D6B71E000ABF2A /* TimeSmoother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeSmoother.cpp; sourceTree = "<group>"; }; + 7CEE2E5A13D6B71E000ABF2A /* TimeSmoother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeSmoother.h; sourceTree = "<group>"; }; 7CF1FB09123B1AF000B2CBCB /* Variant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Variant.cpp; sourceTree = "<group>"; }; 7CF1FB0A123B1AF000B2CBCB /* Variant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Variant.h; sourceTree = "<group>"; }; 810C9F600D67BD2F0095F5DD /* PltMediaConnect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PltMediaConnect.cpp; path = MediaConnect/PltMediaConnect.cpp; sourceTree = "<group>"; }; @@ -6771,6 +6775,8 @@ 18ECC96113CF178D00A9ED6C /* StreamUtils.h */, E38E1E830D25F9FD00618676 /* SystemInfo.cpp */, E38E1E840D25F9FD00618676 /* SystemInfo.h */, + 7CEE2E5913D6B71E000ABF2A /* TimeSmoother.cpp */, + 7CEE2E5A13D6B71E000ABF2A /* TimeSmoother.h */, 7CCF7FC7106A0DF500992676 /* TimeUtils.cpp */, 7CCF7FC8106A0DF500992676 /* TimeUtils.h */, E38E1E890D25F9FD00618676 /* TuxBoxUtil.cpp */, @@ -8069,6 +8075,7 @@ F5CEE60913D3C89700225F72 /* DVDOverlayCodecTX3G.cpp in Sources */, 38F4E57013CCCB3B00664821 /* Implementation.cpp in Sources */, 3802709A13D5A653009493DD /* SystemClock.cpp in Sources */, + 7CEE2E5B13D6B71E000ABF2A /* TimeSmoother.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -8959,10 +8966,7 @@ 7C89619313B6A16F003631FE /* GUIWindowScreensaverDim.cpp in Sources */, 1830212913B8E2DC00770920 /* controledit.cpp in Sources */, 43B0A5D013C64DF900B2382A /* InertialScrollingHandler.cpp in Sources */, - 18ECC96313CF178D00A9ED6C /* StreamUtils.cpp in Sources */, - F5CEE60A13D3C89700225F72 /* DVDOverlayCodecTX3G.cpp in Sources */, - 38F4E57213CCCB3B00664821 /* Implementation.cpp in Sources */, - 3802709B13D5A653009493DD /* SystemClock.cpp in Sources */, + 7CEE2E5C13D6B71E000ABF2A /* TimeSmoother.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index ca85b8083e..304d555804 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -789,6 +789,7 @@ <ClCompile Include="..\..\xbmc\utils\StreamUtils.cpp" /> <ClCompile Include="..\..\xbmc\utils\StringUtils.cpp" /> <ClCompile Include="..\..\xbmc\utils\SystemInfo.cpp" /> + <ClCompile Include="..\..\xbmc\utils\TimeSmoother.cpp" /> <ClCompile Include="..\..\xbmc\utils\TimeUtils.cpp" /> <ClCompile Include="..\..\xbmc\utils\TuxBoxUtil.cpp" /> <ClCompile Include="..\..\xbmc\utils\URIUtils.cpp" /> @@ -1641,6 +1642,7 @@ <ClInclude Include="..\..\xbmc\utils\StreamUtils.h" /> <ClInclude Include="..\..\xbmc\utils\StringUtils.h" /> <ClInclude Include="..\..\xbmc\utils\SystemInfo.h" /> + <ClInclude Include="..\..\xbmc\utils\TimeSmoother.h" /> <ClInclude Include="..\..\xbmc\utils\TimeUtils.h" /> <ClInclude Include="..\..\xbmc\utils\TuxBoxUtil.h" /> <ClInclude Include="..\..\xbmc\utils\URIUtils.h" /> @@ -2072,4 +2074,4 @@ </VisualStudio> </ProjectExtensions> <Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" /> -</Project>
\ No newline at end of file +</Project> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index 09caa19207..a1513def78 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -1970,6 +1970,9 @@ <ClCompile Include="..\..\xbmc\utils\SystemInfo.cpp"> <Filter>utils</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\utils\TimeSmoother.cpp"> + <Filter>utils</Filter> + </ClCompile> <ClCompile Include="..\..\xbmc\utils\TimeUtils.cpp"> <Filter>utils</Filter> </ClCompile> @@ -4484,6 +4487,9 @@ <ClInclude Include="..\..\xbmc\utils\SystemInfo.h"> <Filter>utils</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\utils\TimeSmoother.h"> + <Filter>utils</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\utils\TimeUtils.h"> <Filter>utils</Filter> </ClInclude> @@ -5001,4 +5007,4 @@ <Filter>win32</Filter> </CustomBuild> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 8a2ae520a5..97f9a86b68 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -1974,7 +1974,6 @@ void CApplication::Render() } CSingleLock lock(g_graphicsContext); - CTimeUtils::UpdateFrameTime(); g_infoManager.UpdateFPS(); if (g_graphicsContext.IsFullScreenVideo() && IsPlaying() && vsync_mode == VSYNC_VIDEO) @@ -2027,6 +2026,7 @@ void CApplication::Render() if (flip) g_graphicsContext.Flip(g_windowManager.GetDirty()); + CTimeUtils::UpdateFrameTime(flip); g_renderManager.UpdateResolution(); g_renderManager.ManageCaptures(); @@ -2580,44 +2580,45 @@ void CApplication::UpdateLCD() #endif } -void CApplication::FrameMove() +void CApplication::FrameMove(bool processEvents) { MEASURE_FUNCTION; - // currently we calculate the repeat time (ie time from last similar keypress) just global as fps - float frameTime = m_frameTime.GetElapsedSeconds(); - m_frameTime.StartZero(); - // never set a frametime less than 2 fps to avoid problems when debuggin and on breaks - if( frameTime > 0.5 ) frameTime = 0.5; - - g_graphicsContext.Lock(); - // check if there are notifications to display - CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); - if (toast && toast->DoWork()) + if (processEvents) { - if (!toast->IsDialogRunning()) + // currently we calculate the repeat time (ie time from last similar keypress) just global as fps + float frameTime = m_frameTime.GetElapsedSeconds(); + m_frameTime.StartZero(); + // never set a frametime less than 2 fps to avoid problems when debuggin and on breaks + if( frameTime > 0.5 ) frameTime = 0.5; + + g_graphicsContext.Lock(); + // check if there are notifications to display + CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); + if (toast && toast->DoWork()) { - toast->Show(); + if (!toast->IsDialogRunning()) + { + toast->Show(); + } } - } - g_graphicsContext.Unlock(); + g_graphicsContext.Unlock(); - UpdateLCD(); + UpdateLCD(); #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) - // Read the input from a remote - g_RemoteControl.Update(); + // Read the input from a remote + g_RemoteControl.Update(); #endif - // process input actions - CWinEvents::MessagePump(); - ProcessHTTPApiButtons(); - ProcessRemote(frameTime); - ProcessGamepad(frameTime); - ProcessEventServer(frameTime); - m_pInertialScrollingHandler->ProcessInertialScroll(frameTime); - - // Process events and animate controls + // process input actions + CWinEvents::MessagePump(); + ProcessHTTPApiButtons(); + ProcessRemote(frameTime); + ProcessGamepad(frameTime); + ProcessEventServer(frameTime); + m_pInertialScrollingHandler->ProcessInertialScroll(frameTime); + } if (!m_bStop) g_windowManager.Process(CTimeUtils::GetFrameTime()); g_windowManager.FrameMove(); diff --git a/xbmc/Application.h b/xbmc/Application.h index 5b9fb6580d..3b0e5880fe 100644 --- a/xbmc/Application.h +++ b/xbmc/Application.h @@ -86,7 +86,7 @@ public: CApplication(void); virtual ~CApplication(void); virtual bool Initialize(); - virtual void FrameMove(); + virtual void FrameMove(bool processEvents); virtual void Render(); virtual bool RenderNoPresent(); virtual void Preflight(); diff --git a/xbmc/XBApplicationEx.cpp b/xbmc/XBApplicationEx.cpp index 50b9c91467..8ec46b999c 100644 --- a/xbmc/XBApplicationEx.cpp +++ b/xbmc/XBApplicationEx.cpp @@ -117,7 +117,7 @@ INT CXBApplicationEx::Run() try { #endif - if (!m_bStop) FrameMove(); + if (!m_bStop) FrameMove(true); //reset exception count frameMoveExceptionCount = 0; diff --git a/xbmc/dialogs/GUIDialogProgress.cpp b/xbmc/dialogs/GUIDialogProgress.cpp index 7ea4c408bb..6673e118d9 100644 --- a/xbmc/dialogs/GUIDialogProgress.cpp +++ b/xbmc/dialogs/GUIDialogProgress.cpp @@ -106,7 +106,7 @@ void CGUIDialogProgress::ProgressKeys() { if (m_bRunning) { - g_application.FrameMove(); + g_application.FrameMove(true); } } diff --git a/xbmc/guilib/GUIWindow.cpp b/xbmc/guilib/GUIWindow.cpp index 44d5573252..0717eaae8c 100644 --- a/xbmc/guilib/GUIWindow.cpp +++ b/xbmc/guilib/GUIWindow.cpp @@ -448,7 +448,6 @@ void CGUIWindow::OnDeinitWindow(int nextWindowID) // TODO This shouldn't be handled like this // The processing should be done from WindowManager and deinit // should probably be called from there. - g_windowManager.Process(CTimeUtils::GetFrameTime()); g_windowManager.ProcessRenderLoop(true); } } diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp index c25f6600c4..95a2fd0753 100644 --- a/xbmc/guilib/GUIWindowManager.cpp +++ b/xbmc/guilib/GUIWindowManager.cpp @@ -33,7 +33,6 @@ #include "addons/Skin.h" #include "GUITexture.h" #include "windowing/WindowingFactory.h" -#include "utils/TimeUtils.h" using namespace std; @@ -636,12 +635,8 @@ void CGUIWindowManager::ProcessRenderLoop(bool renderOnly /*= false*/) { m_iNested++; if (!renderOnly) - { m_pCallback->Process(); - m_pCallback->FrameMove(); - } else { - Process(CTimeUtils::GetFrameTime()); - } + m_pCallback->FrameMove(!renderOnly); m_pCallback->Render(); m_iNested--; } diff --git a/xbmc/guilib/IWindowManagerCallback.h b/xbmc/guilib/IWindowManagerCallback.h index 25391b228c..ce6ad19362 100644 --- a/xbmc/guilib/IWindowManagerCallback.h +++ b/xbmc/guilib/IWindowManagerCallback.h @@ -36,7 +36,7 @@ public: IWindowManagerCallback(void); virtual ~IWindowManagerCallback(void); - virtual void FrameMove() = 0; + virtual void FrameMove(bool processEvents) = 0; virtual void Render() = 0; virtual void Process() = 0; }; diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile index 8cff8fa304..c714ae9bbc 100644 --- a/xbmc/utils/Makefile +++ b/xbmc/utils/Makefile @@ -47,6 +47,7 @@ SRCS=AlarmClock.cpp \ StreamUtils.cpp \ StringUtils.cpp \ SystemInfo.cpp \ + TimeSmoother.cpp \ TimeUtils.cpp \ TuxBoxUtil.cpp \ URIUtils.cpp \ diff --git a/xbmc/utils/TimeSmoother.cpp b/xbmc/utils/TimeSmoother.cpp new file mode 100644 index 0000000000..8e9ca35224 --- /dev/null +++ b/xbmc/utils/TimeSmoother.cpp @@ -0,0 +1,238 @@ +/* +* Copyright (C) 2011 Team XBMC +* http://www.xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + + +#include "TimeSmoother.h" +#include <math.h> +#include <limits> +#include "utils/MathUtils.h" + +using namespace std; + +CTimeSmoother::CTimeSmoother() +: m_diffs(num_diffs), + m_periods(num_periods), + m_period(0), + m_lastFrameTime(0), + m_prevIn(num_stamps), + m_prevOut(num_stamps) +{ +} + +void CTimeSmoother::AddTimeStamp(unsigned int currentTime) +{ + double diff = m_prevIn.size() ? currentTime - m_prevIn.back() : currentTime; + if (diff) + m_diffs.push_back(diff); + + vector<double> bins; + BinData(m_diffs, bins, 0.15, 2); + + if (bins.size() && m_diffs.size() == num_diffs) + { + // have enough data to update our estimate + vector<unsigned int> binMultipliers; + GetGCDMultipliers(bins, binMultipliers, 2); + assert(binMultipliers.size() == bins.size()); + + vector<unsigned int> intRepresentation; + GetIntRepresentation(m_diffs, intRepresentation, bins, binMultipliers); + assert(intRepresentation.size() == m_diffs.size()); + + double period = EstimatePeriod(m_diffs, intRepresentation); + + // update our mean period + if (fabs(period - m_period) > m_period*0.1) + { // more than 10 % out - kill our previous running average + m_periods.clear(); + m_period = 0; + } + if (m_periods.size() < m_periods.capacity()) + m_period = (m_period * m_periods.size() + period) / (m_periods.size() + 1); + else + m_period += (period - m_periods[0]) / m_periods.size(); + m_periods.push_back(period); + } + double frameTime = EstimateFrameTime(currentTime); + m_prevIn.push_back(currentTime); + m_prevOut.push_back(frameTime); +} + +unsigned int CTimeSmoother::GetNextFrameTime(unsigned int currentTime) +{ + if (m_period) + { + double frameTime = EstimateFrameTime(currentTime); + // ensure we jump at least 1 period ahead of the last time we were called + if (frameTime < m_lastFrameTime + m_period) + frameTime = m_lastFrameTime + m_period; + m_lastFrameTime = frameTime; + return MathUtils::round_int(frameTime); + } + return currentTime; +} + +void CTimeSmoother::BinData(const boost::circular_buffer<double> &data, vector<double> &bins, const double threshold, const unsigned int minbinsize) +{ + if (!data.size()) + return; + + bins.clear(); + vector<unsigned int> counts; + + for (boost::circular_buffer<double>::const_iterator i = data.begin(); i != data.end(); ++i) + { + bool found = false; + for (unsigned int j = 0; j < bins.size(); ++j) + { + if (fabs(*i - bins[j]) < threshold*bins[j]) + { + found = true; + // update our bin mean and count + bins[j] = (bins[j]*counts[j] + *i)/(counts[j]+1); + counts[j]++; + break; + } + } + if (!found) + { + bins.push_back(*i); + counts.push_back(1); + } + } + if (minbinsize) + { + assert(counts.size() == bins.size()); + assert(counts.size()); + // filter out any bins that are not large enough (and any bins that aren't positive) + for (unsigned int j = 0; j < counts.size(); ) + { + if (counts[j] < minbinsize || bins[j] < 0.05) + { + bins.erase(bins.begin() + j); + counts.erase(counts.begin() + j); + } + else + j++; + } + } +} + +void CTimeSmoother::GetConvergent(double value, unsigned int &num, unsigned int &denom, const unsigned int maxnumden) +{ + assert(value > 0); + + unsigned int old_n = 1, old_d = 0; + num = 0; denom = 1; + + // this while loop is guaranteed to terminate as new_n, new_d are increasing non-negative integers + // as long as f >= 0. This in turn is guaranteed as if f == 0, then value < 1 so that 1/(value - f) > 1, + // and hence the next loop f >= 1. + while (true) + { + unsigned int f = (unsigned int)floor(value); + unsigned int new_n = f * num + old_n; + unsigned int new_d = f * denom + old_d; + if (min(new_n, new_d) > maxnumden) + return; + old_n = num; old_d = denom; + num = new_n; denom = new_d; + if ((double)f == value) + return; + value = 1/(value - f); + } +} + +void CTimeSmoother::GetGCDMultipliers(const vector<double> &data, vector<unsigned int> &multipliers, const unsigned int maxminmult) +{ + vector<double>::const_iterator i = std::min_element(data.begin(), data.end()); + + multipliers.clear(); + + vector<unsigned int> num, denom; + for (vector<double>::const_iterator j = data.begin(); j != data.end(); ++j) + { + if (j != i) + { + unsigned int n, d; + GetConvergent(*j / *i, n, d, maxminmult); + num.push_back(n); + denom.push_back(d); + } + else + { + num.push_back(1); + denom.push_back(1); + } + } + vector<unsigned int>::const_iterator k = std::max_element(num.begin(), num.end()); + for (unsigned int i = 0; i < num.size(); ++i) + multipliers.push_back(denom[i] * (*k) / num[i]); +} + +void CTimeSmoother::GetIntRepresentation(const boost::circular_buffer<double> &data, vector<unsigned int> &intData, const vector<double> &bins, const vector<unsigned int> &intBins) +{ + intData.clear(); + for (boost::circular_buffer<double>::const_iterator i = data.begin(); i != data.end(); ++i) + { + double min_r2 = numeric_limits<double>::max(); + unsigned int min_j = 0; + for (unsigned int j = 0; j < bins.size(); ++j) + { + double d = MathUtils::round_int(*i/bins[j]); + double r2 = (*i - bins[j]*d)*(*i - bins[j]*d); + if (r2 < min_r2) + { + min_j = j; + min_r2 = r2; + } + } + intData.push_back(MathUtils::round_int(*i/bins[min_j])*intBins[min_j]); + } +} + +double CTimeSmoother::EstimatePeriod(const boost::circular_buffer<double> &data, const vector<unsigned int> &intData) +{ + double sxy = 0, sxx = 0; + for (unsigned int i = 0; i < data.size(); ++i) + { + sxy += intData[i] * data[i]; + sxx += intData[i] * intData[i]; + } + return sxy/sxx; +} + +double CTimeSmoother::EstimateFrameTime(unsigned int currentTime) +{ + assert(m_prevIn.size() == m_prevOut.size()); + if (m_period) + { + vector<double> outTimes; + for (unsigned int i = 0; i < m_prevIn.size(); ++i) + outTimes.push_back(m_prevOut[i] + m_period * MathUtils::round_int((currentTime - m_prevIn[i]) / m_period)); + sort(outTimes.begin(), outTimes.end()); + double outTime = outTimes[(outTimes.size()-1)/2]; + if (outTime < m_prevOut.back() + m_period) + outTime = m_prevOut.back() + m_period; + return outTime; + } + return currentTime; +} diff --git a/xbmc/utils/TimeSmoother.h b/xbmc/utils/TimeSmoother.h new file mode 100644 index 0000000000..61d2ceb745 --- /dev/null +++ b/xbmc/utils/TimeSmoother.h @@ -0,0 +1,188 @@ +/* +* Copyright (C) 2011 Team XBMC +* http://www.xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#pragma once + +#include <vector> +#include <boost/circular_buffer.hpp> + +/*! \brief Class to smooth timestamps + + This class is designed to smooth reasonably regular timestamps into more regular timestamps. It was designed + to deal with per-frame timestamps, i.e. timestamps immediately following a back/front buffer flip. + + The main difficultes are: + 1. The timestamps are generally noisy. + 2. We do not know the vsync rate. + 3. We do not know whether or not we've missed flips, either intermittently or due to some cadence (eg 2/3/2/3 + frame durations) + + Primarily we're interested in what the vsync rate is, i.e. what is the duration of a single frame. To do this, + we solve the linear regression + + y_i = b_0 + b_1*x_i + e_i + + where y_i are the time stamps, x_i are the integer frame counts that these timestamps correspond to, b_0 is an offset, + b_1 is the frame duration, and e_i is an error term, assumed to be independent, identically distributed as + Normal(0, \sigma^2) for some fixed \sigma^2. + + The main difficulty is we do not know the predictors x_i's - all we know is that they're an increasing sequence of + integers. Thus, to solve this problem we must estimate both the x_i's, b_0 and b_1. We can eliminate b_0 by operating + on the differences t_i = y_i - y_{i-1} and k_i = x_i - x_{i-1} rather than y_i and x_i directly. Further, if we assume + that the error in the differences is additive gaussian white noise, by the maximum likelihood principle we may estimate + the k_i's and b_1 by minimizing the least squares problem: + + min_{b_1 \in R, k \in Z^M}||t - kb_1||_2^2 + + This is linearly separable, and the cost function can be concentrated to k, yielding + + min_{b_1 \in R}||t - b_1 round(t/b_1)||_2^2 ....(1) + + This allows the period b_1 to be estimated without knowledge of the x_i. The main problem with this is we require a + limit on the range for b_1, as clearly b_1 / j for any positive integer j also minimizes (1). This presents a problem + in the case where we have no knowledge what b_1 is. Furthermore, (1) is typically not smooth, with the concave portion + which contains it's minimum often being very small. Thus, in order to minimize (1) we need to evaluate it over a fairly + fine grid, between pre-defined limits. This is infeasible. + + Instead, we attack the problem by first attempting to estimate the k_i's. We do this by binning the differences t_i + into common categories and then computing the greatest common divisor of those bins. Given the data is noisy, the binning + procedure (BinData) has a tolerance for the bin size. To minimize the influence of timestamps that are out of the ordinary + we then discard any bins with only one value in them, thus leaving only binned values that are common. We then compute + an approximate greatest common divisor by computing the continued fraction expansion of the ratio of pairs of bins, to + produce integer divisors that produce a common divisor. We restrict the size of these integer divisors so that our divisor + is not too small - typically we're wanting to pick up simple cadences such as 1,2,1,2 and 2,3,2,3 but are unlikely to need + to detect 5,4,5,4 etc - at that point the CPU/GPU is limiting the frame rate so much that timing issues aren't likely to be + the largest issue. + + Once we have the GCD we can then compute the integers k_i. Given k_i we can then estimate b_1 using a standard simple + linear regression without intercept: + + t_i = b_1*x_i + e_i + + which can be solved in the standard manner. + + Our procedure is thus as follows: + 1. We collect K differences, where K is enough to ensure we get an estimate of the period, but not too large so that the + computation is unnecessarily long. + 2. We then keep a running average of our period that is fairly long in order to stabilise the period over time. + 3. To allow the running average to adapt quickly to framerate changes, if the newly computed period is much different + from our running average, we start a new running average. + 4. Given our period, we then estimate our noise-free timestamps by rounding to the nearest multiple of the period + from past time points. We use several time points and choose the median such point as our final point. This allows + for deciding whether to round up or round down in cases where it may not be particularly obvious. + 5. Finally, we ensure the timestamps always move forward by the period. + + This works well for the most part. Note that we're estimating a noise-free version of the last timestamp passed in, + we do not claim to be estimating the timestamp of the next frame time. Such an estimate is essentially indeterminant, as + the time to process the frame is non-constant. + */ + +class CTimeSmoother +{ +public: + CTimeSmoother(); + + /*! \brief Add a valid time stamp to the time smoother + This function will add a time stamp to the smoother and use it to update the current average frame rate + and estimate the current cadence. The next frame time can be retrieved using GetNextFrameTime. + \param currentTime the current time stamp to add to the smoother + \sa GetNextFrameTime + */ + void AddTimeStamp(unsigned int currentTime); + + /*! \brief Retreive an estimate of the next frame time, based on the current time + This function uses previously estimated average frame rates and the current cadence to estimate the next + frame time. + \param currentTime the current time stamp to use to estimate the next frame time + \return the estimated time the next frame will be displayed + \sa AddTimeStamp + */ + unsigned int GetNextFrameTime(unsigned int currentTime); + +protected: + /*! \brief Bin data into separate clusters, determined by a given threshold and minimum bin size. + \param data a circular buffer of data points + \param bins the bins to return + \param threshold the threshold to determine whether a data point is close to a given bin as a proportion of bin mean + \param minbinsize the minimum bin size of each bin + */ + void BinData(const boost::circular_buffer<double> &data, std::vector<double> &bins, const double threshold, const unsigned int minbinsize); + + /*! \brief Given a real value, find a rational convergent + Uses a continued fraction expansion of value to determine the numerator and denominator of a rational convergent + where min(num, denom) does not exceed maxnumdem + \param value real data vlaue + \param num [out] the numerator + \param denom [out] the denominator + \param maxnumden the maximal value of min(num, denom) + */ + void GetConvergent(double value, unsigned int &num, unsigned int &denom, const unsigned int maxnumden); + + /*! \brief Given a set of data, find integer multipliers such that data[i] \sim quotient[i] * gcd(data) + Uses rational convergents to data[i]/min(data) to find integer multipliers to the (approximate) greatest common divisor + of the data. + \param data a vector of data + \param multipliers the output multipliers + \param maxminmult the maximal value of multiplier[min(data)] + \sa GetConvergent + */ + void GetGCDMultipliers(const std::vector<double> &data, std::vector<unsigned int> &multipliers, const unsigned int maxminmult); + + /*! \brief Given a set of bins and integer values associated with each bin, find the integer representation of some data + This allows noisy data to be approximated by a set of clean data, and to compute the integer representation of that data. + \param data the data on which to compute the integer representation + \param intData [out] the integer representation of the data + \param bins the bins to use for approximating the data + \param intBins the integer representation of the bins + */ + void GetIntRepresentation(const boost::circular_buffer<double> &data, std::vector<unsigned int> &intData, const std::vector<double> &bins, const std::vector<unsigned int> &intBins); + + /*! \brief Given a set of data, and an integer representation of that data, estimate the period of the data + Essentially we solve a linear regression d_i = \theta*z_i, where d_i is the original data, and z_i is the integer + representation of that data. Note that no intercept is included, so this is appropriate for operating on difference data + (such as the difference between timestamps following flipping of buffers during rendering - the integers represent the vsync + sequence). + \param data noisy data to estimate the period of + \param intData an integral representation of the data + \return the period of the data + */ + double EstimatePeriod(const boost::circular_buffer<double> &data, const std::vector<unsigned int> &intData); + + /*! \brief Compute the next frame time + \param currentTime the current time + \return the next frame time + */ + double EstimateFrameTime(unsigned int currentTime); + +private: + static const unsigned int num_diffs = 10; ///< \brief number of differences to keep for evaluating period + static const unsigned int num_periods = 100; ///< \brief number of previous period estimates to use for the average period + static const unsigned int num_stamps = 3; ///< \brief number of previous time stamps to keep to optimize next time point + + boost::circular_buffer<double> m_diffs; ///< \brief the recently received differences + boost::circular_buffer<double> m_periods; ///< \brief the recently evaluated periods + double m_period; ///< \brief the running average of m_periods + + double m_lastFrameTime; ///< \brief the last frame time + + boost::circular_buffer<double> m_prevIn; ///< \brief the previous timestamps coming in + boost::circular_buffer<double> m_prevOut; ///< \brief the previous timestamps going out +}; diff --git a/xbmc/utils/TimeUtils.cpp b/xbmc/utils/TimeUtils.cpp index 69db439af1..1a9f3f1f96 100644 --- a/xbmc/utils/TimeUtils.cpp +++ b/xbmc/utils/TimeUtils.cpp @@ -32,6 +32,8 @@ #include <time.h> #endif +#include "TimeSmoother.h" + int64_t CurrentHostCounter(void) { #if defined(TARGET_DARWIN) @@ -60,11 +62,17 @@ int64_t CurrentHostFrequency(void) #endif } +CTimeSmoother *CTimeUtils::frameTimer = NULL; unsigned int CTimeUtils::frameTime = 0; -void CTimeUtils::UpdateFrameTime() +void CTimeUtils::UpdateFrameTime(bool flip) { - frameTime = XbmcThreads::SystemClockMillis(); + if (!frameTimer) + frameTimer = new CTimeSmoother(); + unsigned int currentTime = XbmcThreads::SystemClockMillis(); + if (flip) + frameTimer->AddTimeStamp(currentTime); + frameTime = frameTimer->GetNextFrameTime(currentTime); } unsigned int CTimeUtils::GetFrameTime() diff --git a/xbmc/utils/TimeUtils.h b/xbmc/utils/TimeUtils.h index 67abb94911..7b869fc8e8 100644 --- a/xbmc/utils/TimeUtils.h +++ b/xbmc/utils/TimeUtils.h @@ -25,6 +25,7 @@ #include <time.h> class CDateTime; +class CTimeSmoother; int64_t CurrentHostCounter(void); int64_t CurrentHostFrequency(void); @@ -32,11 +33,12 @@ int64_t CurrentHostFrequency(void); class CTimeUtils { public: - static void UpdateFrameTime(); ///< update the frame time. Not threadsafe + static void UpdateFrameTime(bool flip); ///< update the frame time. Not threadsafe static unsigned int GetFrameTime(); ///< returns the frame time in MS. Not threadsafe static CDateTime GetLocalTime(time_t time); private: static unsigned int frameTime; + static CTimeSmoother *frameTimer; }; |