tools: gst-play: Enable Windows high-resolution clock
authorSeungha Yang <seungha@centricular.com>
Wed, 19 Jan 2022 18:17:58 +0000 (03:17 +0900)
committerSeungha Yang <seungha@centricular.com>
Wed, 19 Jan 2022 19:25:03 +0000 (04:25 +0900)
Apply https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/817
to gst-play as well, especially for better high-framerate
(60fps or higher) video support, because
15ms default clock precision (actual value is system dependent)
is not sufficient for such scenario.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1541>

subprojects/gst-plugins-base/tools/gst-play.c
subprojects/gst-plugins-base/tools/meson.build

index d02b421..508800a 100644 (file)
 
 #include <glib/gprintf.h>
 
+#ifdef HAVE_WINMM
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <timeapi.h>
+#endif
+
 #include "gst-play-kb.h"
 
 #define VOLUME_STEPS 20
@@ -1491,6 +1497,43 @@ keyboard_cb (const gchar * key_input, gpointer user_data)
   }
 }
 
+#ifdef HAVE_WINMM
+static guint
+enable_winmm_timer_resolution (void)
+{
+  TIMECAPS time_caps;
+  guint resolution = 0;
+  MMRESULT res;
+
+  res = timeGetDevCaps (&time_caps, sizeof (TIMECAPS));
+  if (res != TIMERR_NOERROR) {
+    g_warning ("timeGetDevCaps() returned non-zero code %d", res);
+    return 0;
+  }
+
+  resolution = MIN (MAX (time_caps.wPeriodMin, 1), time_caps.wPeriodMax);
+  res = timeBeginPeriod (resolution);
+  if (res != TIMERR_NOERROR) {
+    g_warning ("timeBeginPeriod() returned non-zero code %d", res);
+    return 0;
+  }
+
+  gst_println (_("Use Windows high-resolution clock, precision: %u ms\n"),
+      resolution);
+
+  return resolution;
+}
+
+static void
+clear_winmm_timer_resolution (guint resolution)
+{
+  if (resolution == 0)
+    return;
+
+  timeEndPeriod (resolution);
+}
+#endif
+
 int
 main (int argc, char **argv)
 {
@@ -1513,6 +1556,9 @@ main (int argc, char **argv)
   GOptionContext *ctx;
   gchar *playlist_file = NULL;
   gboolean use_playbin3 = FALSE;
+#ifdef HAVE_WINMM
+  guint winmm_timer_resolution = 0;
+#endif
   GOptionEntry options[] = {
     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         N_("Output status information and property notifications"), NULL},
@@ -1669,6 +1715,22 @@ main (int argc, char **argv)
         ("Failed to create 'playbin' element. Check your GStreamer installation.\n");
     return EXIT_FAILURE;
   }
+#ifdef HAVE_WINMM
+  /* Enable high-precision clock which will improve accuracy of various
+   * Windows timer APIs (e.g., Sleep()), and it will increase the precision
+   * of GstSystemClock as well
+   */
+
+  /* NOTE: Once timer resolution is updated via timeBeginPeriod(),
+   * application should undo it by calling timeEndPeriod()
+   *
+   * Prior to Windows 10, version 2004, timeBeginPeriod() affects global
+   * Windows setting (meaning that it will affect other processes),
+   * but starting with Windows 10, version 2004, this function no longer
+   * affects global timer resolution
+   */
+  winmm_timer_resolution = enable_winmm_timer_resolution ();
+#endif
 
   if (interactive) {
     if (gst_play_kb_set_key_handler (keyboard_cb, play)) {
@@ -1682,6 +1744,11 @@ main (int argc, char **argv)
   /* play */
   do_play (play);
 
+#ifdef HAVE_WINMM
+  /* Undo timeBeginPeriod() if required */
+  clear_winmm_timer_resolution (winmm_timer_resolution);
+#endif
+
   /* clean up */
   play_free (play);
 
index 612d6b0..6f96b23 100644 (file)
@@ -1,5 +1,30 @@
 tool_deps = glib_deps + [pbutils_dep, audio_dep, video_dep, tag_dep, gst_dep, gst_base_dep, gmodule_dep]
 
+extra_args = []
+extra_deps = []
+
+if host_system == 'windows'
+  # Check whether we're building for UWP apps, and if so, will not link winmm
+  # of which APIs are for WIN32 desktop
+  building_for_uwp = false
+  code = '''
+  #include <windows.h>
+  #if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
+  #error "Not building for UWP"
+  #endif'''
+  if cc.compiles(code, name : 'building for UWP')
+    building_for_uwp = true
+  endif
+
+  if not building_for_uwp
+    winmm_lib = cc.find_library('winmm', required: false)
+    if winmm_lib.found() and cc.has_header('timeapi.h')
+      extra_args += ['-DHAVE_WINMM']
+      extra_deps += [winmm_lib]
+    endif
+  endif
+endif
+
 executable('gst-device-monitor-@0@'.format(api_version),
   'gst-device-monitor.c',
   install: true,
@@ -21,8 +46,8 @@ install_man('gst-discoverer-@0@.1'.format(api_version))
 executable('gst-play-@0@'.format(api_version),
   'gst-play.c', 'gst-play-kb.c',
   install: true,
-  c_args : gst_plugins_base_args + ['-DG_LOG_DOMAIN="gst-play-@0@"'.format(api_version)],
+  c_args : gst_plugins_base_args + ['-DG_LOG_DOMAIN="gst-play-@0@"'.format(api_version)] + extra_args,
   include_directories: [configinc],
-  dependencies : tool_deps + [libm],
+  dependencies : tool_deps + [libm] + extra_deps,
 )
 install_man('gst-play-@0@.1'.format(api_version))