wasapi2: Add a new property for ICoreDispatcher setting
authorSeungha Yang <seungha@centricular.com>
Fri, 24 Jul 2020 12:53:37 +0000 (21:53 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 26 Jul 2020 12:52:15 +0000 (12:52 +0000)
... so that ensure device activation on UI thread.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1466>

sys/wasapi2/gstwasapi2client.cpp
sys/wasapi2/gstwasapi2client.h
sys/wasapi2/gstwasapi2device.c
sys/wasapi2/gstwasapi2sink.c
sys/wasapi2/gstwasapi2src.c

index 07d6343..e2f1627 100644 (file)
@@ -77,14 +77,22 @@ public:
   }
 
   HRESULT
-  RuntimeClassInitialize (GstWasapi2Client * listener)
+  RuntimeClassInitialize (GstWasapi2Client * listener, gpointer dispatcher)
   {
     if (!listener)
       return E_INVALIDARG;
 
     listener_ = listener;
 
-    findCoreDispatcher ();
+    if (dispatcher) {
+      ComPtr<IInspectable> inspectable =
+        reinterpret_cast<IInspectable*> (dispatcher);
+      HRESULT hr;
+
+      hr = inspectable.As (&dispatcher_);
+      if (gst_wasapi2_result (hr))
+        GST_INFO("Main UI dispatcher is available");
+    }
 
     return S_OK;
   }
@@ -150,36 +158,6 @@ public:
       });
   }
 
-  /* Try to find ICoreDispatcher of main UI so that active audio
-   * interface on main UI thread */
-  void findCoreDispatcher(void)
-  {
-    HRESULT hr;
-    HStringReference hstr_core_app =
-        HStringReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication);
-    ComPtr<ICoreApplication> core_app;
-    ComPtr<ICoreApplicationView> core_app_view;
-    ComPtr<ICoreWindow> core_window;
-
-    hr = GetActivationFactory (hstr_core_app.Get(), &core_app);
-    if (!gst_wasapi2_result (hr))
-      return;
-
-    hr = core_app->GetCurrentView (&core_app_view);
-    if (!gst_wasapi2_result (hr))
-      return;
-
-    hr = core_app_view->get_CoreWindow (&core_window);
-    if (!gst_wasapi2_result (hr))
-      return;
-
-    hr = core_window->get_Dispatcher (&dispatcher_);
-    if (!gst_wasapi2_result (hr))
-      return;
-
-    GST_DEBUG ("Main UI dispatcher is available");
-  }
-
   template <typename CB>
   HRESULT
   runOnUIThread (DWORD timeout, CB && cb)
@@ -242,6 +220,7 @@ struct _GstWasapi2Client
   gchar *device_id;
   gchar *device_name;
   gint device_index;
+  gpointer dispatcher;
 
   IAudioClient3 *audio_client;
   IAudioCaptureClient *audio_capture_client;
@@ -284,6 +263,7 @@ enum
   PROP_DEVICE_INDEX,
   PROP_DEVICE_CLASS,
   PROP_LOW_LATENCY,
+  PROP_DISPATCHER,
 };
 
 #define DEFAULT_DEVICE_INDEX  -1
@@ -356,6 +336,9 @@ gst_wasapi2_client_class_init (GstWasapi2ClientClass * klass)
       g_param_spec_boolean ("low-latency", "Low latency",
           "Optimize all settings for lowest latency. Always safe to enable.",
           DEFAULT_LOW_LATENCY, param_flags));
+  g_object_class_install_property (gobject_class, PROP_DISPATCHER,
+      g_param_spec_pointer ("dispatcher", "Dispatcher",
+          "ICoreDispatcher COM object to use", param_flags));
 }
 
 static void
