gstaudiosrc/sink: Set audio ringbuffer thread priority

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.
diff --git a/gst-libs/gst/audio/gstaudiosink.c b/gst-libs/gst/audio/gstaudiosink.c
index cc48f7d..cc7b791 100644
--- a/gst-libs/gst/audio/gstaudiosink.c
+++ b/gst-libs/gst/audio/gstaudiosink.c
@@ -53,6 +53,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
@@ -213,6 +214,9 @@
   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);
diff --git a/gst-libs/gst/audio/gstaudiosrc.c b/gst-libs/gst/audio/gstaudiosrc.c
index c3faf8f..008921f 100644
--- a/gst-libs/gst/audio/gstaudiosrc.c
+++ b/gst-libs/gst/audio/gstaudiosrc.c
@@ -46,6 +46,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
@@ -192,6 +193,9 @@
   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);
diff --git a/gst-libs/gst/audio/gstaudioutilsprivate.c b/gst-libs/gst/audio/gstaudioutilsprivate.c
index 27b056b..cb3935c 100644
--- a/gst-libs/gst/audio/gstaudioutilsprivate.c
+++ b/gst-libs/gst/audio/gstaudioutilsprivate.c
@@ -23,6 +23,10 @@
 #include "config.h"
 #endif
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 #include <gst/audio/audio.h>
 #include "gstaudioutilsprivate.h"
 
@@ -212,3 +216,64 @@
 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
+}
diff --git a/gst-libs/gst/audio/gstaudioutilsprivate.h b/gst-libs/gst/audio/gstaudioutilsprivate.h
index 976765f..b9db8d8 100644
--- a/gst-libs/gst/audio/gstaudioutilsprivate.h
+++ b/gst-libs/gst/audio/gstaudioutilsprivate.h
@@ -42,6 +42,9 @@
                                             gint64 src_value, GstFormat * dest_format,
                                             gint64 * dest_value);
 
+G_GNUC_INTERNAL
+gpointer __gst_audio_set_thread_priority   (void);
+
 G_END_DECLS
 
 #endif