• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "gstwinrtdevicewatcher.h"
25 
26 /* workaround for GetCurrentTime collision */
27 #ifdef GetCurrentTime
28 #undef GetCurrentTime
29 #endif
30 
31 #include <windows.foundation.h>
32 #include <wrl.h>
33 #include <wrl/wrappers/corewrappers.h>
34 
35 /* *INDENT-OFF* */
36 typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformation IAddedHandler;
37 typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformationUpdate IUpdatedHandler;
38 typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_Windows__CDevices__CEnumeration__CDeviceInformationUpdate IRemovedHandler;
39 typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_IInspectable IEnumerationCompletedHandler;
40 typedef __FITypedEventHandler_2_Windows__CDevices__CEnumeration__CDeviceWatcher_IInspectable IStoppedHandler;
41 
42 using namespace Microsoft::WRL;
43 using namespace Microsoft::WRL::Wrappers;
44 using namespace ABI::Windows::Foundation;
45 using namespace ABI::Windows::Devices;
46 using namespace ABI::Windows::Devices::Enumeration;
47 
48 GST_DEBUG_CATEGORY_STATIC (gst_winrt_device_watcher_debug);
49 #define GST_CAT_DEFAULT gst_winrt_device_watcher_debug
50 
51 static void
52 gst_winrt_device_watcher_device_added (GstWinRTDeviceWatcher * self,
53     IDeviceInformation * info);
54 static void
55 gst_winrt_device_watcher_device_updated (GstWinRTDeviceWatcher * self,
56     IDeviceInformationUpdate * info_update);
57 static void
58 gst_winrt_device_watcher_device_removed (GstWinRTDeviceWatcher * self,
59     IDeviceInformationUpdate * info_update);
60 static void
61 gst_winrt_device_watcher_device_enumeration_completed (GstWinRTDeviceWatcher *
62     self);
63 static void
64 gst_winrt_device_watcher_device_enumeration_stopped (GstWinRTDeviceWatcher *
65     self);
66 
67 class AddedHandler
68     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IAddedHandler>
69 {
70 public:
AddedHandler()71   AddedHandler () {}
RuntimeClassInitialize(GstWinRTDeviceWatcher * listenr)72   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
73   {
74     if (!listenr)
75       return E_INVALIDARG;
76 
77     listener_ = listenr;
78     return S_OK;
79   }
80 
IFACEMETHOD(Invoke)81   IFACEMETHOD(Invoke)
82   (IDeviceWatcher* sender, IDeviceInformation * arg)
83   {
84     gst_winrt_device_watcher_device_added (listener_, arg);
85 
86     return S_OK;
87   }
88 
89 private:
90   GstWinRTDeviceWatcher * listener_;
91 };
92 
93 class UpdatedHandler
94     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IUpdatedHandler>
95 {
96 public:
UpdatedHandler()97   UpdatedHandler () {}
RuntimeClassInitialize(GstWinRTDeviceWatcher * listenr)98   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
99   {
100     if (!listenr)
101       return E_INVALIDARG;
102 
103     listener_ = listenr;
104     return S_OK;
105   }
106 
IFACEMETHOD(Invoke)107   IFACEMETHOD(Invoke)
108   (IDeviceWatcher* sender, IDeviceInformationUpdate * arg)
109   {
110     gst_winrt_device_watcher_device_updated (listener_, arg);
111 
112     return S_OK;
113   }
114 
115 private:
116   GstWinRTDeviceWatcher * listener_;
117 };
118 
119 class RemovedHandler
120     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IRemovedHandler>
121 {
122 public:
RemovedHandler()123   RemovedHandler () {}
RuntimeClassInitialize(GstWinRTDeviceWatcher * listenr)124   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
125   {
126     if (!listenr)
127       return E_INVALIDARG;
128 
129     listener_ = listenr;
130     return S_OK;
131   }
132 
IFACEMETHOD(Invoke)133   IFACEMETHOD(Invoke)
134   (IDeviceWatcher* sender, IDeviceInformationUpdate * arg)
135   {
136     gst_winrt_device_watcher_device_removed (listener_, arg);
137 
138     return S_OK;
139   }
140 
141 private:
142   GstWinRTDeviceWatcher * listener_;
143 };
144 
145 class EnumerationCompletedHandler
146     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IEnumerationCompletedHandler>
147 {
148 public:
EnumerationCompletedHandler()149   EnumerationCompletedHandler () {}
RuntimeClassInitialize(GstWinRTDeviceWatcher * listenr)150   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
151   {
152     if (!listenr)
153       return E_INVALIDARG;
154 
155     listener_ = listenr;
156     return S_OK;
157   }
158 
IFACEMETHOD(Invoke)159   IFACEMETHOD(Invoke)
160   (IDeviceWatcher* sender, IInspectable * arg)
161   {
162     gst_winrt_device_watcher_device_enumeration_completed (listener_);
163 
164     return S_OK;
165   }
166 
167 private:
168   GstWinRTDeviceWatcher * listener_;
169 };
170 
171 class StoppedHandler
172     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IStoppedHandler>
173 {
174 public:
StoppedHandler()175   StoppedHandler () {}
RuntimeClassInitialize(GstWinRTDeviceWatcher * listenr)176   HRESULT RuntimeClassInitialize (GstWinRTDeviceWatcher * listenr)
177   {
178     if (!listenr)
179       return E_INVALIDARG;
180 
181     listener_ = listenr;
182     return S_OK;
183   }
184 
IFACEMETHOD(Invoke)185   IFACEMETHOD(Invoke)
186   (IDeviceWatcher* sender, IInspectable * arg)
187   {
188     gst_winrt_device_watcher_device_enumeration_stopped (listener_);
189 
190     return S_OK;
191   }
192 
193 private:
194   GstWinRTDeviceWatcher * listener_;
195 };
196 /* *INDENT-ON* */
197 
198 typedef struct
199 {
200   ComPtr < IDeviceWatcher > watcher;
201 
202   EventRegistrationToken added_token;
203   EventRegistrationToken updated_token;
204   EventRegistrationToken removed_token;
205   EventRegistrationToken enum_completed_token;
206   EventRegistrationToken stopped_token;
207 } GstWinRTDeviceWatcherInner;
208 
209 enum
210 {
211   PROP_0,
212   PROP_DEVICE_CLASS,
213 };
214 
215 #define DEFAULT_DEVICE_CLASS GST_WINRT_DEVICE_CLASS_ALL
216 
217 struct _GstWinRTDeviceWatcherPrivate
218 {
219   GMutex lock;
220   GCond cond;
221 
222   GThread *thread;
223   GMainContext *context;
224   GMainLoop *loop;
225 
226   gboolean running;
227 
228   GstWinRTDeviceWatcherCallbacks callbacks;
229   gpointer user_data;
230 
231   GstWinRTDeviceWatcherInner *inner;
232 
233   GstWinRTDeviceClass device_class;
234 };
235 
236 GType
gst_winrt_device_class_get_type(void)237 gst_winrt_device_class_get_type (void)
238 {
239   static gsize device_class_type = 0;
240 
241   if (g_once_init_enter (&device_class_type)) {
242     static const GEnumValue classes[] = {
243       {GST_WINRT_DEVICE_CLASS_ALL, "All", "all"},
244       {GST_WINRT_DEVICE_CLASS_AUDIO_CAPTURE, "AudioCapture", "audio-capture"},
245       {GST_WINRT_DEVICE_CLASS_AUDIO_RENDER, "AudioRender", "audio-render"},
246       {GST_WINRT_DEVICE_CLASS_PORTABLE_STORAGE_DEVICE,
247           "PortableStorageDevice", "portable-storage-device"},
248       {GST_WINRT_DEVICE_CLASS_VIDEO_CAPTURE,
249           "VideoCapture", "video-capture"},
250       {0, nullptr, nullptr},
251     };
252     GType tmp = g_enum_register_static ("GstWinRTDeviceClass", classes);
253     g_once_init_leave (&device_class_type, tmp);
254   }
255 
256   return (GType) device_class_type;
257 }
258 
259 static void gst_winrt_device_watcher_constructed (GObject * object);
260 static void gst_winrt_device_watcher_finalize (GObject * object);
261 static void gst_winrt_device_watcher_set_property (GObject * object,
262     guint prop_id, const GValue * value, GParamSpec * pspec);
263 static void gst_winrt_device_watcher_get_property (GObject * object,
264     guint prop_id, GValue * value, GParamSpec * pspec);
265 
266 static gpointer
267 gst_winrt_device_watcher_thread_func (GstWinRTDeviceWatcher * self);
268 
269 #define gst_winrt_device_watcher_parent_class parent_class
270 G_DEFINE_TYPE_WITH_PRIVATE (GstWinRTDeviceWatcher, gst_winrt_device_watcher,
271     GST_TYPE_OBJECT);
272 
273 static void
gst_winrt_device_watcher_class_init(GstWinRTDeviceWatcherClass * klass)274 gst_winrt_device_watcher_class_init (GstWinRTDeviceWatcherClass * klass)
275 {
276   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
277 
278   gobject_class->constructed = gst_winrt_device_watcher_constructed;
279   gobject_class->finalize = gst_winrt_device_watcher_finalize;
280   gobject_class->set_property = gst_winrt_device_watcher_set_property;
281   gobject_class->get_property = gst_winrt_device_watcher_get_property;
282 
283   g_object_class_install_property (gobject_class, PROP_DEVICE_CLASS,
284       g_param_spec_enum ("device-class", "Device Class",
285           "Device class to watch", GST_TYPE_WINRT_DEVICE_CLASS,
286           DEFAULT_DEVICE_CLASS,
287           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
288               G_PARAM_STATIC_STRINGS)));
289 
290   GST_DEBUG_CATEGORY_INIT (gst_winrt_device_watcher_debug,
291       "winrtdevicewatcher", 0, "winrtdevicewatcher");
292 }
293 
294 static void
gst_winrt_device_watcher_init(GstWinRTDeviceWatcher * self)295 gst_winrt_device_watcher_init (GstWinRTDeviceWatcher * self)
296 {
297   GstWinRTDeviceWatcherPrivate *priv;
298 
299   self->priv = priv = (GstWinRTDeviceWatcherPrivate *)
300       gst_winrt_device_watcher_get_instance_private (self);
301 
302   g_mutex_init (&priv->lock);
303   g_cond_init (&priv->cond);
304   priv->context = g_main_context_new ();
305   priv->loop = g_main_loop_new (priv->context, FALSE);
306 }
307 
308 static void
gst_winrt_device_watcher_constructed(GObject * object)309 gst_winrt_device_watcher_constructed (GObject * object)
310 {
311   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
312   GstWinRTDeviceWatcherPrivate *priv = self->priv;
313 
314   g_mutex_lock (&priv->lock);
315   priv->thread = g_thread_new ("GstWinRTDeviceWatcher",
316       (GThreadFunc) gst_winrt_device_watcher_thread_func, self);
317   while (!g_main_loop_is_running (priv->loop))
318     g_cond_wait (&priv->cond, &priv->lock);
319   g_mutex_unlock (&priv->lock);
320 }
321 
322 static void
gst_winrt_device_watcher_finalize(GObject * object)323 gst_winrt_device_watcher_finalize (GObject * object)
324 {
325   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
326   GstWinRTDeviceWatcherPrivate *priv = self->priv;
327 
328   g_main_loop_quit (priv->loop);
329   if (g_thread_self () != priv->thread) {
330     g_thread_join (priv->thread);
331     g_main_loop_unref (priv->loop);
332     g_main_context_unref (priv->context);
333   } else {
334     g_warning ("Trying join from self-thread");
335   }
336 
337   g_mutex_clear (&priv->lock);
338   g_cond_clear (&priv->cond);
339 
340   G_OBJECT_CLASS (parent_class)->finalize (object);
341 }
342 
343 static void
gst_winrt_device_watcher_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)344 gst_winrt_device_watcher_set_property (GObject * object, guint prop_id,
345     const GValue * value, GParamSpec * pspec)
346 {
347   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
348   GstWinRTDeviceWatcherPrivate *priv = self->priv;
349 
350   switch (prop_id) {
351     case PROP_DEVICE_CLASS:
352       priv->device_class = (GstWinRTDeviceClass) g_value_get_enum (value);
353       break;
354     default:
355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356       break;
357   }
358 }
359 
360 static void
gst_winrt_device_watcher_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)361 gst_winrt_device_watcher_get_property (GObject * object, guint prop_id,
362     GValue * value, GParamSpec * pspec)
363 {
364   GstWinRTDeviceWatcher *self = GST_WINRT_DEVICE_WATCHER (object);
365   GstWinRTDeviceWatcherPrivate *priv = self->priv;
366 
367   switch (prop_id) {
368     case PROP_DEVICE_CLASS:
369       g_value_set_enum (value, priv->device_class);
370       break;
371     default:
372       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
373       break;
374   }
375 }
376 
377 static gboolean
loop_running_cb(GstWinRTDeviceWatcher * self)378 loop_running_cb (GstWinRTDeviceWatcher * self)
379 {
380   GstWinRTDeviceWatcherPrivate *priv = self->priv;
381 
382   g_mutex_lock (&priv->lock);
383   g_cond_signal (&priv->cond);
384   g_mutex_unlock (&priv->lock);
385 
386   return G_SOURCE_REMOVE;
387 }
388 
389 static void
gst_winrt_device_watcher_thread_func_inner(GstWinRTDeviceWatcher * self)390 gst_winrt_device_watcher_thread_func_inner (GstWinRTDeviceWatcher * self)
391 {
392   GstWinRTDeviceWatcherPrivate *priv = self->priv;
393   GSource *idle_source;
394   HRESULT hr;
395   GstWinRTDeviceWatcherInner *inner = nullptr;
396   ComPtr < IDeviceInformationStatics > factory;
397   ComPtr < IDeviceWatcher > watcher;
398   ComPtr < IAddedHandler > added_handler;
399   ComPtr < IUpdatedHandler > updated_handler;
400   ComPtr < IRemovedHandler > removed_handler;
401   ComPtr < IEnumerationCompletedHandler > enum_completed_handler;
402   ComPtr < IStoppedHandler > stopped_handler;
403 
404   g_main_context_push_thread_default (priv->context);
405 
406   idle_source = g_idle_source_new ();
407   g_source_set_callback (idle_source,
408       (GSourceFunc) loop_running_cb, self, nullptr);
409   g_source_attach (idle_source, priv->context);
410   g_source_unref (idle_source);
411 
412   hr = GetActivationFactory (HStringReference
413       (RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get (),
414       &factory);
415   if (FAILED (hr)) {
416     GST_ERROR_OBJECT (self,
417         "Failed to get IDeviceInformationStatics, hr: 0x%x", (guint) hr);
418     goto run_loop;
419   }
420 
421   hr = factory->CreateWatcherDeviceClass ((DeviceClass) priv->device_class,
422       &watcher);
423   if (FAILED (hr)) {
424     GST_ERROR_OBJECT (self,
425         "Failed to create IDeviceWatcher, hr: 0x%x", (guint) hr);
426     goto run_loop;
427   }
428 
429   hr = MakeAndInitialize < AddedHandler > (&added_handler, self);
430   if (FAILED (hr)) {
431     GST_ERROR_OBJECT (self, "Failed to create added handler, hr: 0x%x", hr);
432     goto run_loop;
433   }
434 
435   hr = MakeAndInitialize < UpdatedHandler > (&updated_handler, self);
436   if (FAILED (hr)) {
437     GST_ERROR_OBJECT (self, "Failed to create updated handler, hr: 0x%x", hr);
438     goto run_loop;
439   }
440 
441   hr = MakeAndInitialize < RemovedHandler > (&removed_handler, self);
442   if (FAILED (hr)) {
443     GST_ERROR_OBJECT (self, "Failed to create removed handler, hr: 0x%x", hr);
444     goto run_loop;
445   }
446 
447   hr = MakeAndInitialize < EnumerationCompletedHandler >
448       (&enum_completed_handler, self);
449   if (FAILED (hr)) {
450     GST_ERROR_OBJECT (self,
451         "Failed to create enumeration completed handler, hr: 0x%x", hr);
452     goto run_loop;
453   }
454 
455   hr = MakeAndInitialize < StoppedHandler > (&stopped_handler, self);
456   if (FAILED (hr)) {
457     GST_ERROR_OBJECT (self, "Failed to create stopped handler, hr: 0x%x", hr);
458     goto run_loop;
459   }
460 
461   inner = new GstWinRTDeviceWatcherInner ();
462   hr = watcher->add_Added (added_handler.Get (), &inner->added_token);
463   if (FAILED (hr)) {
464     GST_ERROR_OBJECT (self, "Failed to register added handler, hr: 0x%x", hr);
465     delete inner;
466     inner = nullptr;
467 
468     goto run_loop;
469   }
470 
471   hr = watcher->add_Updated (updated_handler.Get (), &inner->updated_token);
472   if (FAILED (hr)) {
473     GST_ERROR_OBJECT (self, "Failed to register updated handler, hr: 0x%x", hr);
474     delete inner;
475     inner = nullptr;
476 
477     goto run_loop;
478   }
479 
480   hr = watcher->add_Removed (removed_handler.Get (), &inner->removed_token);
481   if (FAILED (hr)) {
482     GST_ERROR_OBJECT (self, "Failed to register removed handler, hr: 0x%x", hr);
483     delete inner;
484     inner = nullptr;
485 
486     goto run_loop;
487   }
488 
489   hr = watcher->add_EnumerationCompleted (enum_completed_handler.Get (),
490       &inner->enum_completed_token);
491   if (FAILED (hr)) {
492     GST_ERROR_OBJECT (self,
493         "Failed to register enumeration completed handler, hr: 0x%x", hr);
494     delete inner;
495     inner = nullptr;
496 
497     goto run_loop;
498   }
499 
500   hr = watcher->add_Stopped (stopped_handler.Get (), &inner->stopped_token);
501   if (FAILED (hr)) {
502     GST_ERROR_OBJECT (self, "Failed to register stopped handler, hr: 0x%x", hr);
503     delete inner;
504     inner = nullptr;
505 
506     goto run_loop;
507   }
508 
509   inner->watcher = watcher;
510   priv->inner = inner;
511 
512 run_loop:
513   GST_INFO_OBJECT (self, "Starting loop");
514   g_main_loop_run (priv->loop);
515   GST_INFO_OBJECT (self, "Stopped loop");
516 
517   if (inner) {
518     if (priv->running)
519       watcher->Stop ();
520 
521     watcher->remove_Added (inner->added_token);
522     watcher->remove_Updated (inner->updated_token);
523     watcher->remove_Removed (inner->removed_token);
524     watcher->remove_EnumerationCompleted (inner->enum_completed_token);
525     watcher->remove_Stopped (inner->stopped_token);
526 
527     delete inner;
528   }
529 
530   g_main_context_pop_thread_default (priv->context);
531 }
532 
533 static gpointer
gst_winrt_device_watcher_thread_func(GstWinRTDeviceWatcher * self)534 gst_winrt_device_watcher_thread_func (GstWinRTDeviceWatcher * self)
535 {
536   RoInitializeWrapper initialize (RO_INIT_MULTITHREADED);
537 
538   /* wrap with another function so that everything can happen
539    * before RoInitializeWrapper is destructed */
540   gst_winrt_device_watcher_thread_func_inner (self);
541 
542   return nullptr;
543 }
544 
545 static void
gst_winrt_device_watcher_device_added(GstWinRTDeviceWatcher * self,IDeviceInformation * info)546 gst_winrt_device_watcher_device_added (GstWinRTDeviceWatcher * self,
547     IDeviceInformation * info)
548 {
549   GstWinRTDeviceWatcherPrivate *priv = self->priv;
550 
551   GST_DEBUG_OBJECT (self, "Device added");
552 
553   if (priv->callbacks.added)
554     priv->callbacks.added (self, info, priv->user_data);
555 }
556 
557 static void
gst_winrt_device_watcher_device_updated(GstWinRTDeviceWatcher * self,IDeviceInformationUpdate * info_update)558 gst_winrt_device_watcher_device_updated (GstWinRTDeviceWatcher * self,
559     IDeviceInformationUpdate * info_update)
560 {
561   GstWinRTDeviceWatcherPrivate *priv = self->priv;
562 
563   GST_DEBUG_OBJECT (self, "Device updated");
564 
565   if (priv->callbacks.updated)
566     priv->callbacks.updated (self, info_update, priv->user_data);
567 }
568 
569 static void
gst_winrt_device_watcher_device_removed(GstWinRTDeviceWatcher * self,IDeviceInformationUpdate * info_update)570 gst_winrt_device_watcher_device_removed (GstWinRTDeviceWatcher * self,
571     IDeviceInformationUpdate * info_update)
572 {
573   GstWinRTDeviceWatcherPrivate *priv = self->priv;
574 
575   GST_DEBUG_OBJECT (self, "Device removed");
576 
577   if (priv->callbacks.removed)
578     priv->callbacks.removed (self, info_update, priv->user_data);
579 }
580 
581 static void
gst_winrt_device_watcher_device_enumeration_completed(GstWinRTDeviceWatcher * self)582 gst_winrt_device_watcher_device_enumeration_completed (GstWinRTDeviceWatcher *
583     self)
584 {
585   GstWinRTDeviceWatcherPrivate *priv = self->priv;
586 
587   GST_DEBUG_OBJECT (self, "Enumeration completed");
588 
589   if (priv->callbacks.enumeration_completed)
590     priv->callbacks.enumeration_completed (self, priv->user_data);
591 }
592 
593 static void
gst_winrt_device_watcher_device_enumeration_stopped(GstWinRTDeviceWatcher * self)594 gst_winrt_device_watcher_device_enumeration_stopped (GstWinRTDeviceWatcher *
595     self)
596 {
597   GST_DEBUG_OBJECT (self, "Stopped");
598 }
599 
600 /**
601  * gst_winrt_device_watcher_new:
602  * @device_class: a #GstWinRTDeviceClass to watch
603  * @callbacks: a pointer to #GstWinRTDeviceWatcherCallbacks
604  * @user_data: a user_data argument for the callbacks
605  *
606  * Constructs a new #GstWinRTDeviceWatcher object for watching device update
607  * of @device_class
608  *
609  * Returns: (transfer full) (nullable): a new #GstWinRTDeviceWatcher
610  * or %NULL when failed to create/setup #GstWinRTDeviceWatcher object
611  *
612  * Since: 1.20
613  */
614 GstWinRTDeviceWatcher *
gst_winrt_device_watcher_new(GstWinRTDeviceClass device_class,const GstWinRTDeviceWatcherCallbacks * callbacks,gpointer user_data)615 gst_winrt_device_watcher_new (GstWinRTDeviceClass device_class,
616     const GstWinRTDeviceWatcherCallbacks * callbacks, gpointer user_data)
617 {
618   GstWinRTDeviceWatcher *self;
619   GstWinRTDeviceWatcherPrivate *priv;
620 
621   g_return_val_if_fail (callbacks != nullptr, nullptr);
622 
623   self = (GstWinRTDeviceWatcher *)
624       g_object_new (GST_TYPE_WINRT_DEVICE_WATCHER, "device-class", device_class,
625       nullptr);
626 
627   priv = self->priv;
628   if (!priv->inner) {
629     gst_object_unref (self);
630     return nullptr;
631   }
632 
633   priv->callbacks = *callbacks;
634   priv->user_data = user_data;
635 
636   gst_object_ref_sink (self);
637 
638   return self;
639 }
640 
641 /**
642  * gst_winrt_device_watcher_start:
643  * @device_class: a #GstWinRTDeviceClass to watch
644  *
645  * Starts watching device update.
646  *
647  * Returns: %TRUE if successful
648  *
649  * Since: 1.20
650  */
651 gboolean
gst_winrt_device_watcher_start(GstWinRTDeviceWatcher * watcher)652 gst_winrt_device_watcher_start (GstWinRTDeviceWatcher * watcher)
653 {
654   GstWinRTDeviceWatcherPrivate *priv;
655   GstWinRTDeviceWatcherInner *inner;
656   HRESULT hr;
657 
658   g_return_val_if_fail (GST_IS_WINRT_DEVICE_WATCHER (watcher), FALSE);
659 
660   priv = watcher->priv;
661   inner = priv->inner;
662 
663   GST_DEBUG_OBJECT (watcher, "Start");
664 
665   g_mutex_lock (&priv->lock);
666   if (priv->running) {
667     GST_DEBUG_OBJECT (watcher, "Already running");
668     g_mutex_unlock (&priv->lock);
669 
670     return TRUE;
671   }
672 
673   hr = inner->watcher->Start ();
674   if (FAILED (hr)) {
675     GST_ERROR_OBJECT (watcher, "Failed to start watcher, hr: 0x%x", (guint) hr);
676     g_mutex_unlock (&priv->lock);
677 
678     return FALSE;
679   }
680 
681   priv->running = TRUE;
682   g_mutex_unlock (&priv->lock);
683 
684   return TRUE;
685 }
686 
687 /**
688  * gst_winrt_device_watcher_stop:
689  * @device_class: a #GstWinRTDeviceClass to watch
690  *
691  * Stops watching device update.
692  *
693  * Since: 1.20
694  */
695 void
gst_winrt_device_watcher_stop(GstWinRTDeviceWatcher * watcher)696 gst_winrt_device_watcher_stop (GstWinRTDeviceWatcher * watcher)
697 {
698   GstWinRTDeviceWatcherPrivate *priv;
699   GstWinRTDeviceWatcherInner *inner;
700   HRESULT hr;
701 
702   g_return_if_fail (GST_IS_WINRT_DEVICE_WATCHER (watcher));
703 
704   GST_DEBUG_OBJECT (watcher, "Stop");
705 
706   priv = watcher->priv;
707   inner = priv->inner;
708 
709   g_mutex_lock (&priv->lock);
710   if (!priv->running) {
711     g_mutex_unlock (&priv->lock);
712 
713     return;
714   }
715 
716   priv->running = FALSE;
717   hr = inner->watcher->Stop ();
718   if (FAILED (hr)) {
719     GST_WARNING_OBJECT (watcher,
720         "Failed to stop watcher, hr: 0x%x", (guint) hr);
721   }
722   g_mutex_unlock (&priv->lock);
723 }
724