libs: Introduce GstWinRT library
authorSeungha Yang <seungha@centricular.com>
Thu, 26 Aug 2021 10:47:51 +0000 (19:47 +0900)
committerSeungha Yang <seungha@centricular.com>
Thu, 30 Sep 2021 06:13:07 +0000 (06:13 +0000)
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>

subprojects/gst-plugins-bad/gst-libs/gst/meson.build
subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrt.h [new file with mode: 0644]
subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrtdevicewatcher.cpp [new file with mode: 0644]
subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrtdevicewatcher.h [new file with mode: 0644]
subprojects/gst-plugins-bad/gst-libs/gst/winrt/meson.build [new file with mode: 0644]
subprojects/gst-plugins-bad/gst-libs/gst/winrt/winrt-prelude.h [new file with mode: 0644]

index a2862a3..77dadcf 100644 (file)
@@ -15,7 +15,8 @@ subdir('play')
 subdir('player')
 subdir('sctp')
 subdir('transcoder')
+subdir('va')
 subdir('vulkan')
 subdir('wayland')
 subdir('webrtc')
-subdir('va')
+subdir('winrt')
diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrt.h b/subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrt.h
new file mode 100644 (file)
index 0000000..944c8c0
--- /dev/null
@@ -0,0 +1,31 @@
+/* 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_H__
+#define __GST_WINRT_H__
+
+#ifndef GST_USE_UNSTABLE_API
+#pragma message ("The winrt library from gst-plugins-bad is unstable API and may change in future.")
+#pragma message ("You can define GST_USE_UNSTABLE_API to avoid this warning.")
+#endif
+
+#include <gst/gst.h>
+#include <gst/winrt/gstwinrtdevicewatcher.h>
+
+#endif /* __GST_WINRT_H__ */
diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrtdevicewatcher.cpp b/subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrtdevicewatcher.cpp
new file mode 100644 (file)
index 0000000..3d6fc10
--- /dev/null
@@ -0,0 +1,723 @@
+/* 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);
+}
diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrtdevicewatcher.h b/subprojects/gst-plugins-bad/gst-libs/gst/winrt/gstwinrtdevicewatcher.h
new file mode 100644 (file)
index 0000000..f71471a
--- /dev/null
@@ -0,0 +1,136 @@
+/* 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__ */
diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/winrt/meson.build b/subprojects/gst-plugins-bad/gst-libs/gst/winrt/meson.build
new file mode 100644 (file)
index 0000000..4a28595
--- /dev/null
@@ -0,0 +1,86 @@
+winrt_sources = [
+  'gstwinrtdevicewatcher.cpp',
+]
+
+gstwinrt_dep = dependency('', required : false)
+
+extra_c_args = [
+  '-DCOBJMACROS',
+]
+extra_comm_args = [
+  '-DGST_USE_UNSTABLE_API',
+  '-DBUILDING_GST_WINRT'
+]
+
+if host_system != 'windows'
+  subdir_done()
+endif
+
+# TODO: Need to bump mingw tool chain
+if cxx.get_id() != 'msvc'
+  subdir_done()
+endif
+
+runtimeobject_lib = cc.find_library('runtimeobject', required : false)
+if not runtimeobject_lib.found()
+  subdir_done()
+endif
+
+winapi_app = cxx.compiles('''#include <winapifamily.h>
+  #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+  #error "not winrt"
+  #endif
+  int main (int argc, char ** argv) {
+    return 0;
+  } ''',
+  dependencies: runtimeobject_lib,
+  name: 'building for WinRT')
+
+if not winapi_app
+  subdir_done()
+endif
+
+win10_sdk = cxx.compiles('''#include <windows.h>
+    #ifndef WDK_NTDDI_VERSION
+    #error "unknown Windows SDK version"
+    #endif
+    #if (WDK_NTDDI_VERSION < 0x0A000000)
+    #error "Not a Windows 10 SDK"
+    #endif
+    ''',
+    name: 'building with Windows 10 SDK')
+
+if not win10_sdk
+  subdir_done()
+endif
+
+building_for_win10 = cxx.compiles('''#include <windows.h>
+    #ifndef WINVER
+    #error "unknown minimum supported OS version"
+    #endif
+    #if (WINVER < 0x0A00)
+    #error "Windows 10 API is not guaranteed"
+    #endif
+    ''',
+    name: 'building for Windows 10')
+
+if not building_for_win10
+  message('Bumping target Windows version to Windows 10 for building gstwinrt library')
+  extra_comm_args += ['-DWINVER=0x0A00', '-D_WIN32_WINNT=0x0A00', '-DNTDDI_VERSION=WDK_NTDDI_VERSION']
+endif
+
+gstwinrt = library('gstwinrt-' + api_version,
+  winrt_sources,
+  c_args : gst_plugins_bad_args + extra_c_args + extra_comm_args,
+  cpp_args : gst_plugins_bad_args + extra_comm_args,
+  include_directories : [configinc, libsinc],
+  version : libversion,
+  soversion : soversion,
+  install : true,
+  dependencies : [gstbase_dep, runtimeobject_lib]
+)
+
+# Still non-public api, should not install headers
+gstwinrt_dep = declare_dependency(link_with : gstwinrt,
+  include_directories : [libsinc],
+  dependencies : [gstbase_dep, runtimeobject_lib])
diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/winrt/winrt-prelude.h b/subprojects/gst-plugins-bad/gst-libs/gst/winrt/winrt-prelude.h
new file mode 100644 (file)
index 0000000..ed2c201
--- /dev/null
@@ -0,0 +1,33 @@
+/* GStreamer
+ * Copyright (C) 2021 GStreamer developers
+ *
+ * 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_PRELUDE_H__
+#define __GST_WINRT_PRELUDE_H__
+
+#include <gst/gst.h>
+
+#ifndef GST_WINRT_API
+# ifdef BUILDING_GST_WINRT
+#  define GST_WINRT_API GST_API_EXPORT         /* from config.h */
+# else
+#  define GST_WINRT_API GST_API_IMPORT
+# endif
+#endif
+
+#endif /* __GST_WIN32_PRELUDE_H__ */