@@ -468,6 +451,9 @@ gst_wasapi2_client_get_property (GObject * object, guint prop_id,
     case PROP_LOW_LATENCY:
       g_value_set_boolean (value, self->low_latency);
       break;
+    case PROP_DISPATCHER:
+      g_value_set_pointer (value, self->dispatcher);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -499,6 +485,9 @@ gst_wasapi2_client_set_property (GObject * object, guint prop_id,
     case PROP_LOW_LATENCY:
       self->low_latency = g_value_get_boolean (value);
       break;
+    case PROP_DISPATCHER:
+      self->dispatcher = g_value_get_pointer (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -604,7 +593,8 @@ gst_wasapi2_client_thread_func_internal (GstWasapi2Client * self)
        self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE ? "capture" :
        "render", GST_STR_NULL (self->device_id), self->device_index);
 
-  hr = MakeAndInitialize<GstWasapiDeviceActivator> (&activator, self);
+  hr = MakeAndInitialize<GstWasapiDeviceActivator> (&activator,
+      self, self->dispatcher);
   if (!gst_wasapi2_result (hr))
     goto run_loop;
 
@@ -1776,15 +1766,65 @@ gst_wasapi2_client_get_volume (GstWasapi2Client * client, gfloat * volume)
   return TRUE;
 }
 
+static HRESULT
+find_dispatcher (ICoreDispatcher ** dispatcher)
+{
+  HStringReference hstr_core_app =
+      HStringReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication);
+  HRESULT hr;
+
+  ComPtr<ICoreApplication> core_app;
+  hr = GetActivationFactory (hstr_core_app.Get(), &core_app);
+  if (!gst_wasapi2_result (hr))
+    return hr;
+
+  ComPtr<ICoreApplicationView> core_app_view;
+  hr = core_app->GetCurrentView (&core_app_view);
+  if (!gst_wasapi2_result (hr))
+    return hr;
+
+  ComPtr<ICoreWindow> core_window;
+  hr = core_app_view->get_CoreWindow (&core_window);
+  if (!gst_wasapi2_result (hr))
+    return hr;
+
+  return core_window->get_Dispatcher (dispatcher);
+}
+
 GstWasapi2Client *
 gst_wasapi2_client_new (GstWasapi2ClientDeviceClass device_class,
-    gboolean low_latency, gint device_index, const gchar * device_id)
+    gboolean low_latency, gint device_index, const gchar * device_id,
+    gpointer dispatcher)
 {
   GstWasapi2Client *self;
+  ComPtr<ICoreDispatcher> core_dispatcher;
+  /* Multiple COM init is allowed */
+  RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED);
+
+  /* If application didn't pass ICoreDispatcher object,
+   * try to get dispatcher object for the current thread */
+  if (!dispatcher) {
+    HRESULT hr;
+
+    hr = find_dispatcher (&core_dispatcher);
+    if (gst_wasapi2_result (hr)) {
+      GST_DEBUG ("UI dispatcher is available");
+      dispatcher = core_dispatcher.Get ();
+    } else {
+      GST_DEBUG ("UI dispatcher is unavailable");
+    }
+  } else {
+    GST_DEBUG ("Use user passed UI dispatcher");
+  }
 
   self = (GstWasapi2Client *) g_object_new (GST_TYPE_WASAPI2_CLIENT,
       "device-class", device_class, "low-latency", low_latency,
-      "device-index", device_index, "device", device_id, NULL);
+      "device-index", device_index, "device", device_id,
+      "dispatcher", dispatcher, NULL);
+
+  /* Reset explicitly to ensure that it happens before
+   * RoInitializeWrapper dtor is called */
+  core_dispatcher.Reset ();
 
   if (!self->audio_client) {
     gst_object_unref (self);
index adc9125..7c9bd12 100644 (file)
@@ -73,7 +73,8 @@ gboolean  gst_wasapi2_client_get_volume (GstWasapi2Client * client,
 GstWasapi2Client * gst_wasapi2_client_new (GstWasapi2ClientDeviceClass device_class,
                                            gboolean low_latency,
                                            gint device_index,
-                                           const gchar * device_id);
+                                           const gchar * device_id,
+                                           gpointer dispatcher);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstWasapi2Client, gst_object_unref)
 
index ac51126..3688c2e 100644 (file)
@@ -182,7 +182,7 @@ gst_wasapi2_device_provider_probe_internal (GstWasapi2DeviceProvider * self,
     gchar *device_id = NULL;
     gchar *device_name = NULL;
 
-    client = gst_wasapi2_client_new (client_class, FALSE, i, NULL);
+    client = gst_wasapi2_client_new (client_class, FALSE, i, NULL, NULL);
 
     if (!client)
       return;
index a55767c..280f837 100644 (file)
@@ -69,6 +69,7 @@ enum
   PROP_LOW_LATENCY,
   PROP_MUTE,
   PROP_VOLUME,
+  PROP_DISPATCHER,
 };
 
 struct _GstWasapi2Sink
