Skip to content

Commit

Permalink
clapper-app: Support Windows high resolution clock
Browse files Browse the repository at this point in the history
Windows high resolution clock improves accuracy of various Windows
timer APIs and precision of GstSystemClock during playback
  • Loading branch information
Rafostar committed Jan 12, 2025
1 parent 60e7d56 commit d9b20dc
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 3 deletions.
120 changes: 118 additions & 2 deletions src/bin/clapper-app/clapper-app-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <windows.h>
#ifdef HAVE_WIN_PROCESS_THREADS_API
#include <processthreadsapi.h>
#endif
#ifdef HAVE_WIN_TIME_API
#include <timeapi.h>
#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)
{
Expand All @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions src/bin/clapper-app/clapper-app-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
16 changes: 16 additions & 0 deletions src/bin/clapper-app/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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, "");
Expand 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;
}
18 changes: 17 additions & 1 deletion src/bin/clapper-app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down

0 comments on commit d9b20dc

Please sign in to comment.