Adding a helper library for various WinRT specific implementations. Currently this library supports only DeviceWatcher abstraction object which can be used for dynamic device add/remove detection. See also https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher?view=winrt-20348 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/947>
		
			
				
	
	
		
			724 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			724 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* GStreamer
 | |
|  * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Library General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Library General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Library General Public
 | |
|  * License along with this library; if not, write to the
 | |
|  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | |
|  * Boston, MA 02110-1301, USA.
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_CONFIG_H
 | |
| #include "config.h"
 | |
| #endif
 | |
| 
 | |
| #include "gstwinrtdevicewatcher.h"
 | |
| 
 | |
| /* workaround for GetCurrentTime collision */
 | |
| #ifdef GetCurrentTime
 | |
| #undef GetCurrentTime
 | |
| #endif
 | |
| 
 | |
| #include <windows.foundation.h>
 | |
| #include <wrl.h>
 | |
| #include <wrl/wrappers/corewrappers.h>
 | |
| 
 | |
| /* *INDENT-OFF* */
 | |
| typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformation IAddedHandler;
 | |
| typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformationUpdate IUpdatedHandler;
 | |
| typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformationUpdate IRemovedHandler;
 | |
| typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_IInspectable IEnumerationCompletedHandler;
 | |
| typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_IInspectable IStoppedHandler;
 | |
| 
 | |
| using namespace Microsoft::WRL;
 | |
| using namespace Microsoft::WRL::Wrappers;
 | |
| using namespace ABI::Windows::Foundation;
 | |
| using namespace ABI::Windows::Devices;
 | |
| using namespace ABI::Windows::Devices::Enumeration;
 | |
| 
 | |
| GST_DEBUG_CATEGORY_STATIC (gst_winrt_device_watcher_debug);
 | |
| #define GST_CAT_DEFAULT gst_winrt_device_watcher_debug
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_device_added (GstWinRTDeviceWatcher * self,
 | |
|     IDeviceInformation * info);
 | |
| static void
 | |
| gst_winrt_device_watcher_device_updated (GstWinRTDeviceWatcher * self,
 | |
|     IDeviceInformationUpdate * info_update);
 | |
| static void
 | |
| gst_winrt_device_watcher_device_removed (GstWinRTDeviceWatcher * self,
 | |
|     IDeviceInformationUpdate * info_update);
 | |
| static void
 | |
| gst_winrt_device_watcher_device_enumeration_completed (GstWinRTDeviceWatcher *
 | |
|     self);
 | |
| static void
 | |
| gst_winrt_device_watcher_device_enumeration_stopped (GstWinRTDeviceWatcher *
 | |
|     self);
 | |
| 
 | |
| class AddedHandler
 | |
|     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IAddedHandler>
 | |
