mfvideosrc: Add a new property for ICoreDispatcher setting
authorSeungha Yang <seungha@centricular.com>
Fri, 24 Jul 2020 11:48:20 +0000 (20:48 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 26 Jul 2020 12:52:15 +0000 (12:52 +0000)
Since the commit c29c71ae9d46cc70e29e0cca2313917f319ef6f2,
device activation method will be called from an internal thread.

A problem is that, CoreApplication::GetCurrentView()
method will return nullptr if it was called from non-UI thread,
and as a result, currently implemented method for accessing ICoreDispatcher
will not work in any case. There seems to be no robust way for
accessing ICoreDispatcher other then setting it by user.

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

sys/mediafoundation/gstmfcapturewinrt.cpp
sys/mediafoundation/gstmfcapturewinrt.h
sys/mediafoundation/gstmfdevice.c
sys/mediafoundation/gstmfsourceobject.c
sys/mediafoundation/gstmfsourceobject.h
sys/mediafoundation/gstmfvideosrc.c
sys/mediafoundation/mediacapturewrapper.cpp
sys/mediafoundation/mediacapturewrapper.h

index 3af6a86..48f036a 100644 (file)
@@ -42,6 +42,12 @@ GST_DEBUG_CATEGORY_EXTERN (gst_mf_source_object_debug);
 
 G_END_DECLS
 
+enum
+{
+  PROP_0,
+  PROP_DISPATCHER,
+};
+
 struct _GstMFCaptureWinRT
 {
   GstMFSourceObject parent;
@@ -61,10 +67,16 @@ struct _GstMFCaptureWinRT
   GstVideoInfo info;
   gboolean flushing;
   gboolean got_error;
+
+  gpointer dispatcher;
 };
 
 static void gst_mf_capture_winrt_constructed (GObject * object);
 static void gst_mf_capture_winrt_finalize (GObject * object);
+static void gst_mf_capture_winrt_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+static void gst_mf_capture_winrt_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
 
 static gboolean gst_mf_capture_winrt_start (GstMFSourceObject * object);
 static gboolean gst_mf_capture_winrt_stop  (GstMFSourceObject * object);
@@ -94,6 +106,14 @@ gst_mf_capture_winrt_class_init (GstMFCaptureWinRTClass * klass)
 
   gobject_class->constructed = gst_mf_capture_winrt_constructed;
   gobject_class->finalize = gst_mf_capture_winrt_finalize;
+  gobject_class->get_property = gst_mf_capture_winrt_get_property;
+  gobject_class->set_property = gst_mf_capture_winrt_set_property;
+
+  g_object_class_install_property (gobject_class, PROP_DISPATCHER,
+      g_param_spec_pointer ("dispatcher", "Dispatcher",
+          "ICoreDispatcher COM object to use",
+          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+          G_PARAM_STATIC_STRINGS)));
 
   source_class->start = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_start);
   source_class->stop = GST_DEBUG_FUNCPTR (gst_mf_capture_winrt_stop);
