From d9b20dcc18a2fb14b5fa88be019e8e386b94a382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= Date: Sun, 12 Jan 2025 12:52:44 +0100 Subject: [PATCH] clapper-app: Support Windows high resolution clock Windows high resolution clock improves accuracy of various Windows timer APIs and precision of GstSystemClock during playback --- src/bin/clapper-app/clapper-app-utils.c | 120 +++++++++++++++++++++++- src/bin/clapper-app/clapper-app-utils.h | 11 +++ src/bin/clapper-app/main.c | 16 ++++ src/bin/clapper-app/meson.build | 18 +++- 4 files changed, 162 insertions(+), 3 deletions(-) diff --git a/src/bin/clapper-app/clapper-app-utils.c b/src/bin/clapper-app/clapper-app-utils.c index 9b85900f..202e4255 100644 --- a/src/bin/clapper-app/clapper-app-utils.c +++ b/src/bin/clapper-app/clapper-app-utils.c @@ -23,8 +23,124 @@ #include "clapper-app-utils.h" #include "clapper-app-media-item-box.h" -/* Useful only on Windows */ #ifdef G_OS_WIN32 +#include +#ifdef HAVE_WIN_PROCESS_THREADS_API +#include +#endif +#ifdef HAVE_WIN_TIME_API +#include +#endif +#endif + +#define GST_CAT_DEFAULT clapper_app_utils_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +void +clapper_app_utils_debug_init (void) +{ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperapputils", 0, + "Clapper App Utils"); +} + +/* Windows specific functions */ +#ifdef G_OS_WIN32 + +/* + * clapper_app_utils_win_enforce_hi_res_clock: + * + * Enforce high resolution clock by explicitly disabling Windows + * timer resolution power throttling. When disabled, system remembers + * and honors any previous timer resolution request by the process. + * + * By default, Windows 11 may automatically ignore the timer + * resolution requests in certain scenarios. + */ +void +clapper_app_utils_win_enforce_hi_res_clock (void) +{ +#ifdef HAVE_WIN_PROCESS_THREADS_API + PROCESS_POWER_THROTTLING_STATE PowerThrottling; + RtlZeroMemory (&PowerThrottling, sizeof (PowerThrottling)); + gboolean success; + + PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION; + PowerThrottling.ControlMask = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION; + PowerThrottling.StateMask = 0; // Always honor timer resolution requests + + success = (gboolean) SetProcessInformation( + GetCurrentProcess (), + ProcessPowerThrottling, + &PowerThrottling, + sizeof (PowerThrottling)); + + /* Not an error. Older Windows does not have this functionality, but + * also honor hi-res clock by default anyway, so do not print then. */ + GST_INFO ("Windows hi-res clock support is %senforced", + (success) ? "" : "NOT "); +#endif +} + +/* + * clapper_app_utils_win_hi_res_clock_start: + * + * Start Windows high resolution clock which will improve + * accuracy of various Windows timer APIs and precision + * of #GstSystemClock during playback. + * + * On Windows 10 version 2004 (and older), this function affects + * a global Windows setting. On any other (newer) version this + * will only affect a single process. + * + * Returns: Timer resolution period value. + */ +guint +clapper_app_utils_win_hi_res_clock_start (void) +{ + guint resolution = 0; + +#ifdef HAVE_WIN_TIME_API + TIMECAPS time_caps; + MMRESULT res; + + if ((res = timeGetDevCaps (&time_caps, sizeof (TIMECAPS))) != TIMERR_NOERROR) { + GST_WARNING ("Could not query timer resolution, code: %u", res); + return 0; + } + + resolution = MIN (MAX (time_caps.wPeriodMin, 1), time_caps.wPeriodMax); + + if ((res = timeBeginPeriod (resolution)) != TIMERR_NOERROR) { + GST_WARNING ("Could not request timer resolution, code: %u", res); + return 0; + } + + GST_INFO ("Started Windows hi-res clock, precision: %ums", resolution); +#endif + + return resolution; +} + +/* + * clapper_app_utils_win_hi_res_clock_stop: + * @resolution: started resolution value (non-zero) + * + * Stop previously started Microsoft Windows high resolution clock. + */ +void +clapper_app_utils_win_hi_res_clock_stop (guint resolution) +{ +#ifdef HAVE_WIN_TIME_API + MMRESULT res; + + if ((res = timeEndPeriod (resolution)) == TIMERR_NOERROR) + GST_INFO ("Stopped Windows hi-res clock"); + else + GST_ERROR ("Could not stop hi-res clock, code: %u", res); +#endif +} + +/* Extensions are used only on Windows */ const gchar *const * clapper_app_utils_get_extensions (void) { @@ -45,7 +161,7 @@ clapper_app_utils_get_subtitles_extensions (void) return subs_extensions; } -#endif +#endif // G_OS_WIN32 const gchar *const * clapper_app_utils_get_mime_types (void) diff --git a/src/bin/clapper-app/clapper-app-utils.h b/src/bin/clapper-app/clapper-app-utils.h index 86d57356..f0332d35 100644 --- a/src/bin/clapper-app/clapper-app-utils.h +++ b/src/bin/clapper-app/clapper-app-utils.h @@ -26,7 +26,18 @@ G_BEGIN_DECLS typedef void (* ClapperAppUtilsIterRanks) (const gchar *feature_name, GstRank rank, gboolean from_env, gpointer user_data); +void clapper_app_utils_debug_init (void); + #ifdef G_OS_WIN32 +G_GNUC_INTERNAL +void clapper_app_utils_win_enforce_hi_res_clock (void); + +G_GNUC_INTERNAL +guint clapper_app_utils_win_hi_res_clock_start (void); + +G_GNUC_INTERNAL +void clapper_app_utils_win_hi_res_clock_stop (guint resolution); + G_GNUC_INTERNAL const gchar *const * clapper_app_utils_get_extensions (void); diff --git a/src/bin/clapper-app/main.c b/src/bin/clapper-app/main.c index da3d87fa..7acf4e10 100644 --- a/src/bin/clapper-app/main.c +++ b/src/bin/clapper-app/main.c @@ -26,6 +26,7 @@ #include "clapper-app-application.h" #include "clapper-app-types.h" +#include "clapper-app-utils.h" gint main (gint argc, gchar **argv) @@ -34,6 +35,10 @@ main (gint argc, gchar **argv) GApplication *application; gint status; +#ifdef G_OS_WIN32 + guint resolution = 0; +#endif + g_setenv ("GSK_RENDERER", "gl", FALSE); setlocale (LC_ALL, ""); @@ -48,13 +53,24 @@ main (gint argc, gchar **argv) adw_init (); clapper_app_types_init (); + clapper_app_utils_debug_init (); g_set_application_name ("Clapper"); +#ifdef G_OS_WIN32 + clapper_app_utils_win_enforce_hi_res_clock (); + resolution = clapper_app_utils_win_hi_res_clock_start (); +#endif + application = clapper_app_application_new (); status = g_application_run (application, argc, argv); g_object_unref (application); +#ifdef G_OS_WIN32 + if (resolution > 0) + clapper_app_utils_win_hi_res_clock_stop (resolution); +#endif + return status; } diff --git a/src/bin/clapper-app/meson.build b/src/bin/clapper-app/meson.build index 515e83cc..c63b5bb2 100644 --- a/src/bin/clapper-app/meson.build +++ b/src/bin/clapper-app/meson.build @@ -80,6 +80,22 @@ clapperapp_c_args = [ '-DGST_USE_UNSTABLE_API', ] +is_windows = ['windows'].contains(host_machine.system()) + +if is_windows + clapperapp_c_args += ['-D_WIN32_WINNT=_WIN32_WINNT_WIN8'] + kernel32_dep = cc.find_library('kernel32', required: false) + if kernel32_dep.found() and cc.has_header('processthreadsapi.h') + clapperapp_deps += kernel32_dep + clapperapp_c_args += ['-DHAVE_WIN_PROCESS_THREADS_API'] + endif + winmm_dep = cc.find_library('winmm', required: false) + if winmm_dep.found() and cc.has_header('timeapi.h') + clapperapp_deps += winmm_dep + clapperapp_c_args += ['-DHAVE_WIN_TIME_API'] + endif +endif + executable( meson.project_name(), clapperapp_sources, @@ -90,7 +106,7 @@ executable( install_dir: bindir, win_subsystem: 'windows', ) -if ['windows'].contains(host_machine.system()) +if is_windows executable( meson.project_name() + '-console', clapperapp_sources,