From 502aea3969da71ccc974cc0ea3b6886888241688 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Fri, 24 Jul 2020 20:48:20 +0900 Subject: [PATCH] mfvideosrc: Add a new property for ICoreDispatcher setting 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: --- sys/mediafoundation/gstmfcapturewinrt.cpp | 83 +++++++++++++++++++++++++++-- sys/mediafoundation/gstmfcapturewinrt.h | 3 +- sys/mediafoundation/gstmfdevice.c | 3 +- sys/mediafoundation/gstmfsourceobject.c | 6 +-- sys/mediafoundation/gstmfsourceobject.h | 3 +- sys/mediafoundation/gstmfvideosrc.c | 23 +++++++- sys/mediafoundation/mediacapturewrapper.cpp | 67 ++++++++++++----------- sys/mediafoundation/mediacapturewrapper.h | 6 ++- 8 files changed, 149 insertions(+), 45 deletions(-) diff --git a/sys/mediafoundation/gstmfcapturewinrt.cpp b/sys/mediafoundation/gstmfcapturewinrt.cpp index 3af6a86..48f036a 100644 --- a/sys/mediafoundation/gstmfcapturewinrt.cpp +++ b/sys/mediafoundation/gstmfcapturewinrt.cpp @@ -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 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; } diff --git a/sys/mediafoundation/gstmfcapturewinrt.h b/sys/mediafoundation/gstmfcapturewinrt.h index 2f4cf15..6b4f52a 100644 --- a/sys/mediafoundation/gstmfcapturewinrt.h +++ b/sys/mediafoundation/gstmfcapturewinrt.h @@ -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 diff --git a/sys/mediafoundation/gstmfdevice.c b/sys/mediafoundation/gstmfdevice.c index abb704f..7e2d74a 100644 --- a/sys/mediafoundation/gstmfdevice.c +++ b/sys/mediafoundation/gstmfdevice.c @@ -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; diff --git a/sys/mediafoundation/gstmfsourceobject.c b/sys/mediafoundation/gstmfsourceobject.c index 407e2ba..2b7c4d0 100644 --- a/sys/mediafoundation/gstmfsourceobject.c +++ b/sys/mediafoundation/gstmfsourceobject.c @@ -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"); diff --git a/sys/mediafoundation/gstmfsourceobject.h b/sys/mediafoundation/gstmfsourceobject.h index 2f9266d..48bdcb0 100644 --- a/sys/mediafoundation/gstmfsourceobject.h +++ b/sys/mediafoundation/gstmfsourceobject.h @@ -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, diff --git a/sys/mediafoundation/gstmfvideosrc.c b/sys/mediafoundation/gstmfvideosrc.c index 4841e5f..ed67ddf 100644 --- a/sys/mediafoundation/gstmfvideosrc.c +++ b/sys/mediafoundation/gstmfvideosrc.c @@ -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; diff --git a/sys/mediafoundation/mediacapturewrapper.cpp b/sys/mediafoundation/mediacapturewrapper.cpp index 90a02b9..b8e0af9 100644 --- a/sys/mediafoundation/mediacapturewrapper.cpp +++ b/sys/mediafoundation/mediacapturewrapper.cpp @@ -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 inspectable = + reinterpret_cast (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 core_app; - hr = GetActivationFactory (hstr_core_app.Get(), &core_app); - if (!gst_mf_result(hr)) - return; - - ComPtr core_app_view; - hr = core_app->GetCurrentView (&core_app_view); - if (!gst_mf_result(hr)) - return; - - ComPtr 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 &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 core_app; + hr = GetActivationFactory (hstr_core_app.Get(), &core_app); + if (FAILED (hr)) + return hr; + + ComPtr core_app_view; + hr = core_app->GetCurrentView (&core_app_view); + if (FAILED (hr)) + return hr; + + ComPtr 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 diff --git a/sys/mediafoundation/mediacapturewrapper.h b/sys/mediafoundation/mediacapturewrapper.h index 2d029a1..fd609e7 100644 --- a/sys/mediafoundation/mediacapturewrapper.h +++ b/sys/mediafoundation/mediacapturewrapper.h @@ -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 &list); template @@ -214,4 +213,7 @@ private: } }; +HRESULT +FindCoreDispatcherForCurrentThread(ICoreDispatcher ** dispatcher); + #endif /* __GST_MEDIA_CAPTURE_WRAPPER_H__ */ \ No newline at end of file -- 2.7.4