| {
 | |
| public:
 | |
|   AddedHandler () {}
 | |
|   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
 | |
|   {
 | |
|     if (!listenr)
 | |
|       return E_INVALIDARG;
 | |
| 
 | |
|     listener_ = listenr;
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   IFACEMETHOD(Invoke)
 | |
|   (IDeviceWatcher* sender, IDeviceInformation * arg)
 | |
|   {
 | |
|     gst_winrt_device_watcher_device_added (listener_, arg);
 | |
| 
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   GstWinRTDeviceWatcher * listener_;
 | |
| };
 | |
| 
 | |
| class UpdatedHandler
 | |
|     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IUpdatedHandler>
 | |
| {
 | |
| public:
 | |
|   UpdatedHandler () {}
 | |
|   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
 | |
|   {
 | |
|     if (!listenr)
 | |
|       return E_INVALIDARG;
 | |
| 
 | |
|     listener_ = listenr;
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   IFACEMETHOD(Invoke)
 | |
|   (IDeviceWatcher* sender, IDeviceInformationUpdate * arg)
 | |
|   {
 | |
|     gst_winrt_device_watcher_device_updated (listener_, arg);
 | |
| 
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   GstWinRTDeviceWatcher * listener_;
 | |
| };
 | |
| 
 | |
| class RemovedHandler
 | |
|     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IRemovedHandler>
 | |
| {
 | |
| public:
 | |
|   RemovedHandler () {}
 | |
|   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
 | |
|   {
 | |
|     if (!listenr)
 | |
|       return E_INVALIDARG;
 | |
| 
 | |
|     listener_ = listenr;
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   IFACEMETHOD(Invoke)
 | |
|   (IDeviceWatcher* sender, IDeviceInformationUpdate * arg)
 | |
|   {
 | |
|     gst_winrt_device_watcher_device_removed (listener_, arg);
 | |
| 
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   GstWinRTDeviceWatcher * listener_;
 | |
| };
 | |
| 
 | |
| class EnumerationCompletedHandler
 | |
|     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IEnumerationCompletedHandler>
 | |
| {
 | |
| public:
 | |
|   EnumerationCompletedHandler () {}
 | |
|   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
 | |
|   {
 | |
|     if (!listenr)
 | |
|       return E_INVALIDARG;
 | |
| 
 | |
|     listener_ = listenr;
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   IFACEMETHOD(Invoke)
 | |
|   (IDeviceWatcher* sender, IInspectable * arg)
 | |
|   {
 | |
|     gst_winrt_device_watcher_device_enumeration_completed (listener_);
 | |
| 
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   GstWinRTDeviceWatcher * listener_;
 | |
| };
 | |
| 
 | |
| class StoppedHandler
 | |
|     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IStoppedHandler>
 | |
| {
 | |
| public:
 | |
|   StoppedHandler () {}
 | |
|   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
 | |
|   {
 | |
|     if (!listenr)
 | |
|       return E_INVALIDARG;
 | |
| 
 | |
|     listener_ = listenr;
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   IFACEMETHOD(Invoke)
 | |
|   (IDeviceWatcher* sender, IInspectable * arg)
 | |
|   {
 | |
|     gst_winrt_device_watcher_device_enumeration_stopped (listener_);
 | |
| 
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   GstWinRTDeviceWatcher * listener_;
 | |
| };
 | |
| /* *INDENT-ON* */
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
|   ComPtr < IDeviceWatcher > watcher;
 | |
| 
 | |
|   EventRegistrationToken added_token;
 | |
|   EventRegistrationToken updated_token;
 | |
|   EventRegistrationToken removed_token;
 | |
|   EventRegistrationToken enum_completed_token;
 | |
|   EventRegistrationToken stopped_token;
 | |
| } GstWinRTDeviceWatcherInner;
 | |
| 
 | |
| enum
 | |
| {
 | |
|   PROP_0,
 | |
|   PROP_DEVICE_CLASS,
 | |
| };
 | |
| 
 | |
| #define DEFAULT_DEVICE_CLASS GST_WINRT_DEVICE_CLASS_ALL
 | |
| 
 | |
| struct _GstWinRTDeviceWatcherPrivate
 | |
| {
 | |
|   GMutex lock;
 | |
|   GCond cond;
 | |
| 
 | |
|   GThread *thread;
 | |
|   GMainContext *context;
 | |
|   GMainLoop *loop;
 | |
| 
 | |
|   gboolean running;
 | |
| 
 | |
|   GstWinRTDeviceWatcherCallbacks callbacks;
 | |
|   gpointer user_data;
 | |
| 
 | |
|   GstWinRTDeviceWatcherInner *inner;
 | |
| 
 | |
|   GstWinRTDeviceClass device_class;
 | |
| };
 | |
| 
 | |
| GType
 | |
| gst_winrt_device_class_get_type (void)
 | |
| {
 | |
|   static gsize device_class_type = 0;
 | |
| 
 | |
|   if (g_once_init_enter (&device_class_type)) {
 | |
|     static const GEnumValue classes[] = {
 | |
|       {GST_WINRT_DEVICE_CLASS_ALL, "All", "all"},
 | |
|       {GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE, "AudioCapture", "audio-capture"},
 | |
|       {GST_WINRT_DEVICE_CLASS_AUDIO_RENDER, "AudioRender", "audio-render"},
 | |
|       {GST_WINRT_DEVICE_CLASS_PORTABLE_STORAGE_DEVICE,
 | |
|           "PortableStorageDevice", "portable-storage-device"},
 | |
|       {GST_WINRT_DEVICE_CLASS_VIDEO_CAPTURE,
 | |
|           "VideoCapture", "video-capture"},
 | |
|       {0, nullptr, nullptr},
 | |
|     };
 | |
|     GType tmp = g_enum_register_static ("GstWinRTDeviceClass", classes);
 | |
|     g_once_init_leave (&device_class_type, tmp);
 | |
|   }
 | |
| 
 | |
|   return (GType) device_class_type;
 | |
| }
 | |
| 
 | |
| static void gst_winrt_device_watcher_constructed (GObject * object);
 | |
| static void gst_winrt_device_watcher_finalize (GObject * object);
 | |
| static void gst_winrt_device_watcher_set_property (GObject * object,
 | |
|     guint prop_id, const GValue * value, GParamSpec * pspec);
 | |
| static void gst_winrt_device_watcher_get_property (GObject * object,
 | |
|     guint prop_id, GValue * value, GParamSpec * pspec);
 | |
| 
 | |
| static gpointer
 | |
| gst_winrt_device_watcher_thread_func (GstWinRTDeviceWatcher * self);
 | |
| 
 | |
| #define gst_winrt_device_watcher_parent_class parent_class
 | |
| G_DEFINE_TYPE_WITH_PRIVATE (GstWinRTDeviceWatcher, gst_winrt_device_watcher,
 | |
|     GST_TYPE_OBJECT);
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_class_init (GstWinRTDeviceWatcherClass * klass)
 | |
| {
 | |
|   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 | |
| 
 | |
|   gobject_class->constructed = gst_winrt_device_watcher_constructed;
 | |
|   gobject_class->finalize = gst_winrt_device_watcher_finalize;
 | |
|   gobject_class->set_property = gst_winrt_device_watcher_set_property;
 | |
|   gobject_class->get_property = gst_winrt_device_watcher_get_property;
 | |
| 
 | |
|   g_object_class_install_property (gobject_class, PROP_DEVICE_CLASS,
 | |
|       g_param_spec_enum ("device-class", "Device Class",
 | |
|           "Device class to watch", GST_TYPE_WINRT_DEVICE_CLASS,
 | |
|           DEFAULT_DEVICE_CLASS,
 | |
|           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 | |
|               G_PARAM_STATIC_STRINGS)));
 | |
| 
 | |
|   GST_DEBUG_CATEGORY_INIT (gst_winrt_device_watcher_debug,
 | |
|       "winrtdevicewatcher", 0, "winrtdevicewatcher");
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_init (GstWinRTDeviceWatcher * self)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv;
 | |
| 
 | |
|   self->priv = priv = (GstWinRTDeviceWatcherPrivate *)
 | |
|       gst_winrt_device_watcher_get_instance_private (self);
 | |
| 
 | |
|   g_mutex_init (&priv->lock);
 | |
|   g_cond_init (&priv->cond);
 | |
|   priv->context = g_main_context_new ();
 | |
|   priv->loop = g_main_loop_new (priv->context, FALSE);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_constructed (GObject * object)
 | |
| {
 | |
|   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   g_mutex_lock (&priv->lock);
 | |
|   priv->thread = g_thread_new ("GstWinRTDeviceWatcher",
 | |
|       (GThreadFunc) gst_winrt_device_watcher_thread_func, self);
 | |
|   while (!g_main_loop_is_running (priv->loop))
 | |
|     g_cond_wait (&priv->cond, &priv->lock);
 | |
|   g_mutex_unlock (&priv->lock);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_finalize (GObject * object)
 | |
| {
 | |
|   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   g_main_loop_quit (priv->loop);
 | |
|   if (g_thread_self () != priv->thread) {
 | |
|     g_thread_join (priv->thread);
 | |
|     g_main_loop_unref (priv->loop);
 | |
|     g_main_context_unref (priv->context);
 | |
|   } else {
 | |
|     g_warning ("Trying join from self-thread");
 | |
|   }
 | |
| 
 | |
|   g_mutex_clear (&priv->lock);
 | |
|   g_cond_clear (&priv->cond);
 | |
| 
 | |
|   G_OBJECT_CLASS (parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_set_property (GObject * object, guint prop_id,
 | |
|     const GValue * value, GParamSpec * pspec)
 | |
| {
 | |
|   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   switch (prop_id) {
 | |
|     case PROP_DEVICE_CLASS:
 | |
|       priv->device_class = (GstWinRTDeviceClass) g_value_get_enum (value);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_get_property (GObject * object, guint prop_id,
 | |
|     GValue * value, GParamSpec * pspec)
 | |
| {
 | |
|   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   switch (prop_id) {
 | |
|     case PROP_DEVICE_CLASS:
 | |
|       g_value_set_enum (value, priv->device_class);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| loop_running_cb (GstWinRTDeviceWatcher * self)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   g_mutex_lock (&priv->lock);
 | |
|   g_cond_signal (&priv->cond);
 | |
|   g_mutex_unlock (&priv->lock);
 | |
| 
 | |
|   return G_SOURCE_REMOVE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_thread_func_inner (GstWinRTDeviceWatcher * self)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
|   GSource *idle_source;
 | |
|   HRESULT hr;
 | |
|   GstWinRTDeviceWatcherInner *inner = nullptr;
 | |
|   ComPtr < IDeviceInformationStatics > factory;
 | |
|   ComPtr < IDeviceWatcher > watcher;
 | |
|   ComPtr < IAddedHandler > added_handler;
 | |
|   ComPtr < IUpdatedHandler > updated_handler;
 | |
|   ComPtr < IRemovedHandler > removed_handler;
 | |
|   ComPtr < IEnumerationCompletedHandler > enum_completed_handler;
 | |
|   ComPtr < IStoppedHandler > stopped_handler;
 | |
| 
 | |
|   g_main_context_push_thread_default (priv->context);
 | |
| 
 | |
|   idle_source = g_idle_source_new ();
 | |
|   g_source_set_callback (idle_source,
 | |
|       (GSourceFunc) loop_running_cb, self, nullptr);
 | |
|   g_source_attach (idle_source, priv->context);
 | |
|   g_source_unref (idle_source);
 | |
| 
 | |
|   hr = GetActivationFactory (HStringReference
 | |
|       (RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get (),
 | |
|       &factory);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self,
 | |
|         "Failed to get IDeviceInformationStatics, hr: 0x%x", (guint) hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = factory->CreateWatcherDeviceClass ((DeviceClass) priv->device_class,
 | |
|       &watcher);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self,
 | |
|         "Failed to create IDeviceWatcher, hr: 0x%x", (guint) hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = MakeAndInitialize < AddedHandler > (&added_handler, self);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to create added handler, hr: 0x%x", hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = MakeAndInitialize < UpdatedHandler > (&updated_handler, self);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to create updated handler, hr: 0x%x", hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = MakeAndInitialize < RemovedHandler > (&removed_handler, self);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to create removed handler, hr: 0x%x", hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = MakeAndInitialize < EnumerationCompletedHandler >
 | |
|       (&enum_completed_handler, self);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self,
 | |
|         "Failed to create enumeration completed handler, hr: 0x%x", hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = MakeAndInitialize < StoppedHandler > (&stopped_handler, self);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to create stopped handler, hr: 0x%x", hr);
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   inner = new GstWinRTDeviceWatcherInner ();
 | |
|   hr = watcher->add_Added (added_handler.Get (), &inner->added_token);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to register added handler, hr: 0x%x", hr);
 | |
|     delete inner;
 | |
|     inner = nullptr;
 | |
| 
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = watcher->add_Updated (updated_handler.Get (), &inner->updated_token);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to register updated handler, hr: 0x%x", hr);
 | |
|     delete inner;
 | |
|     inner = nullptr;
 | |
| 
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = watcher->add_Removed (removed_handler.Get (), &inner->removed_token);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to register removed handler, hr: 0x%x", hr);
 | |
|     delete inner;
 | |
|     inner = nullptr;
 | |
| 
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = watcher->add_EnumerationCompleted (enum_completed_handler.Get (),
 | |
|       &inner->enum_completed_token);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self,
 | |
|         "Failed to register enumeration completed handler, hr: 0x%x", hr);
 | |
|     delete inner;
 | |
|     inner = nullptr;
 | |
| 
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   hr = watcher->add_Stopped (stopped_handler.Get (), &inner->stopped_token);
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (self, "Failed to register stopped handler, hr: 0x%x", hr);
 | |
|     delete inner;
 | |
|     inner = nullptr;
 | |
| 
 | |
|     goto run_loop;
 | |
|   }
 | |
| 
 | |
|   inner->watcher = watcher;
 | |
|   priv->inner = inner;
 | |
| 
 | |
| run_loop:
 | |
|   GST_INFO_OBJECT (self, "Starting loop");
 | |
|   g_main_loop_run (priv->loop);
 | |
|   GST_INFO_OBJECT (self, "Stopped loop");
 | |
| 
 | |
|   if (inner) {
 | |
|     if (priv->running)
 | |
|       watcher->Stop ();
 | |
| 
 | |
|     watcher->remove_Added (inner->added_token);
 | |
|     watcher->remove_Updated (inner->updated_token);
 | |
|     watcher->remove_Removed (inner->removed_token);
 | |
|     watcher->remove_EnumerationCompleted (inner->enum_completed_token);
 | |
|     watcher->remove_Stopped (inner->stopped_token);
 | |
| 
 | |
|     delete inner;
 | |
|   }
 | |
| 
 | |
|   g_main_context_pop_thread_default (priv->context);
 | |
| }
 | |
| 
 | |
| static gpointer
 | |
| gst_winrt_device_watcher_thread_func (GstWinRTDeviceWatcher * self)
 | |
| {
 | |
|   RoInitializeWrapper initialize (RO_INIT_MULTITHREADED);
 | |
| 
 | |
|   /* wrap with another function so that everything can happen
 | |
|    * before RoInitializeWrapper is destructed */
 | |
|   gst_winrt_device_watcher_thread_func_inner (self);
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_device_added (GstWinRTDeviceWatcher * self,
 | |
|     IDeviceInformation * info)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   GST_DEBUG_OBJECT (self, "Device added");
 | |
| 
 | |
|   if (priv->callbacks.added)
 | |
|     priv->callbacks.added (self, info, priv->user_data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_device_updated (GstWinRTDeviceWatcher * self,
 | |
|     IDeviceInformationUpdate * info_update)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   GST_DEBUG_OBJECT (self, "Device updated");
 | |
| 
 | |
|   if (priv->callbacks.updated)
 | |
|     priv->callbacks.updated (self, info_update, priv->user_data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_device_removed (GstWinRTDeviceWatcher * self,
 | |
|     IDeviceInformationUpdate * info_update)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   GST_DEBUG_OBJECT (self, "Device removed");
 | |
| 
 | |
|   if (priv->callbacks.removed)
 | |
|     priv->callbacks.removed (self, info_update, priv->user_data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_device_enumeration_completed (GstWinRTDeviceWatcher *
 | |
|     self)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv = self->priv;
 | |
| 
 | |
|   GST_DEBUG_OBJECT (self, "Enumeration completed");
 | |
| 
 | |
|   if (priv->callbacks.enumeration_completed)
 | |
|     priv->callbacks.enumeration_completed (self, priv->user_data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_winrt_device_watcher_device_enumeration_stopped (GstWinRTDeviceWatcher *
 | |
|     self)
 | |
| {
 | |
|   GST_DEBUG_OBJECT (self, "Stopped");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * gst_winrt_device_watcher_new:
 | |
|  * @device_class: a #GstWinRTDeviceClass to watch
 | |
|  * @callbacks: a pointer to #GstWinRTDeviceWatcherCallbacks
 | |
|  * @user_data: a user_data argument for the callbacks
 | |
|  *
 | |
|  * Constructs a new #GstWinRTDeviceWatcher object for watching device update
 | |
|  * of @device_class
 | |
|  *
 | |
|  * Returns: (transfer full) (nullable): a new #GstWinRTDeviceWatcher
 | |
|  * or %NULL when failed to create/setup #GstWinRTDeviceWatcher object
 | |
|  *
 | |
|  * Since: 1.20
 | |
|  */
 | |
| GstWinRTDeviceWatcher *
 | |
| gst_winrt_device_watcher_new (GstWinRTDeviceClass device_class,
 | |
|     const GstWinRTDeviceWatcherCallbacks * callbacks, gpointer user_data)
 | |
| {
 | |
|   GstWinRTDeviceWatcher *self;
 | |
|   GstWinRTDeviceWatcherPrivate *priv;
 | |
| 
 | |
|   g_return_val_if_fail (callbacks != nullptr, nullptr);
 | |
| 
 | |
|   self = (GstWinRTDeviceWatcher *)
 | |
|       g_object_new (GST_TYPE_WINRT_DEVICE_WATCHER, "device-class", device_class,
 | |
|       nullptr);
 | |
| 
 | |
|   priv = self->priv;
 | |
|   if (!priv->inner) {
 | |
|     gst_object_unref (self);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   priv->callbacks = *callbacks;
 | |
|   priv->user_data = user_data;
 | |
| 
 | |
|   gst_object_ref_sink (self);
 | |
| 
 | |
|   return self;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * gst_winrt_device_watcher_start:
 | |
|  * @device_class: a #GstWinRTDeviceClass to watch
 | |
|  *
 | |
|  * Starts watching device update.
 | |
|  *
 | |
|  * Returns: %TRUE if successful
 | |
|  *
 | |
|  * Since: 1.20
 | |
|  */
 | |
| gboolean
 | |
| gst_winrt_device_watcher_start (GstWinRTDeviceWatcher * watcher)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv;
 | |
|   GstWinRTDeviceWatcherInner *inner;
 | |
|   HRESULT hr;
 | |
| 
 | |
|   g_return_val_if_fail (GST_IS_WINRT_DEVICE_WATCHER (watcher), FALSE);
 | |
| 
 | |
|   priv = watcher->priv;
 | |
|   inner = priv->inner;
 | |
| 
 | |
|   GST_DEBUG_OBJECT (watcher, "Start");
 | |
| 
 | |
|   g_mutex_lock (&priv->lock);
 | |
|   if (priv->running) {
 | |
|     GST_DEBUG_OBJECT (watcher, "Already running");
 | |
|     g_mutex_unlock (&priv->lock);
 | |
| 
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   hr = inner->watcher->Start ();
 | |
|   if (FAILED (hr)) {
 | |
|     GST_ERROR_OBJECT (watcher, "Failed to start watcher, hr: 0x%x", (guint) hr);
 | |
|     g_mutex_unlock (&priv->lock);
 | |
| 
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   priv->running = TRUE;
 | |
|   g_mutex_unlock (&priv->lock);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * gst_winrt_device_watcher_stop:
 | |
|  * @device_class: a #GstWinRTDeviceClass to watch
 | |
|  *
 | |
|  * Stops watching device update.
 | |
|  *
 | |
|  * Since: 1.20
 | |
|  */
 | |
| void
 | |
| gst_winrt_device_watcher_stop (GstWinRTDeviceWatcher * watcher)
 | |
| {
 | |
|   GstWinRTDeviceWatcherPrivate *priv;
 | |
|   GstWinRTDeviceWatcherInner *inner;
 | |
|   HRESULT hr;
 | |
| 
 | |
|   g_return_if_fail (GST_IS_WINRT_DEVICE_WATCHER (watcher));
 | |
| 
 | |
|   GST_DEBUG_OBJECT (watcher, "Stop");
 | |
| 
 | |
|   priv = watcher->priv;
 | |
|   inner = priv->inner;
 | |
| 
 | |
|   g_mutex_lock (&priv->lock);
 | |
|   if (!priv->running) {
 | |
|     g_mutex_unlock (&priv->lock);
 | |
| 
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   priv->running = FALSE;
 | |
|   hr = inner->watcher->Stop ();
 | |
|   if (FAILED (hr)) {
 | |
|     GST_WARNING_OBJECT (watcher,
 | |
|         "Failed to stop watcher, hr: 0x%x", (guint) hr);
 | |
|   }
 | |
|   g_mutex_unlock (&priv->lock);
 | |
| }
 |