@@ -84,6 +85,7 @@ struct _GstWasapi2Sink
   gboolean low_latency;
   gboolean mute;
   gdouble volume;
+  gpointer dispatcher;
 
   gboolean mute_changed;
   gboolean volume_changed;
@@ -157,6 +159,14 @@ gst_wasapi2_sink_class_init (GstWasapi2SinkClass * klass)
           GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE |
           G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_DISPATCHER,
+      g_param_spec_pointer ("dispatcher", "Dispatcher",
+          "ICoreDispatcher COM object to use. In order for application to ask "
+          "permission of audio device, device activation should be running "
+          "on UI thread via ICoreDispatcher",
+          GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
+          G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_static_pad_template (element_class, &sink_template);
   gst_element_class_set_static_metadata (element_class, "Wasapi2Sink",
       "Sink/Audio/Hardware",
@@ -233,6 +243,9 @@ gst_wasapi2_sink_set_property (GObject * object, guint prop_id,
     case PROP_VOLUME:
       gst_wasapi2_sink_set_volume (self, g_value_get_double (value));
       break;
+    case PROP_DISPATCHER:
+      self->dispatcher = g_value_get_pointer (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -258,6 +271,9 @@ gst_wasapi2_sink_get_property (GObject * object, guint prop_id,
     case PROP_VOLUME:
       g_value_set_double (value, gst_wasapi2_sink_get_volume (self));
       break;
+    case PROP_DISPATCHER:
+      g_value_set_pointer (value, self->dispatcher);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -303,7 +319,7 @@ gst_wasapi2_sink_open_unlocked (GstAudioSink * asink)
 
   self->client =
       gst_wasapi2_client_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER,
-      self->low_latency, -1, self->device_id);
+      self->low_latency, -1, self->device_id, self->dispatcher);
 
   return ! !self->client;
 }
index 7f3bbee..f6a0fce 100644 (file)
@@ -67,6 +67,7 @@ enum
   PROP_LOW_LATENCY,
   PROP_MUTE,
   PROP_VOLUME,
+  PROP_DISPATCHER,
 };
 
 struct _GstWasapi2Src
@@ -82,6 +83,7 @@ struct _GstWasapi2Src
   gboolean low_latency;
   gboolean mute;
   gdouble volume;
+  gpointer dispatcher;
 
   gboolean mute_changed;
   gboolean volume_changed;
@@ -154,6 +156,14 @@ gst_wasapi2_src_class_init (GstWasapi2SrcClass * klass)
           GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE |
           G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_DISPATCHER,
+      g_param_spec_pointer ("dispatcher", "Dispatcher",
+          "ICoreDispatcher COM object to use. In order for application to ask "
+          "permission of audio device, device activation should be running "
+          "on UI thread via ICoreDispatcher",
+          GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
+          G_PARAM_STATIC_STRINGS));
+
   gst_element_class_add_static_pad_template (element_class, &src_template);
   gst_element_class_set_static_metadata (element_class, "Wasapi2Src",
       "Source/Audio/Hardware",
@@ -230,6 +240,9 @@ gst_wasapi2_src_set_property (GObject * object, guint prop_id,
     case PROP_VOLUME:
       gst_wasapi2_src_set_volume (self, g_value_get_double (value));
       break;
+    case PROP_DISPATCHER:
+      self->dispatcher = g_value_get_pointer (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -255,6 +268,9 @@ gst_wasapi2_src_get_property (GObject * object, guint prop_id,
     case PROP_VOLUME:
       g_value_set_double (value, gst_wasapi2_src_get_volume (self));
       break;
+    case PROP_DISPATCHER:
+      g_value_set_pointer (value, self->dispatcher);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -300,7 +316,7 @@ gst_wasapi2_src_open_unlocked (GstAudioSrc * asrc)
 
   self->client =
       gst_wasapi2_client_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE,
-      self->low_latency, -1, self->device_id);
+      self->low_latency, -1, self->device_id, self->dispatcher);
 
   return ! !self->client;
 }