aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--XBMC-ATV2.xcodeproj/project.pbxproj6
-rw-r--r--XBMC-IOS.xcodeproj/project.pbxproj6
-rw-r--r--XBMC.xcodeproj/project.pbxproj12
-rw-r--r--project/VS2010Express/XBMC.vcxproj4
-rw-r--r--project/VS2010Express/XBMC.vcxproj.filters8
-rw-r--r--xbmc/Application.cpp57
-rw-r--r--xbmc/Application.h2
-rw-r--r--xbmc/XBApplicationEx.cpp2
-rw-r--r--xbmc/dialogs/GUIDialogProgress.cpp2
-rw-r--r--xbmc/guilib/GUIWindow.cpp1
-rw-r--r--xbmc/guilib/GUIWindowManager.cpp7
-rw-r--r--xbmc/guilib/IWindowManagerCallback.h2
-rw-r--r--xbmc/utils/Makefile1
-rw-r--r--xbmc/utils/TimeSmoother.cpp238
-rw-r--r--xbmc/utils/TimeSmoother.h188
-rw-r--r--xbmc/utils/TimeUtils.cpp12
-rw-r--r--xbmc/utils/TimeUtils.h4
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;
};