gstaudiosrc/sink: Set audio ringbuffer thread priority
authorNirbheek Chauhan <nirbheek@centricular.com>
Mon, 10 Sep 2018 19:11:59 +0000 (00:41 +0530)
committerNirbheek Chauhan <nirbheek@centricular.com>
Mon, 10 Sep 2018 19:11:59 +0000 (00:41 +0530)
On Windows, the ringbuffer thread function must have the "Pro Audio"
priority set, otherwise it sometimes doesn't get scheduled for
200-300ms, which will immediately cause an underrun unless you set
a very high latency-time and buffer-time.

This has no compile-time deps since it tries to load avrt.dll at
runtime to set the thread priority.

gst-libs/gst/audio/gstaudiosink.c
gst-libs/gst/audio/gstaudiosrc.c
gst-libs/gst/audio/gstaudioutilsprivate.c
gst-libs/gst/audio/gstaudioutilsprivate.h

index dfbdf94..4325f71 100644 (file)
@@ -56,6 +56,7 @@
 
 #include <gst/audio/audio.h>
 #include "gstaudiosink.h"
+#include "gstaudioutilsprivate.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_audio_sink_debug);
 #define GST_CAT_DEFAULT gst_audio_sink_debug
@@ -216,6 +217,9 @@ audioringbuffer_thread_func (GstAudioRingBuffer * buf)
   if (writefunc == NULL)
     goto no_function;
 
+  if (G_UNLIKELY (!__gst_audio_set_thread_priority ()))
+    GST_WARNING_OBJECT (sink, "failed to set thread priority");
+
   message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
       GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (sink));
   g_value_init (&val, GST_TYPE_G_THREAD);
index 64fcb9d..16b090c 100644 (file)
@@ -49,6 +49,7 @@
 
 #include <gst/audio/audio.h>
 #include "gstaudiosrc.h"
+#include "gstaudioutilsprivate.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_audio_src_debug);
 #define GST_CAT_DEFAULT gst_audio_src_debug
@@ -195,6 +196,9 @@ audioringbuffer_thread_func (GstAudioRingBuffer * buf)
   if ((readfunc = csrc->read) == NULL)
     goto no_function;
 
+  if (G_UNLIKELY (!__gst_audio_set_thread_priority ()))
+    GST_WARNING_OBJECT (src, "failed to set thread priority");
+
   message = gst_message_new_stream_status (GST_OBJECT_CAST (buf),
       GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (src));
   g_value_init (&val, GST_TYPE_G_THREAD);
index 27b056b..cb3935c 100644 (file)
 #include "config.h"
 #endif
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 #include <gst/audio/audio.h>
 #include "gstaudioutilsprivate.h"
 
@@ -212,3 +216,64 @@ __gst_audio_encoded_audio_convert (GstAudioInfo * fmt,
 exit:
   return res;
 }
+
+#ifdef _WIN32
+/* *INDENT-OFF* */
+static struct
+{
+  HMODULE dll;
+  gboolean tried_loading;
+
+    HANDLE (WINAPI * AvSetMmThreadCharacteristics) (LPCSTR, LPDWORD);
+    BOOL (WINAPI * AvRevertMmThreadCharacteristics) (HANDLE);
+} _gst_audio_avrt_tbl = { 0 };
+/* *INDENT-ON* */
+#endif
+
+static gboolean
+__gst_audio_init_thread_priority (void)
+{
+#ifdef _WIN32
+  if (_gst_audio_avrt_tbl.tried_loading)
+    return _gst_audio_avrt_tbl.dll != NULL;
+
+  if (!_gst_audio_avrt_tbl.dll)
+    _gst_audio_avrt_tbl.dll = LoadLibrary (TEXT ("avrt.dll"));
+
+  if (!_gst_audio_avrt_tbl.dll) {
+    GST_WARNING ("Failed to set thread priority, can't find avrt.dll");
+    _gst_audio_avrt_tbl.tried_loading = TRUE;
+    return FALSE;
+  }
+
+  _gst_audio_avrt_tbl.AvSetMmThreadCharacteristics =
+      GetProcAddress (_gst_audio_avrt_tbl.dll, "AvSetMmThreadCharacteristicsA");
+  _gst_audio_avrt_tbl.AvRevertMmThreadCharacteristics =
+      GetProcAddress (_gst_audio_avrt_tbl.dll,
+      "AvRevertMmThreadCharacteristics");
+
+  _gst_audio_avrt_tbl.tried_loading = TRUE;
+#endif
+
+  return TRUE;
+}
+
+/*
+ * Increases the priority of the thread it's called from
+ */
+gpointer
+__gst_audio_set_thread_priority (void)
+{
+  if (!__gst_audio_init_thread_priority ())
+    return NULL;
+
+#ifdef _WIN32
+  DWORD taskIndex = 0;
+  /* This is only used from ringbuffer thread functions, so we don't need to
+   * ever need to revert the thread priorities. */
+  return _gst_audio_avrt_tbl.AvSetMmThreadCharacteristics (TEXT ("Pro Audio"),
+      &taskIndex);
+#else
+  return NULL;
+#endif
+}
index 976765f..b9db8d8 100644 (file)
@@ -42,6 +42,9 @@ gboolean __gst_audio_encoded_audio_convert (GstAudioInfo * fmt, gint64 bytes,
                                             gint64 src_value, GstFormat * dest_format,
                                             gint64 * dest_value);
 
+G_GNUC_INTERNAL
+gpointer __gst_audio_set_thread_priority   (void);
+
 G_END_DECLS
 
 #endif