--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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.
+ */
+
+#ifndef __GST_WINRT_DEVICE_WATCHER_H__
+#define __GST_WINRT_DEVICE_WATCHER_H__
+
+#include <gst/gst.h>
+#include <gst/winrt/winrt-prelude.h>
+#include <windows.devices.enumeration.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_WINRT_DEVICE_WATCHER (gst_winrt_device_watcher_get_type())
+#define GST_WINRT_DEVICE_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_WINRT_DEVICE_WATCHER, GstWinRTDeviceWatcher))
+#define GST_WINRT_DEVICE_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_WINRT_DEVICE_WATCHER, GstWinRTDeviceWatcherClass))
+#define GST_IS_WINRT_DEVICE_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_WINRT_DEVICE_WATCHER))
+#define GST_IS_WINRT_DEVICE_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_WINRT_DEVICE_WATCHER))
+#define GST_WINRT_DEVICE_WATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_WINRT_DEVICE_WATCHER, GstWinRTDeviceWatcherClass))
+#define GST_WINRT_DEVICE_WATCHER_CAST(obj) ((GstWinRTDeviceWatcher *)obj)
+
+typedef struct _GstWinRTDeviceWatcher GstWinRTDeviceWatcher;
+typedef struct _GstWinRTDeviceWatcherClass GstWinRTDeviceWatcherClass;
+typedef struct _GstWinRTDeviceWatcherPrivate GstWinRTDeviceWatcherPrivate;
+
+/* ABI::Windows::Devices::Enumeration::DeviceClass */
+#define GST_TYPE_WINRT_DEVICE_CLASS (gst_winrt_device_class_get_type ())
+typedef enum
+{
+ GST_WINRT_DEVICE_CLASS_ALL = 0,
+ GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE = 1,
+ GST_WINRT_DEVICE_CLASS_AUDIO_RENDER = 2,
+ GST_WINRT_DEVICE_CLASS_PORTABLE_STORAGE_DEVICE = 3,
+ GST_WINRT_DEVICE_CLASS_VIDEO_CAPTURE = 4,
+} GstWinRTDeviceClass;
+
+typedef struct
+{
+ /**
+ * GstWinRTDeviceWatcherCallbacks::added:
+ * @watcher: a #GstWinRTDeviceWatcher
+ * @info: (transfer none): a IDeviceInformation interface handle
+ * @user_data: a user_data
+ *
+ * Called when a device is added to the collection enumerated by the DeviceWatcher
+ */
+ void (*added) (GstWinRTDeviceWatcher * watcher,
+ __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformation * info,
+ gpointer user_data);
+
+ /**
+ * GstWinRTDeviceWatcherCallbacks::updated:
+ * @watcher: a #GstWinRTDeviceWatcher
+ * @info_update: (transfer none): a IDeviceInformationUpdate interface handle
+ * @user_data: a user_data
+ *
+ * Called when a device is updated in the collection of enumerated devices
+ */
+ void (*updated) (GstWinRTDeviceWatcher * watcher,
+ __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformationUpdate * info_update,
+ gpointer user_data);
+
+ /**
+ * GstWinRTDeviceWatcherCallbacks::removed:
+ * @watcher: a #GstWinRTDeviceWatcher
+ * @info_update: (transfer none): a IDeviceInformationUpdate interface handle
+ * @user_data: a user_data
+ *
+ * Called when a device is removed from the collection of enumerated devices
+ */
+ void (*removed) (GstWinRTDeviceWatcher * watcher,
+ __x_ABI_CWindows_CDevices_CEnumeration_CIDeviceInformationUpdate * info_update,
+ gpointer user_data);
+
+ /**
+ * GstWinRTDeviceWatcherCallbacks::removed:
+ * @watcher: a #GstWinRTDeviceWatcher
+ * @user_data: a user_data
+ *
+ * Called when the enumeration of devices completes
+ */
+ void (*enumeration_completed) (GstWinRTDeviceWatcher * watcher,
+ gpointer user_data);
+} GstWinRTDeviceWatcherCallbacks;
+
+struct _GstWinRTDeviceWatcher
+{
+ GstObject parent;
+
+ GstWinRTDeviceWatcherPrivate *priv;
+
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _GstWinRTDeviceWatcherClass
+{
+ GstObjectClass parent_class;
+
+ gpointer _gst_reserved[GST_PADDING_LARGE];
+};
+
+GST_WINRT_API
+GType gst_winrt_device_class_get_type (void);
+
+GST_WINRT_API
+GType gst_winrt_device_watcher_get_type (void);
+
+GST_WINRT_API
+GstWinRTDeviceWatcher * gst_winrt_device_watcher_new (GstWinRTDeviceClass device_class,
+ const GstWinRTDeviceWatcherCallbacks * callbacks,
+ gpointer user_data);
+
+GST_WINRT_API
+gboolean gst_winrt_device_watcher_start (GstWinRTDeviceWatcher * watcher);
+
+GST_WINRT_API
+void gst_winrt_device_watcher_stop (GstWinRTDeviceWatcher * watcher);
+
+G_END_DECLS
+
+#endif /* __GST_WINRT_DEVICE_WATCHER_H__ */