@@ -150,6 +170,38 @@ gst_mf_capture_winrt_finalize (GObject * object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+static void
+gst_mf_capture_winrt_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object);
+
+  switch (prop_id) {
+    case PROP_DISPATCHER:
+      g_value_set_pointer (value, self->dispatcher);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_mf_capture_winrt_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstMFCaptureWinRT *self = GST_MF_CAPTURE_WINRT (object);
+
+  switch (prop_id) {
+    case PROP_DISPATCHER:
+      self->dispatcher = g_value_get_pointer (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
 static gboolean
 gst_mf_capture_winrt_main_loop_running_cb (GstMFCaptureWinRT * self)
 {
@@ -182,7 +234,7 @@ gst_mf_capture_winrt_thread_func (GstMFCaptureWinRT * self)
 
   RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED);
 
-  self->capture = new MediaCaptureWrapper;
+  self->capture = new MediaCaptureWrapper(self->dispatcher);
   callbacks.frame_arrived = gst_mf_capture_winrt_on_frame;
   callbacks.failed = gst_mf_capture_winrt_on_failed;
   self->capture->RegisterCb (callbacks, self);
@@ -587,18 +639,39 @@ gst_mf_capture_winrt_set_caps (GstMFSourceObject * object, GstCaps * caps)
 
 GstMFSourceObject *
 gst_mf_capture_winrt_new (GstMFSourceType type, gint device_index,
-    const gchar * device_name, const gchar * device_path)
+    const gchar * device_name, const gchar * device_path, gpointer dispatcher)
 {
   GstMFSourceObject *self;
+  ComPtr<ICoreDispatcher> core_dispatcher;
+  /* Multiple COM init is allowed */
+  RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED);
 
   /* TODO: Add audio capture support */
   g_return_val_if_fail (type == GST_MF_SOURCE_TYPE_VIDEO, NULL);
 
+  /* If application didn't pass ICoreDispatcher object,
+   * try to get dispatcher object for the current thread */
+  if (!dispatcher) {
+    HRESULT hr;
+
+    hr = FindCoreDispatcherForCurrentThread (&core_dispatcher);
+    if (gst_mf_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 = (GstMFSourceObject *) g_object_new (GST_TYPE_MF_CAPTURE_WINRT,
       "source-type", type, "device-index", device_index, "device-name",
-      device_name, "device-path", device_path, NULL);
+      device_name, "device-path", device_path, "dispatcher", dispatcher, NULL);
 
-  gst_object_ref_sink (self);
+  /* Reset explicitly to ensure that it happens before
+   * RoInitializeWrapper dtor is called */
+  core_dispatcher.Reset ();
 
   if (!self->opened) {
     GST_WARNING_OBJECT (self, "Couldn't open device");
@@ -606,5 +679,7 @@ gst_mf_capture_winrt_new (GstMFSourceType type, gint device_index,
     return NULL;
   }
 
+  gst_object_ref_sink (self);
+
   return self;
 }
index 2f4cf15..6b4f52a 100644 (file)
@@ -32,7 +32,8 @@ G_DECLARE_FINAL_TYPE (GstMFCaptureWinRT, gst_mf_capture_winrt,
 GstMFSourceObject * gst_mf_capture_winrt_new (GstMFSourceType type,
                                               gint device_index,
                                               const gchar * device_name,
-                                              const gchar * device_path);
+                                              const gchar * device_path,
+                                              gpointer dispatcher);
 
 G_END_DECLS
 
index abb704f..7e2d74a 100644 (file)
@@ -176,7 +176,8 @@ gst_mf_device_provider_probe (GstDeviceProvider * provider)
     gchar *device_name = NULL;
     gchar *device_path = NULL;
 
-    obj = gst_mf_source_object_new (GST_MF_SOURCE_TYPE_VIDEO, i, NULL, NULL);
+    obj = gst_mf_source_object_new (GST_MF_SOURCE_TYPE_VIDEO,
+        i, NULL, NULL, NULL);
     if (!obj)
       break;
 
index 407e2ba..2b7c4d0 100644 (file)
@@ -310,7 +310,7 @@ gst_mf_source_object_use_winrt_api (void)
 
 GstMFSourceObject *
 gst_mf_source_object_new (GstMFSourceType type, gint device_index,
-    const gchar * device_name, const gchar * device_path)
+    const gchar * device_name, const gchar * device_path, gpointer dispatcher)
 {
 #if (!GST_MF_WINAPI_APP)
   GST_INFO ("Try IMFSourceReader implementation");
@@ -320,12 +320,12 @@ gst_mf_source_object_new (GstMFSourceType type, gint device_index,
 #if (!GST_MF_WINAPI_DESKTOP)
   GST_INFO ("Try WinRT implementation");
   return gst_mf_capture_winrt_new (type,
-      device_index, device_name, device_path);
+      device_index, device_name, device_path, dispatcher);
 #else
   if (gst_mf_source_object_use_winrt_api ()) {
     GST_INFO ("Both Desktop and WinRT APIs were enabled, user choice: WinRT");
     return gst_mf_capture_winrt_new (type,
-        device_index, device_name, device_path);
+        device_index, device_name, device_path, dispatcher);
   } else {
     GST_INFO
         ("Both Desktop and WinRT APIs were enabled, default: IMFSourceReader");
index 2f9266d..48bdcb0 100644 (file)
@@ -106,7 +106,8 @@ gboolean        gst_mf_source_object_set_caps     (GstMFSourceObject * object,
 GstMFSourceObject * gst_mf_source_object_new      (GstMFSourceType type,
                                                    gint device_index,
                                                    const gchar * device_name,
-                                                   const gchar * device_path);
+                                                   const gchar * device_path,
+                                                   gpointer dispatcher);
 
 /* Utility methods */
 gint            gst_mf_source_object_caps_compare (GstCaps * caps1,
index 4841e5f..ed67ddf 100644 (file)
@@ -80,6 +80,7 @@ struct _GstMFVideoSrc
   gchar *device_path;
   gchar *device_name;
   gint device_index;
+  gpointer dispatcher;
 };
 
 enum
@@ -88,6 +89,7 @@ enum
   PROP_DEVICE_PATH,
   PROP_DEVICE_NAME,
   PROP_DEVICE_INDEX,
+  PROP_DISPATCHER,
 };
 
 #define DEFAULT_DEVICE_PATH     NULL
@@ -141,6 +143,15 @@ gst_mf_video_src_class_init (GstMFVideoSrcClass * klass)
           "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
           G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
           G_PARAM_STATIC_STRINGS));
+#if GST_MF_WINAPI_APP
+  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 capture device, device activation should be running "
+          "on UI thread via ICoreDispatcher",
+          GST_PARAM_CONDITIONALLY_AVAILABLE | GST_PARAM_MUTABLE_READY |
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
 
   gst_element_class_set_static_metadata (element_class,
       "Media Foundation Video Source",
@@ -201,6 +212,11 @@ gst_mf_video_src_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_DEVICE_INDEX:
       g_value_set_int (value, self->device_index);
       break;
+#if GST_MF_WINAPI_APP
+    case PROP_DISPATCHER:
+      g_value_set_pointer (value, self->dispatcher);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -225,6 +241,11 @@ gst_mf_video_src_set_property (GObject * object, guint prop_id,
     case PROP_DEVICE_INDEX:
       self->device_index = g_value_get_int (value);
       break;
+#if GST_MF_WINAPI_APP
+    case PROP_DISPATCHER:
+      self->dispatcher = g_value_get_pointer (value);
+      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -239,7 +260,7 @@ gst_mf_video_src_start (GstBaseSrc * src)
   GST_DEBUG_OBJECT (self, "Start");
 
   self->source = gst_mf_source_object_new (GST_MF_SOURCE_TYPE_VIDEO,
-      self->device_index, self->device_name, self->device_path);
+      self->device_index, self->device_name, self->device_path, NULL);
 
   self->first_pts = GST_CLOCK_TIME_NONE;
   self->n_frames = 0;
index 90a02b9..b8e0af9 100644 (file)
@@ -383,14 +383,21 @@ error:
   return hr;
 }
 
-MediaCaptureWrapper::MediaCaptureWrapper()
+MediaCaptureWrapper::MediaCaptureWrapper(gpointer dispatcher)
   : user_data_(nullptr)
 {
   user_cb_.frame_arrived = nullptr;
   user_cb_.failed = nullptr;
 
-  /* Store CoreDispatecher if available */
-  findCoreDispatcher();
+  if (dispatcher) {
+    ComPtr<IInspectable> inspectable =
+        reinterpret_cast<IInspectable*> (dispatcher);
+    HRESULT hr;
+
+    hr = inspectable.As (&dispatcher_);
+    if (gst_mf_result (hr))
+      GST_INFO("Main UI dispatcher is available");
+  }
 }
 
 MediaCaptureWrapper::~MediaCaptureWrapper()
@@ -932,35 +939,6 @@ MediaCaptureWrapper::onCaptureFailed(IMediaCapture *capture,
   return S_OK;
 }
 
-void
-MediaCaptureWrapper::findCoreDispatcher()
-{
-  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_mf_result(hr))
-    return;
-
-  ComPtr<ICoreApplicationView> core_app_view;
-  hr = core_app->GetCurrentView (&core_app_view);
-  if (!gst_mf_result(hr))
-    return;
-
-  ComPtr<ICoreWindow> core_window;
-  hr = core_app_view->get_CoreWindow (&core_window);
-  if (!gst_mf_result(hr))
-    return;
-
-  hr = core_window->get_Dispatcher (&dispatcher_);
-  if (!gst_mf_result(hr))
-    return;
-
-  GST_DEBUG("Main UI dispatcher is available");
-}
-
 HRESULT
 MediaCaptureWrapper::enumrateFrameSourceGroup
     (std::vector<GstWinRTMediaFrameSourceGroup> &groupList)
@@ -1026,3 +1004,28 @@ MediaCaptureWrapper::enumrateFrameSourceGroup
 
   return S_OK;
 }
+
+HRESULT
+FindCoreDispatcherForCurrentThread (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 (FAILED (hr))
+    return hr;
+
+  ComPtr<ICoreApplicationView> core_app_view;
+  hr = core_app->GetCurrentView (&core_app_view);
+  if (FAILED (hr))
+    return hr;
+
+  ComPtr<ICoreWindow> core_window;
+  hr = core_app_view->get_CoreWindow (&core_window);
+  if (FAILED (hr))
+    return hr;
+
+  return core_window->get_Dispatcher (dispatcher);
+}
\ No newline at end of file
index 2d029a1..fd609e7 100644 (file)
@@ -117,7 +117,7 @@ typedef struct
 class MediaCaptureWrapper
 {
 public:
-  MediaCaptureWrapper();
+  MediaCaptureWrapper(gpointer dispatcher);
   ~MediaCaptureWrapper();
 
   void RegisterCb(const MediaCaptureWrapperCallbacks &cb,
@@ -163,7 +163,6 @@ private:
                          IMediaFrameArrivedEventArgs *args);
   HRESULT onCaptureFailed(IMediaCapture *capture,
                           IMediaCaptureFailedEventArgs *args);
-  void findCoreDispatcher();
   static HRESULT enumrateFrameSourceGroup(std::vector<GstWinRTMediaFrameSourceGroup> &list);
 
   template <typename CB>
@@ -214,4 +213,7 @@ private:
   }
 };
 
+HRESULT
+FindCoreDispatcherForCurrentThread(ICoreDispatcher ** dispatcher);
+
 #endif /* __GST_MEDIA_CAPTURE_WRAPPER_H__ */
\ No newline at end of file