ac4b482efc9515fd8e71978f337127282bdc9de9
[platform/upstream/gstreamer.git] / sys / wasapi2 / gstwasapi2client.cpp
1 /*
2  * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3  * Copyright (C) 2013 Collabora Ltd.
4  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  * Copyright (C) 2018 Centricular Ltd.
6  *   Author: Nirbheek Chauhan <nirbheek@centricular.com>
7  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "AsyncOperations.h"
30 #include "gstwasapi2client.h"
31 #include "gstwasapi2util.h"
32 #include <initguid.h>
33 #include <windows.foundation.h>
34 #include <windows.ui.core.h>
35 #include <wrl.h>
36 #include <wrl/wrappers/corewrappers.h>
37 #include <audioclient.h>
38 #include <mmdeviceapi.h>
39 #include <string.h>
40 #include <string>
41 #include <locale>
42 #include <codecvt>
43
44 using namespace ABI::Windows::ApplicationModel::Core;
45 using namespace ABI::Windows::Foundation;
46 using namespace ABI::Windows::Foundation::Collections;
47 using namespace ABI::Windows::UI::Core;
48 using namespace ABI::Windows::Media::Devices;
49 using namespace ABI::Windows::Devices::Enumeration;
50
51 using namespace Microsoft::WRL;
52 using namespace Microsoft::WRL::Wrappers;
53
54 G_BEGIN_DECLS
55
56 GST_DEBUG_CATEGORY_EXTERN (gst_wasapi2_client_debug);
57 #define GST_CAT_DEFAULT gst_wasapi2_client_debug
58
59 G_END_DECLS
60
61 static void
62 gst_wasapi2_client_on_device_activated (GstWasapi2Client * client,
63     IAudioClient3 * audio_client);
64
65 class GstWasapiDeviceActivator
66     : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase,
67         IActivateAudioInterfaceCompletionHandler>
68 {
69 public:
70   GstWasapiDeviceActivator ()
71   {
72     g_weak_ref_init (&listener_, nullptr);
73   }
74
75   ~GstWasapiDeviceActivator ()
76   {
77     g_weak_ref_set (&listener_, nullptr);
78   }
79
80   HRESULT
81   RuntimeClassInitialize (GstWasapi2Client * listener, gpointer dispatcher)
82   {
83     if (!listener)
84       return E_INVALIDARG;
85
86     g_weak_ref_set (&listener_, listener);
87
88     if (dispatcher) {
89       ComPtr<IInspectable> inspectable =
90         reinterpret_cast<IInspectable*> (dispatcher);
91       HRESULT hr;
92
93       hr = inspectable.As (&dispatcher_);
94       if (gst_wasapi2_result (hr))
95         GST_INFO("Main UI dispatcher is available");
96     }
97
98     return S_OK;
99   }
100
101   STDMETHOD(ActivateCompleted)
102   (IActivateAudioInterfaceAsyncOperation *async_op)
103   {
104     ComPtr<IAudioClient3> audio_client;
105     HRESULT hr = S_OK;
106     HRESULT hr_async_op = S_OK;
107     ComPtr<IUnknown> audio_interface;
108     GstWasapi2Client *client;
109
110     client = (GstWasapi2Client *) g_weak_ref_get (&listener_);
111
112     if (!client) {
113       this->Release ();
114       GST_WARNING ("No listener was configured");
115       return S_OK;
116     }
117
118     GST_INFO_OBJECT (client, "AsyncOperation done");
119
120     hr = async_op->GetActivateResult(&hr_async_op, &audio_interface);
121
122     if (!gst_wasapi2_result (hr)) {
123       GST_WARNING_OBJECT (client, "Failed to get activate result, hr: 0x%x", hr);
124       goto done;
125     }
126
127     if (!gst_wasapi2_result (hr_async_op)) {
128       GST_WARNING_OBJECT (client, "Failed to activate device");
129       goto done;
130     }
131
132     hr = audio_interface.As (&audio_client);
133     if (!gst_wasapi2_result (hr)) {
134       GST_ERROR_OBJECT (client, "Failed to get IAudioClient3 interface");
135       goto done;
136     }
137
138   done:
139     /* Should call this method anyway, listener will wait this event */
140     gst_wasapi2_client_on_device_activated (client, audio_client.Get());
141     gst_object_unref (client);
142     /* return S_OK anyway, but listener can know it's succeeded or not
143      * by passed IAudioClient handle via gst_wasapi2_client_on_device_activated
144      */
145
146     this->Release ();
147
148     return S_OK;
149   }
150
151   HRESULT
152   ActivateDeviceAsync(const std::wstring &device_id)
153   {
154     ComPtr<IAsyncAction> async_action;
155     bool run_async = false;
156     HRESULT hr;
157
158     auto work_item = Callback<Implements<RuntimeClassFlags<ClassicCom>,
159         IDispatchedHandler, FtmBase>>([this, device_id]{
160       ComPtr<IActivateAudioInterfaceAsyncOperation> async_op;
161       HRESULT async_hr = S_OK;
162
163       async_hr = ActivateAudioInterfaceAsync (device_id.c_str (),
164             __uuidof(IAudioClient3), nullptr, this, &async_op);
165
166       /* for debugging */
167       gst_wasapi2_result (async_hr);
168
169       return async_hr;
170     });
171
172     if (dispatcher_) {
173       boolean can_now;
174       hr = dispatcher_->get_HasThreadAccess (&can_now);
175
176       if (!gst_wasapi2_result (hr))
177         return hr;
178
179       if (!can_now)
180         run_async = true;
181     }
182
183     if (run_async && dispatcher_) {
184       hr = dispatcher_->RunAsync (CoreDispatcherPriority_Normal,
185           work_item.Get (), &async_action);
186     } else {
187       hr = work_item->Invoke ();
188     }
189
190     /* We should hold activator object until activation callback has executed,
191      * because OS doesn't hold reference of this callback COM object.
192      * otherwise access violation would happen
193      * See https://docs.microsoft.com/en-us/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-activateaudiointerfaceasync
194      *
195      * This reference count will be decreased by self later on callback,
196      * which will be called from device worker thread.
197      */
198     if (gst_wasapi2_result (hr))
199       this->AddRef ();
200
201     return hr;
202   }
203
204 private:
205   GWeakRef listener_;
206   ComPtr<ICoreDispatcher> dispatcher_;
207 };
208
209 typedef enum
210 {
211   GST_WASAPI2_CLIENT_ACTIVATE_FAILED = -1,
212   GST_WASAPI2_CLIENT_ACTIVATE_INIT = 0,
213   GST_WASAPI2_CLIENT_ACTIVATE_WAIT,
214   GST_WASAPI2_CLIENT_ACTIVATE_DONE,
215 } GstWasapi2ClientActivateState;
216
217 enum
218 {
219   PROP_0,
220   PROP_DEVICE,
221   PROP_DEVICE_NAME,
222   PROP_DEVICE_INDEX,
223   PROP_DEVICE_CLASS,
224   PROP_LOW_LATENCY,
225   PROP_DISPATCHER,
226 };
227
228 #define DEFAULT_DEVICE_INDEX  -1
229 #define DEFAULT_DEVICE_CLASS  GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE
230 #define DEFAULT_LOW_LATENCY   FALSE
231
232 struct _GstWasapi2Client
233 {
234   GstObject parent;
235
236   GstWasapi2ClientDeviceClass device_class;
237   gboolean low_latency;
238   gchar *device_id;
239   gchar *device_name;
240   gint device_index;
241   gpointer dispatcher;
242
243   IAudioClient3 *audio_client;
244   IAudioCaptureClient *audio_capture_client;
245   IAudioRenderClient *audio_render_client;
246   ISimpleAudioVolume *audio_volume;
247   GstWasapiDeviceActivator *activator;
248
249   WAVEFORMATEX *mix_format;
250   GstCaps *supported_caps;
251
252   HANDLE event_handle;
253   HANDLE cancellable;
254   gboolean opened;
255   gboolean running;
256
257   guint32 device_period;
258   guint32 buffer_frame_count;
259
260   GstAudioChannelPosition *positions;
261
262   /* Used for capture mode */
263   GstAdapter *adapter;
264
265   GThread *thread;
266   GMutex lock;
267   GCond cond;
268   GMainContext *context;
269   GMainLoop *loop;
270
271   /* To wait ActivateCompleted event */
272   GMutex init_lock;
273   GCond init_cond;
274   GstWasapi2ClientActivateState activate_state;
275 };
276
277 GType
278 gst_wasapi2_client_device_class_get_type (void)
279 {
280   static volatile GType class_type = 0;
281   static const GEnumValue types[] = {
282     {GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE, "Capture", "capture"},
283     {GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER, "Render", "render"},
284     {0, NULL, NULL}
285   };
286
287   if (g_once_init_enter (&class_type)) {
288     GType gtype = g_enum_register_static ("GstWasapi2ClientDeviceClass", types);
289     g_once_init_leave (&class_type, gtype);
290   }
291
292   return class_type;
293 }
294
295 static void gst_wasapi2_client_constructed (GObject * object);
296 static void gst_wasapi2_client_dispose (GObject * object);
297 static void gst_wasapi2_client_finalize (GObject * object);
298 static void gst_wasapi2_client_get_property (GObject * object, guint prop_id,
299     GValue * value, GParamSpec * pspec);
300 static void gst_wasapi2_client_set_property (GObject * object, guint prop_id,
301     const GValue * value, GParamSpec * pspec);
302
303 static gpointer gst_wasapi2_client_thread_func (GstWasapi2Client * self);
304 static gboolean
305 gst_wasapi2_client_main_loop_running_cb (GstWasapi2Client * self);
306
307 #define gst_wasapi2_client_parent_class parent_class
308 G_DEFINE_TYPE (GstWasapi2Client,
309     gst_wasapi2_client, GST_TYPE_OBJECT);
310
311 static void
312 gst_wasapi2_client_class_init (GstWasapi2ClientClass * klass)
313 {
314   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
315   GParamFlags param_flags =
316       (GParamFlags) (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
317       G_PARAM_STATIC_STRINGS);
318
319   gobject_class->constructed = gst_wasapi2_client_constructed;
320   gobject_class->dispose = gst_wasapi2_client_dispose;
321   gobject_class->finalize = gst_wasapi2_client_finalize;
322   gobject_class->get_property = gst_wasapi2_client_get_property;
323   gobject_class->set_property = gst_wasapi2_client_set_property;
324
325   g_object_class_install_property (gobject_class, PROP_DEVICE,
326       g_param_spec_string ("device", "Device",
327           "WASAPI playback device as a GUID string", NULL, param_flags));
328   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
329       g_param_spec_string ("device-name", "Device Name",
330           "The human-readable device name", NULL, param_flags));
331   g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX,
332       g_param_spec_int ("device-index", "Device Index",
333           "The zero-based device index", -1, G_MAXINT, DEFAULT_DEVICE_INDEX,
334           param_flags));
335   g_object_class_install_property (gobject_class, PROP_DEVICE_CLASS,
336       g_param_spec_enum ("device-class", "Device Class",
337           "Device class", GST_TYPE_WASAPI2_CLIENT_DEVICE_CLASS,
338           DEFAULT_DEVICE_CLASS, param_flags));
339   g_object_class_install_property (gobject_class, PROP_LOW_LATENCY,
340       g_param_spec_boolean ("low-latency", "Low latency",
341           "Optimize all settings for lowest latency. Always safe to enable.",
342           DEFAULT_LOW_LATENCY, param_flags));
343   g_object_class_install_property (gobject_class, PROP_DISPATCHER,
344       g_param_spec_pointer ("dispatcher", "Dispatcher",
345           "ICoreDispatcher COM object to use", param_flags));
346 }
347
348 static void
349 gst_wasapi2_client_init (GstWasapi2Client * self)
350 {
351   self->device_index = DEFAULT_DEVICE_INDEX;
352   self->device_class = DEFAULT_DEVICE_CLASS;
353   self->low_latency = DEFAULT_LOW_LATENCY;
354
355   self->adapter = gst_adapter_new ();
356   self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
357   self->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
358
359   g_mutex_init (&self->lock);
360   g_cond_init (&self->cond);
361
362   g_mutex_init (&self->init_lock);
363   g_cond_init (&self->init_cond);
364   self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_INIT;
365
366   self->context = g_main_context_new ();
367   self->loop = g_main_loop_new (self->context, FALSE);
368 }
369
370 static void
371 gst_wasapi2_client_constructed (GObject * object)
372 {
373   GstWasapi2Client *self = GST_WASAPI2_CLIENT (object);
374   ComPtr<GstWasapiDeviceActivator> activator;
375
376   /* Create a new thread to ensure that COM thread can be MTA thread.
377    * We cannot ensure whether CoInitializeEx() was called outside of here for
378    * this thread or not. If it was called with non-COINIT_MULTITHREADED option,
379    * we cannot update it */
380   g_mutex_lock (&self->lock);
381   self->thread = g_thread_new ("GstWasapi2ClientWinRT",
382       (GThreadFunc) gst_wasapi2_client_thread_func, self);
383   while (!self->loop || !g_main_loop_is_running (self->loop))
384     g_cond_wait (&self->cond, &self->lock);
385   g_mutex_unlock (&self->lock);
386
387   G_OBJECT_CLASS (parent_class)->constructed (object);
388 }
389
390 static void
391 gst_wasapi2_client_dispose (GObject * object)
392 {
393   GstWasapi2Client *self = GST_WASAPI2_CLIENT (object);
394
395   GST_DEBUG_OBJECT (self, "dispose");
396
397   gst_clear_caps (&self->supported_caps);
398
399   if (self->loop) {
400     g_main_loop_quit (self->loop);
401     g_thread_join (self->thread);
402     g_main_context_unref (self->context);
403     g_main_loop_unref (self->loop);
404
405     self->thread = NULL;
406     self->context = NULL;
407     self->loop = NULL;
408   }
409
410   g_clear_object (&self->adapter);
411
412   G_OBJECT_CLASS (parent_class)->dispose (object);
413 }
414
415 static void
416 gst_wasapi2_client_finalize (GObject * object)
417 {
418   GstWasapi2Client *self = GST_WASAPI2_CLIENT (object);
419
420   g_free (self->device_id);
421   g_free (self->device_name);
422
423   g_free (self->positions);
424
425   CoTaskMemFree (self->mix_format);
426   CloseHandle (self->event_handle);
427   CloseHandle (self->cancellable);
428
429   g_mutex_clear (&self->lock);
430   g_cond_clear (&self->cond);
431
432   g_mutex_clear (&self->init_lock);
433   g_cond_clear (&self->init_cond);
434
435   G_OBJECT_CLASS (parent_class)->finalize (object);
436 }
437
438 static void
439 gst_wasapi2_client_get_property (GObject * object, guint prop_id,
440     GValue * value, GParamSpec * pspec)
441 {
442   GstWasapi2Client *self = GST_WASAPI2_CLIENT (object);
443
444   switch (prop_id) {
445     case PROP_DEVICE:
446       g_value_set_string (value, self->device_id);
447       break;
448     case PROP_DEVICE_NAME:
449       g_value_set_string (value, self->device_name);
450       break;
451     case PROP_DEVICE_INDEX:
452       g_value_set_int (value, self->device_index);
453       break;
454     case PROP_DEVICE_CLASS:
455       g_value_set_enum (value, self->device_class);
456       break;
457     case PROP_LOW_LATENCY:
458       g_value_set_boolean (value, self->low_latency);
459       break;
460     case PROP_DISPATCHER:
461       g_value_set_pointer (value, self->dispatcher);
462       break;
463     default:
464       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
465       break;
466   }
467 }
468
469 static void
470 gst_wasapi2_client_set_property (GObject * object, guint prop_id,
471     const GValue * value, GParamSpec * pspec)
472 {
473   GstWasapi2Client *self = GST_WASAPI2_CLIENT (object);
474
475   switch (prop_id) {
476     case PROP_DEVICE:
477       g_free (self->device_id);
478       self->device_id = g_value_dup_string (value);
479       break;
480     case PROP_DEVICE_NAME:
481       g_free (self->device_name);
482       self->device_name = g_value_dup_string (value);
483       break;
484     case PROP_DEVICE_INDEX:
485       self->device_index = g_value_get_int (value);
486       break;
487     case PROP_DEVICE_CLASS:
488       self->device_class =
489           (GstWasapi2ClientDeviceClass) g_value_get_enum (value);
490       break;
491     case PROP_LOW_LATENCY:
492       self->low_latency = g_value_get_boolean (value);
493       break;
494     case PROP_DISPATCHER:
495       self->dispatcher = g_value_get_pointer (value);
496       break;
497     default:
498       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
499       break;
500   }
501 }
502
503 static gboolean
504 gst_wasapi2_client_main_loop_running_cb (GstWasapi2Client * self)
505 {
506   GST_DEBUG_OBJECT (self, "Main loop running now");
507
508   g_mutex_lock (&self->lock);
509   g_cond_signal (&self->cond);
510   g_mutex_unlock (&self->lock);
511
512   return G_SOURCE_REMOVE;
513 }
514
515 static void
516 gst_wasapi2_client_on_device_activated (GstWasapi2Client * self,
517     IAudioClient3 * audio_client)
518 {
519   GST_INFO_OBJECT (self, "Device activated");
520
521   g_mutex_lock (&self->init_lock);
522   if (audio_client) {
523     audio_client->AddRef();
524     self->audio_client = audio_client;
525     self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_DONE;
526   } else {
527     GST_WARNING_OBJECT (self, "IAudioClient is unavailable");
528     self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_FAILED;
529   }
530   g_cond_broadcast (&self->init_cond);
531   g_mutex_unlock (&self->init_lock);
532 }
533
534 static std::string
535 convert_wstring_to_string (const std::wstring &wstr)
536 {
537   std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
538
539   return converter.to_bytes (wstr.c_str());
540 }
541
542 static std::string
543 convert_hstring_to_string (HString * hstr)
544 {
545   const wchar_t *raw_hstr;
546
547   if (!hstr)
548     return std::string();
549
550   raw_hstr = hstr->GetRawBuffer (nullptr);
551   if (!raw_hstr)
552     return std::string();
553
554   return convert_wstring_to_string (std::wstring (raw_hstr));
555 }
556
557 static std::wstring
558 gst_wasapi2_client_get_default_device_id (GstWasapi2Client * self)
559 {
560   HRESULT hr;
561   PWSTR default_device_id_wstr = nullptr;
562
563   if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE)
564     hr = StringFromIID (DEVINTERFACE_AUDIO_CAPTURE, &default_device_id_wstr);
565   else
566     hr = StringFromIID (DEVINTERFACE_AUDIO_RENDER, &default_device_id_wstr);
567
568   if (!gst_wasapi2_result (hr))
569     return std::wstring();
570
571   std::wstring ret = std::wstring (default_device_id_wstr);
572   CoTaskMemFree (default_device_id_wstr);
573
574   return ret;
575 }
576
577 static gboolean
578 gst_wasapi2_client_activate_async (GstWasapi2Client * self,
579     GstWasapiDeviceActivator * activator)
580 {
581   HRESULT hr;
582   ComPtr<IDeviceInformationStatics> device_info_static;
583   ComPtr<IAsyncOperation<DeviceInformationCollection*>> async_op;
584   ComPtr<IVectorView<DeviceInformation*>> device_list;
585   HStringReference hstr_device_info =
586       HStringReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation);
587   DeviceClass device_class;
588   unsigned int count = 0;
589   gint device_index = 0;
590   std::wstring default_device_id_wstring;
591   std::string default_device_id;
592   std::wstring target_device_id_wstring;
593   std::string target_device_id;
594   std::string target_device_name;
595   gboolean use_default_device = FALSE;
596
597   GST_INFO_OBJECT (self,
598       "requested device info, device-class: %s, device: %s, device-index: %d",
599        self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE ? "capture" :
600        "render", GST_STR_NULL (self->device_id), self->device_index);
601
602   if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE) {
603     device_class = DeviceClass::DeviceClass_AudioCapture;
604   } else {
605     device_class = DeviceClass::DeviceClass_AudioRender;
606   }
607
608   default_device_id_wstring = gst_wasapi2_client_get_default_device_id (self);
609   if (default_device_id_wstring.empty ()) {
610     GST_WARNING_OBJECT (self, "Couldn't get default device id");
611     goto failed;
612   }
613
614   default_device_id = convert_wstring_to_string (default_device_id_wstring);
615   GST_DEBUG_OBJECT (self, "Default device id: %s", default_device_id.c_str ());
616
617   /* When
618    * 1) default device was requested or
619    * 2) no explicitly requested device or
620    * 3) requested device string id is null but device index is zero
621    * will use default device
622    *
623    * Note that default device is much preferred
624    * See https://docs.microsoft.com/en-us/windows/win32/coreaudio/automatic-stream-routing
625    */
626   if (self->device_id &&
627       g_ascii_strcasecmp (self->device_id, default_device_id.c_str()) == 0) {
628     GST_DEBUG_OBJECT (self, "Default device was requested");
629     use_default_device = TRUE;
630   } else if (self->device_index < 0 && !self->device_id) {
631     GST_DEBUG_OBJECT (self,
632         "No device was explicitly requested, use default device");
633     use_default_device = TRUE;
634   } else if (!self->device_id && self->device_index == 0) {
635     GST_DEBUG_OBJECT (self, "device-index == zero means default device");
636     use_default_device = TRUE;
637   }
638
639   if (use_default_device) {
640     target_device_id_wstring = default_device_id_wstring;
641     target_device_id = default_device_id;
642     if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE)
643       target_device_name = "Default Audio Capture Device";
644     else
645       target_device_name = "Default Audio Render Device";
646     goto activate;
647   }
648
649   hr = GetActivationFactory (hstr_device_info.Get(), &device_info_static);
650   if (!gst_wasapi2_result (hr))
651     goto failed;
652
653   hr = device_info_static->FindAllAsyncDeviceClass (device_class, &async_op);
654   device_info_static.Reset ();
655   if (!gst_wasapi2_result (hr))
656     goto failed;
657
658   hr = SyncWait<DeviceInformationCollection*>(async_op.Get ());
659   if (!gst_wasapi2_result (hr))
660     goto failed;
661
662   hr = async_op->GetResults (&device_list);
663   async_op.Reset ();
664   if (!gst_wasapi2_result (hr))
665     goto failed;
666
667   hr = device_list->get_Size (&count);
668   if (!gst_wasapi2_result (hr))
669     goto failed;
670
671   if (count == 0) {
672     GST_WARNING_OBJECT (self, "No available device");
673     goto failed;
674   }
675
676   /* device_index 0 will be assigned for default device
677    * so the number of available device is count + 1 (for default device) */
678   if (self->device_index >= 0 && self->device_index > (gint) count) {
679     GST_WARNING_OBJECT (self, "Device index %d is unavailable",
680         self->device_index);
681     goto failed;
682   }
683
684   GST_DEBUG_OBJECT (self, "Available device count: %d", count);
685
686   /* zero is for default device */
687   device_index = 1;
688   for (unsigned int i = 0; i < count; i++) {
689     ComPtr<IDeviceInformation> device_info;
690     HString id;
691     HString name;
692     boolean b_value;
693     std::string cur_device_id;
694     std::string cur_device_name;
695
696     hr = device_list->GetAt (i, &device_info);
697     if (!gst_wasapi2_result (hr))
698       continue;
699
700     hr = device_info->get_IsEnabled (&b_value);
701     if (!gst_wasapi2_result (hr))
702       continue;
703
704     /* select only enabled device */
705     if (!b_value) {
706       GST_DEBUG_OBJECT (self, "Device index %d is disabled", i);
707       continue;
708     }
709
710     /* To ensure device id and device name are available,
711      * will query this later again once target device is determined */
712     hr = device_info->get_Id (id.GetAddressOf());
713     if (!gst_wasapi2_result (hr))
714       continue;
715
716     if (!id.IsValid()) {
717       GST_WARNING_OBJECT (self, "Device index %d has invalid id", i);
718       continue;
719     }
720
721     hr = device_info->get_Name (name.GetAddressOf());
722     if (!gst_wasapi2_result (hr))
723       continue;
724
725     if (!name.IsValid ()) {
726       GST_WARNING_OBJECT (self, "Device index %d has invalid name", i);
727       continue;
728     }
729
730     cur_device_id = convert_hstring_to_string (&id);
731     if (cur_device_id.empty ()) {
732       GST_WARNING_OBJECT (self, "Device index %d has empty id", i);
733       continue;
734     }
735
736     cur_device_name = convert_hstring_to_string (&name);
737     if (cur_device_name.empty ()) {
738       GST_WARNING_OBJECT (self, "Device index %d has empty device name", i);
739       continue;
740     }
741
742     GST_DEBUG_OBJECT (self, "device [%d] id: %s, name: %s",
743         device_index, cur_device_id.c_str(), cur_device_name.c_str());
744
745     if (self->device_id &&
746         g_ascii_strcasecmp (self->device_id, cur_device_id.c_str ()) == 0) {
747       GST_INFO_OBJECT (self,
748           "Device index %d has matching device id %s", device_index,
749           cur_device_id.c_str ());
750       target_device_id_wstring = id.GetRawBuffer (nullptr);
751       target_device_id = cur_device_id;
752       target_device_name = cur_device_name;
753       break;
754     }
755
756     if (self->device_index >= 0 && self->device_index == device_index) {
757       GST_INFO_OBJECT (self, "Select device index %d, device id %s",
758           device_index, cur_device_id.c_str ());
759       target_device_id_wstring = id.GetRawBuffer (nullptr);
760       target_device_id = cur_device_id;
761       target_device_name = cur_device_name;
762       break;
763     }
764
765     /* count only available devices */
766     device_index++;
767   }
768
769   if (target_device_id_wstring.empty ()) {
770     GST_WARNING_OBJECT (self, "Couldn't find target device");
771     goto failed;
772   }
773
774 activate:
775   /* fill device id and name */
776   g_free (self->device_id);
777   self->device_id = g_strdup (target_device_id.c_str());
778
779   g_free (self->device_name);
780   self->device_name = g_strdup (target_device_name.c_str ());
781
782   self->device_index = device_index;
783
784   hr = activator->ActivateDeviceAsync (target_device_id_wstring);
785   if (!gst_wasapi2_result (hr)) {
786     GST_WARNING_OBJECT (self, "Failed to activate device");
787     goto failed;
788   }
789
790   g_mutex_lock (&self->lock);
791   if (self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_INIT)
792     self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_WAIT;
793   g_mutex_unlock (&self->lock);
794
795   return TRUE;
796
797 failed:
798   self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_FAILED;
799
800   return FALSE;
801 }
802
803 static const gchar *
804 activate_state_to_string (GstWasapi2ClientActivateState state)
805 {
806   switch (state) {
807     case GST_WASAPI2_CLIENT_ACTIVATE_FAILED:
808       return "FAILED";
809     case GST_WASAPI2_CLIENT_ACTIVATE_INIT:
810       return "INIT";
811     case GST_WASAPI2_CLIENT_ACTIVATE_WAIT:
812       return "WAIT";
813     case GST_WASAPI2_CLIENT_ACTIVATE_DONE:
814       return "DONE";
815   }
816
817   g_assert_not_reached ();
818
819   return "Undefined";
820 }
821
822 static gpointer
823 gst_wasapi2_client_thread_func (GstWasapi2Client * self)
824 {
825   RoInitializeWrapper initialize (RO_INIT_MULTITHREADED);
826   GSource *source;
827   HRESULT hr;
828   ComPtr<GstWasapiDeviceActivator> activator;
829
830   hr = MakeAndInitialize<GstWasapiDeviceActivator> (&activator,
831       self, self->dispatcher);
832   if (!gst_wasapi2_result (hr)) {
833     GST_ERROR_OBJECT (self, "Could not create activator object");
834     self->activate_state = GST_WASAPI2_CLIENT_ACTIVATE_FAILED;
835     goto run_loop;
836   }
837
838   gst_wasapi2_client_activate_async (self, activator.Get ());
839
840   if (!self->dispatcher) {
841     /* In case that dispatcher is unavailable, wait activation synchroniously */
842     GST_DEBUG_OBJECT (self, "Wait device activation");
843     gst_wasapi2_client_ensure_activation (self);
844     GST_DEBUG_OBJECT (self, "Device activation result %s",
845         activate_state_to_string (self->activate_state));
846   }
847
848 run_loop:
849   g_main_context_push_thread_default (self->context);
850
851   source = g_idle_source_new ();
852   g_source_set_callback (source,
853       (GSourceFunc) gst_wasapi2_client_main_loop_running_cb, self, NULL);
854   g_source_attach (source, self->context);
855   g_source_unref (source);
856
857   GST_DEBUG_OBJECT (self, "Starting main loop");
858   g_main_loop_run (self->loop);
859   GST_DEBUG_OBJECT (self, "Stopped main loop");
860
861   g_main_context_pop_thread_default (self->context);
862
863   gst_wasapi2_client_stop (self);
864
865   if (self->audio_volume) {
866     /* this mute state seems to be global setting for this device
867      * Explicitly disable mute for later use of this audio device
868      * by other application. Otherwise users would blame GStreamer
869      * if we close audio device with muted state */
870     self->audio_volume->SetMute(FALSE, nullptr);
871     self->audio_volume->Release ();
872     self->audio_volume = NULL;
873   }
874
875   if (self->audio_render_client) {
876     self->audio_render_client->Release ();
877     self->audio_render_client = NULL;
878   }
879
880   if (self->audio_capture_client) {
881     self->audio_capture_client->Release ();
882     self->audio_capture_client = NULL;
883   }
884
885   if (self->audio_client) {
886     self->audio_client->Release ();
887     self->audio_client = NULL;
888   }
889
890   /* Reset explicitly to ensure that it happens before
891    * RoInitializeWrapper dtor is called */
892   activator.Reset ();
893
894   GST_DEBUG_OBJECT (self, "Exit thread function");
895
896   return NULL;
897 }
898
899 static const gchar *
900 gst_waveformatex_to_audio_format (WAVEFORMATEXTENSIBLE * format)
901 {
902   const gchar *fmt_str = NULL;
903   GstAudioFormat fmt = GST_AUDIO_FORMAT_UNKNOWN;
904
905   if (format->Format.wFormatTag == WAVE_FORMAT_PCM) {
906     fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN,
907         format->Format.wBitsPerSample, format->Format.wBitsPerSample);
908   } else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
909     if (format->Format.wBitsPerSample == 32)
910       fmt = GST_AUDIO_FORMAT_F32LE;
911     else if (format->Format.wBitsPerSample == 64)
912       fmt = GST_AUDIO_FORMAT_F64LE;
913   } else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
914     if (IsEqualGUID (format->SubFormat, KSDATAFORMAT_SUBTYPE_PCM)) {
915       fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN,
916           format->Format.wBitsPerSample, format->Samples.wValidBitsPerSample);
917     } else if (IsEqualGUID (format->SubFormat,
918             KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
919       if (format->Format.wBitsPerSample == 32
920           && format->Samples.wValidBitsPerSample == 32)
921         fmt = GST_AUDIO_FORMAT_F32LE;
922       else if (format->Format.wBitsPerSample == 64 &&
923           format->Samples.wValidBitsPerSample == 64)
924         fmt = GST_AUDIO_FORMAT_F64LE;
925     }
926   }
927
928   if (fmt != GST_AUDIO_FORMAT_UNKNOWN)
929     fmt_str = gst_audio_format_to_string (fmt);
930
931   return fmt_str;
932 }
933
934 static void
935 gst_wasapi_util_channel_position_all_none (guint channels,
936     GstAudioChannelPosition * position)
937 {
938   int ii;
939   for (ii = 0; ii < channels; ii++)
940     position[ii] = GST_AUDIO_CHANNEL_POSITION_NONE;
941 }
942
943 static struct
944 {
945   guint64 wasapi_pos;
946   GstAudioChannelPosition gst_pos;
947 } wasapi_to_gst_pos[] = {
948   {SPEAKER_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT},
949   {SPEAKER_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
950   {SPEAKER_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER},
951   {SPEAKER_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1},
952   {SPEAKER_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT},
953   {SPEAKER_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
954   {SPEAKER_FRONT_LEFT_OF_CENTER,
955       GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER},
956   {SPEAKER_FRONT_RIGHT_OF_CENTER,
957       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER},
958   {SPEAKER_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
959   /* Enum values diverge from this point onwards */
960   {SPEAKER_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT},
961   {SPEAKER_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT},
962   {SPEAKER_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER},
963   {SPEAKER_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT},
964   {SPEAKER_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER},
965   {SPEAKER_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT},
966   {SPEAKER_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT},
967   {SPEAKER_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER},
968   {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
969 };
970
971 /* Parse WAVEFORMATEX to get the gstreamer channel mask, and the wasapi channel
972  * positions so GstAudioRingbuffer can reorder the audio data to match the
973  * gstreamer channel order. */
974 static guint64
975 gst_wasapi_util_waveformatex_to_channel_mask (WAVEFORMATEXTENSIBLE * format,
976     GstAudioChannelPosition ** out_position)
977 {
978   int ii, ch;
979   guint64 mask = 0;
980   WORD nChannels = format->Format.nChannels;
981   DWORD dwChannelMask = format->dwChannelMask;
982   GstAudioChannelPosition *pos = NULL;
983
984   pos = g_new (GstAudioChannelPosition, nChannels);
985   gst_wasapi_util_channel_position_all_none (nChannels, pos);
986
987   /* Too many channels, have to assume that they are all non-positional */
988   if (nChannels > G_N_ELEMENTS (wasapi_to_gst_pos)) {
989     GST_INFO ("Got too many (%i) channels, assuming non-positional", nChannels);
990     goto out;
991   }
992
993   /* Too many bits in the channel mask, and the bits don't match nChannels */
994   if (dwChannelMask >> (G_N_ELEMENTS (wasapi_to_gst_pos) + 1) != 0) {
995     GST_WARNING ("Too many bits in channel mask (%lu), assuming "
996         "non-positional", dwChannelMask);
997     goto out;
998   }
999
1000   /* Map WASAPI's channel mask to Gstreamer's channel mask and positions.
1001    * If the no. of bits in the mask > nChannels, we will ignore the extra. */
1002   for (ii = 0, ch = 0; ii < G_N_ELEMENTS (wasapi_to_gst_pos) && ch < nChannels;
1003       ii++) {
1004     if (!(dwChannelMask & wasapi_to_gst_pos[ii].wasapi_pos))
1005       /* no match, try next */
1006       continue;
1007     mask |= G_GUINT64_CONSTANT (1) << wasapi_to_gst_pos[ii].gst_pos;
1008     pos[ch++] = wasapi_to_gst_pos[ii].gst_pos;
1009   }
1010
1011   /* XXX: Warn if some channel masks couldn't be mapped? */
1012
1013   GST_DEBUG ("Converted WASAPI mask 0x%" G_GINT64_MODIFIER "x -> 0x%"
1014       G_GINT64_MODIFIER "x", (guint64) dwChannelMask, (guint64) mask);
1015
1016 out:
1017   if (out_position)
1018     *out_position = pos;
1019   return mask;
1020 }
1021
1022 static gboolean
1023 gst_wasapi2_util_parse_waveformatex (WAVEFORMATEXTENSIBLE * format,
1024     GstCaps * template_caps, GstCaps ** out_caps,
1025     GstAudioChannelPosition ** out_positions)
1026 {
1027   int ii;
1028   const gchar *afmt;
1029   guint64 channel_mask;
1030
1031   *out_caps = NULL;
1032
1033   /* TODO: handle SPDIF and other encoded formats */
1034
1035   /* 1 or 2 channels <= 16 bits sample size OR
1036    * 1 or 2 channels > 16 bits sample size or >2 channels */
1037   if (format->Format.wFormatTag != WAVE_FORMAT_PCM &&
1038       format->Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT &&
1039       format->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
1040     /* Unhandled format tag */
1041     return FALSE;
1042
1043   /* WASAPI can only tell us one canonical mix format that it will accept. The
1044    * alternative is calling IsFormatSupported on all combinations of formats.
1045    * Instead, it's simpler and faster to require conversion inside gstreamer */
1046   afmt = gst_waveformatex_to_audio_format (format);
1047   if (afmt == NULL)
1048     return FALSE;
1049
1050   *out_caps = gst_caps_copy (template_caps);
1051
1052   /* This will always return something that might be usable */
1053   channel_mask =
1054       gst_wasapi_util_waveformatex_to_channel_mask (format, out_positions);
1055
1056   for (ii = 0; ii < gst_caps_get_size (*out_caps); ii++) {
1057     GstStructure *s = gst_caps_get_structure (*out_caps, ii);
1058
1059     gst_structure_set (s,
1060         "format", G_TYPE_STRING, afmt,
1061         "channels", G_TYPE_INT, format->Format.nChannels,
1062         "rate", G_TYPE_INT, format->Format.nSamplesPerSec, NULL);
1063
1064     if (channel_mask) {
1065       gst_structure_set (s,
1066           "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
1067     }
1068   }
1069
1070   return TRUE;
1071 }
1072
1073 GstCaps *
1074 gst_wasapi2_client_get_caps (GstWasapi2Client * client)
1075 {
1076   WAVEFORMATEX *format = NULL;
1077   static GstStaticCaps static_caps = GST_STATIC_CAPS (GST_WASAPI2_STATIC_CAPS);
1078   GstCaps *scaps;
1079   HRESULT hr;
1080
1081   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), NULL);
1082
1083   if (client->supported_caps)
1084     return gst_caps_ref (client->supported_caps);
1085
1086   if (!client->audio_client) {
1087     GST_WARNING_OBJECT (client, "IAudioClient3 wasn't configured");
1088     return NULL;
1089   }
1090
1091   CoTaskMemFree (client->mix_format);
1092   client->mix_format = nullptr;
1093
1094   g_clear_pointer (&client->positions, g_free);
1095
1096   hr = client->audio_client->GetMixFormat (&format);
1097   if (!gst_wasapi2_result (hr))
1098     return NULL;
1099
1100   scaps = gst_static_caps_get (&static_caps);
1101   gst_wasapi2_util_parse_waveformatex ((WAVEFORMATEXTENSIBLE *) format,
1102       scaps, &client->supported_caps, &client->positions);
1103   gst_caps_unref (scaps);
1104
1105   client->mix_format = format;
1106
1107   if (!client->supported_caps) {
1108     GST_ERROR_OBJECT (client, "No caps from subclass");
1109     return NULL;
1110   }
1111
1112   return gst_caps_ref (client->supported_caps);
1113 }
1114
1115 static gboolean
1116 gst_wasapi2_client_initialize_audio_client3 (GstWasapi2Client * self)
1117 {
1118   HRESULT hr;
1119   UINT32 default_period, fundamental_period, min_period, max_period;
1120   DWORD stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
1121   WAVEFORMATEX *format = NULL;
1122   UINT32 period;
1123   gboolean ret = FALSE;
1124   IAudioClient3 *audio_client = self->audio_client;
1125
1126   hr = audio_client->GetSharedModeEnginePeriod (self->mix_format,
1127       &default_period, &fundamental_period, &min_period, &max_period);
1128   if (!gst_wasapi2_result (hr))
1129     goto done;
1130
1131   GST_INFO_OBJECT (self, "Using IAudioClient3, default period %d frames, "
1132       "fundamental period %d frames, minimum period %d frames, maximum period "
1133       "%d frames", default_period, fundamental_period, min_period, max_period);
1134
1135   hr = audio_client->InitializeSharedAudioStream (stream_flags, min_period,
1136       self->mix_format, nullptr);
1137
1138   if (!gst_wasapi2_result (hr)) {
1139     GST_WARNING_OBJECT (self, "Failed to initialize IAudioClient3");
1140     goto done;
1141   }
1142
1143   /* query period again to be ensured */
1144   hr = audio_client->GetCurrentSharedModeEnginePeriod (&format, &period);
1145   if (!gst_wasapi2_result (hr)) {
1146     GST_WARNING_OBJECT (self, "Failed to get current period");
1147     goto done;
1148   }
1149
1150   self->device_period = period;
1151   ret = TRUE;
1152
1153 done:
1154   CoTaskMemFree (format);
1155
1156   return ret;
1157 }
1158
1159 static void
1160 gst_wasapi2_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec,
1161     REFERENCE_TIME default_period, REFERENCE_TIME min_period,
1162     REFERENCE_TIME * ret_period, REFERENCE_TIME * ret_buffer_duration)
1163 {
1164   REFERENCE_TIME use_period, use_buffer;
1165
1166   /* Shared mode always runs at the default period, so if we want a larger
1167    * period (for lower CPU usage), we do it as a multiple of that */
1168   use_period = default_period;
1169
1170   /* Ensure that the period (latency_time) used is an integral multiple of
1171    * either the default period or the minimum period */
1172   use_period = use_period * MAX ((spec->latency_time * 10) / use_period, 1);
1173
1174   /* Ask WASAPI to create a software ringbuffer of at least this size; it may
1175    * be larger so the actual buffer time may be different, which is why after
1176    * initialization we read the buffer duration actually in-use and set
1177    * segsize/segtotal from that. */
1178   use_buffer = spec->buffer_time * 10;
1179   /* Has to be at least twice the period */
1180   if (use_buffer < 2 * use_period)
1181     use_buffer = 2 * use_period;
1182
1183   *ret_period = use_period;
1184   *ret_buffer_duration = use_buffer;
1185 }
1186
1187 static gboolean
1188 gst_wasapi2_client_initialize_audio_client (GstWasapi2Client * self,
1189     GstAudioRingBufferSpec * spec)
1190 {
1191   REFERENCE_TIME default_period, min_period;
1192   REFERENCE_TIME device_period, device_buffer_duration;
1193   guint rate;
1194   DWORD stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
1195   HRESULT hr;
1196   IAudioClient3 *audio_client = self->audio_client;
1197
1198   hr = audio_client->GetDevicePeriod (&default_period, &min_period);
1199   if (!gst_wasapi2_result (hr)) {
1200     GST_WARNING_OBJECT (self, "Couldn't get device period info");
1201     return FALSE;
1202   }
1203
1204   GST_INFO_OBJECT (self, "wasapi2 default period: %" G_GINT64_FORMAT
1205       ", min period: %" G_GINT64_FORMAT, default_period, min_period);
1206
1207   rate = GST_AUDIO_INFO_RATE (&spec->info);
1208
1209   if (self->low_latency) {
1210     device_period = default_period;
1211     /* this should be same as hnsPeriodicity
1212      * when AUDCLNT_STREAMFLAGS_EVENTCALLBACK is used
1213      * And in case of shared mode, hnsPeriodicity should be zero, so
1214      * this value should be zero as well */
1215     device_buffer_duration = 0;
1216   } else {
1217     /* Clamp values to integral multiples of an appropriate period */
1218     gst_wasapi2_util_get_best_buffer_sizes (spec,
1219         default_period, min_period, &device_period, &device_buffer_duration);
1220   }
1221
1222   hr = audio_client->Initialize (AUDCLNT_SHAREMODE_SHARED, stream_flags,
1223       device_buffer_duration,
1224       /* This must always be 0 in shared mode */
1225       0,
1226       self->mix_format, nullptr);
1227   if (!gst_wasapi2_result (hr)) {
1228     GST_WARNING_OBJECT (self, "Couldn't initialize audioclient");
1229     return FALSE;
1230   }
1231
1232   /* device_period can be a non-power-of-10 value so round while converting */
1233   self->device_period =
1234       gst_util_uint64_scale_round (device_period, rate * 100, GST_SECOND);
1235
1236   return TRUE;
1237 }
1238
1239 gboolean
1240 gst_wasapi2_client_open (GstWasapi2Client * client, GstAudioRingBufferSpec * spec,
1241     GstAudioRingBuffer * buf)
1242 {
1243   HRESULT hr;
1244   REFERENCE_TIME latency_rt;
1245   guint bpf, rate;
1246   IAudioClient3 *audio_client;
1247   ComPtr<ISimpleAudioVolume> audio_volume;
1248   gboolean initialized = FALSE;
1249
1250   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1251
1252   /* FIXME: Once IAudioClient3 was initialized, we may need to re-open
1253    * IAudioClient3 in order to handle audio format change */
1254   if (client->opened) {
1255     GST_INFO_OBJECT (client, "IAudioClient3 object is initialized already");
1256     return TRUE;
1257   }
1258
1259   audio_client = client->audio_client;
1260
1261   if (!audio_client) {
1262     GST_ERROR_OBJECT (client, "IAudioClient3 object wasn't configured");
1263     return FALSE;
1264   }
1265
1266   if (!client->mix_format) {
1267     GST_ERROR_OBJECT (client, "Unknown mix format");
1268     return FALSE;
1269   }
1270
1271   /* Only use audioclient3 when low-latency is requested because otherwise
1272    * very slow machines and VMs with 1 CPU allocated will get glitches:
1273    * https://bugzilla.gnome.org/show_bug.cgi?id=794497 */
1274   if (client->low_latency)
1275     initialized = gst_wasapi2_client_initialize_audio_client3 (client);
1276
1277   /* Try again if IAudioClinet3 API is unavailable.
1278    * NOTE: IAudioClinet3:: methods might not be available for default device
1279    * NOTE: The default device is a special device which is needed for supporting
1280    * automatic stream routing
1281    * https://docs.microsoft.com/en-us/windows/win32/coreaudio/automatic-stream-routing
1282    */
1283   if (!initialized)
1284     initialized = gst_wasapi2_client_initialize_audio_client (client, spec);
1285
1286   if (!initialized) {
1287     GST_ERROR_OBJECT (client, "Failed to initialize audioclient");
1288     return FALSE;
1289   }
1290
1291   bpf = GST_AUDIO_INFO_BPF (&spec->info);
1292   rate = GST_AUDIO_INFO_RATE (&spec->info);
1293
1294   /* Total size in frames of the allocated buffer that we will read from */
1295   hr = audio_client->GetBufferSize (&client->buffer_frame_count);
1296   if (!gst_wasapi2_result (hr)) {
1297     return FALSE;
1298   }
1299
1300   GST_INFO_OBJECT (client, "buffer size is %i frames, device period is %i "
1301       "frames, bpf is %i bytes, rate is %i Hz", client->buffer_frame_count,
1302       client->device_period, bpf, rate);
1303
1304   /* Actual latency-time/buffer-time will be different now */
1305   spec->segsize = client->device_period * bpf;
1306
1307   /* We need a minimum of 2 segments to ensure glitch-free playback */
1308   spec->segtotal = MAX (client->buffer_frame_count * bpf / spec->segsize, 2);
1309
1310   GST_INFO_OBJECT (client, "segsize is %i, segtotal is %i", spec->segsize,
1311       spec->segtotal);
1312
1313   /* Get WASAPI latency for logging */
1314   hr = audio_client->GetStreamLatency (&latency_rt);
1315   if (!gst_wasapi2_result (hr)) {
1316     return FALSE;
1317   }
1318
1319   GST_INFO_OBJECT (client, "wasapi2 stream latency: %" G_GINT64_FORMAT " (%"
1320       G_GINT64_FORMAT " ms)", latency_rt, latency_rt / 10000);
1321
1322   /* Set the event handler which will trigger read/write */
1323   hr = audio_client->SetEventHandle (client->event_handle);
1324   if (!gst_wasapi2_result (hr))
1325     return FALSE;
1326
1327   if (client->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) {
1328     ComPtr<IAudioRenderClient> render_client;
1329
1330     hr = audio_client->GetService (IID_PPV_ARGS (&render_client));
1331     if (!gst_wasapi2_result (hr))
1332       return FALSE;
1333
1334     client->audio_render_client = render_client.Detach ();
1335   } else {
1336     ComPtr<IAudioCaptureClient> capture_client;
1337
1338     hr = audio_client->GetService (IID_PPV_ARGS (&capture_client));
1339     if (!gst_wasapi2_result (hr))
1340       return FALSE;
1341
1342     client->audio_capture_client = capture_client.Detach ();
1343   }
1344
1345   hr = audio_client->GetService (IID_PPV_ARGS (&audio_volume));
1346   if (!gst_wasapi2_result (hr))
1347     return FALSE;
1348
1349   client->audio_volume = audio_volume.Detach ();
1350
1351   /* this mute state seems to be global setting for this device
1352    * but below documentation looks unclear why mute state is preserved
1353    * even after process is terminated
1354    * https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-isimpleaudiovolume-setmute
1355    * Explicitly disable mute so that ensure we can produce or play audio
1356    * regardless of previous status
1357    */
1358   client->audio_volume->SetMute(FALSE, nullptr);
1359
1360   gst_audio_ring_buffer_set_channel_positions (buf, client->positions);
1361
1362   client->opened = TRUE;
1363
1364   return TRUE;
1365 }
1366
1367 /* Get the empty space in the buffer that we have to write to */
1368 static gint
1369 gst_wasapi2_client_get_can_frames (GstWasapi2Client * self)
1370 {
1371   HRESULT hr;
1372   UINT32 n_frames_padding;
1373   IAudioClient3 *audio_client = self->audio_client;
1374
1375   if (!audio_client) {
1376     GST_WARNING_OBJECT (self, "IAudioClient3 wasn't configured");
1377     return -1;
1378   }
1379
1380   /* Frames the card hasn't rendered yet */
1381   hr = audio_client->GetCurrentPadding (&n_frames_padding);
1382   if (!gst_wasapi2_result (hr))
1383     return -1;
1384
1385   GST_LOG_OBJECT (self, "%d unread frames (padding)", n_frames_padding);
1386
1387   /* We can write out these many frames */
1388   return self->buffer_frame_count - n_frames_padding;
1389 }
1390
1391 gboolean
1392 gst_wasapi2_client_start (GstWasapi2Client * client)
1393 {
1394   HRESULT hr;
1395   IAudioClient3 *audio_client;
1396   WAVEFORMATEX *mix_format;
1397
1398   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1399
1400   audio_client = client->audio_client;
1401   mix_format = client->mix_format;
1402
1403   if (!audio_client) {
1404     GST_ERROR_OBJECT (client, "IAudioClient3 object wasn't configured");
1405     return FALSE;
1406   }
1407
1408   if (!mix_format) {
1409     GST_ERROR_OBJECT (client, "Unknown MixFormat");
1410     return FALSE;
1411   }
1412
1413   if (client->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE &&
1414       !client->audio_capture_client) {
1415     GST_ERROR_OBJECT (client, "IAudioCaptureClient wasn't configured");
1416     return FALSE;
1417   }
1418
1419   if (client->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER &&
1420       !client->audio_render_client) {
1421     GST_ERROR_OBJECT (client, "IAudioRenderClient wasn't configured");
1422     return FALSE;
1423   }
1424
1425   ResetEvent (client->cancellable);
1426
1427   if (client->running) {
1428     GST_WARNING_OBJECT (client, "IAudioClient3 is running already");
1429     return TRUE;
1430   }
1431
1432   /* To avoid start-up glitches, before starting the streaming, we fill the
1433    * buffer with silence as recommended by the documentation:
1434    * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370879%28v=vs.85%29.aspx */
1435   if (client->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) {
1436     IAudioRenderClient *render_client = client->audio_render_client;
1437     gint n_frames, len;
1438     BYTE *dst = NULL;
1439
1440     n_frames = gst_wasapi2_client_get_can_frames (client);
1441     if (n_frames < 1) {
1442       GST_ERROR_OBJECT (client,
1443           "should have more than %i frames to write", n_frames);
1444       return FALSE;
1445     }
1446
1447     len = n_frames * mix_format->nBlockAlign;
1448
1449     hr = render_client->GetBuffer (n_frames, &dst);
1450     if (!gst_wasapi2_result (hr)) {
1451       GST_ERROR_OBJECT (client, "Couldn't get buffer");
1452       return FALSE;
1453     }
1454
1455     GST_DEBUG_OBJECT (client, "pre-wrote %i bytes of silence", len);
1456
1457     hr = render_client->ReleaseBuffer (n_frames, AUDCLNT_BUFFERFLAGS_SILENT);
1458     if (!gst_wasapi2_result (hr)) {
1459       GST_ERROR_OBJECT (client, "Couldn't release buffer");
1460       return FALSE;
1461     }
1462   }
1463
1464   hr = audio_client->Start ();
1465   client->running = gst_wasapi2_result (hr);
1466   gst_adapter_clear (client->adapter);
1467
1468   return client->running;
1469 }
1470
1471 gboolean
1472 gst_wasapi2_client_stop (GstWasapi2Client * client)
1473 {
1474   HRESULT hr;
1475   IAudioClient3 *audio_client;
1476
1477   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1478
1479   audio_client = client->audio_client;
1480
1481   if (!client->running) {
1482     GST_DEBUG_OBJECT (client, "We are not running now");
1483     return TRUE;
1484   }
1485
1486   if (!client->audio_client) {
1487     GST_ERROR_OBJECT (client, "IAudioClient3 object wasn't configured");
1488     return FALSE;
1489   }
1490
1491   client->running = FALSE;
1492   SetEvent (client->cancellable);
1493
1494   hr = audio_client->Stop ();
1495   if (!gst_wasapi2_result (hr))
1496     return FALSE;
1497
1498   /* reset state for reuse case */
1499   hr = audio_client->Reset ();
1500   return gst_wasapi2_result (hr);
1501 }
1502
1503 gint
1504 gst_wasapi2_client_read (GstWasapi2Client * client, gpointer data, guint length)
1505 {
1506   IAudioCaptureClient *capture_client;
1507   WAVEFORMATEX *mix_format;
1508   HRESULT hr;
1509   BYTE *from = NULL;
1510   guint wanted = length;
1511   guint bpf;
1512   DWORD flags;
1513
1514   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1515   g_return_val_if_fail (client->audio_capture_client != NULL, -1);
1516   g_return_val_if_fail (client->mix_format != NULL, -1);
1517
1518   capture_client = client->audio_capture_client;
1519   mix_format = client->mix_format;
1520
1521   if (!client->running) {
1522     GST_ERROR_OBJECT (client, "client is not running now");
1523     return -1;
1524   }
1525
1526   /* If we've accumulated enough data, return it immediately */
1527   if (gst_adapter_available (client->adapter) >= wanted) {
1528     memcpy (data, gst_adapter_map (client->adapter, wanted), wanted);
1529     gst_adapter_flush (client->adapter, wanted);
1530     GST_DEBUG_OBJECT (client, "Adapter has enough data, returning %i", wanted);
1531     return wanted;
1532   }
1533
1534   bpf = mix_format->nBlockAlign;
1535
1536   while (wanted > 0) {
1537     DWORD dwWaitResult;
1538     guint got_frames, avail_frames, n_frames, want_frames, read_len;
1539     HANDLE event_handle[2];
1540
1541     event_handle[0] = client->event_handle;
1542     event_handle[1] = client->cancellable;
1543
1544     /* Wait for data to become available */
1545     dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
1546     if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
1547       GST_ERROR_OBJECT (client, "Error waiting for event handle: %x",
1548           (guint) dwWaitResult);
1549       return -1;
1550     }
1551
1552     if (!client->running) {
1553       GST_DEBUG_OBJECT (client, "Cancelled");
1554       return -1;
1555     }
1556
1557     hr = capture_client->GetBuffer (&from, &got_frames, &flags, nullptr,
1558         nullptr);
1559     if (!gst_wasapi2_result (hr)) {
1560       if (hr == AUDCLNT_S_BUFFER_EMPTY) {
1561         GST_INFO_OBJECT (client, "Client buffer is empty, retry");
1562         return 0;
1563       }
1564
1565       GST_ERROR_OBJECT (client, "Couldn't get buffer from capture client");
1566       return -1;
1567     }
1568
1569     if (got_frames == 0) {
1570       GST_DEBUG_OBJECT (client, "No buffer to read");
1571       capture_client->ReleaseBuffer (got_frames);
1572       return 0;
1573     }
1574
1575     if (G_UNLIKELY (flags != 0)) {
1576       /* https://docs.microsoft.com/en-us/windows/win32/api/audioclient/ne-audioclient-_audclnt_bufferflags */
1577       if (flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
1578         GST_DEBUG_OBJECT (client, "WASAPI reported discontinuity (glitch?)");
1579       if (flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR)
1580         GST_DEBUG_OBJECT (client, "WASAPI reported a timestamp error");
1581     }
1582
1583     /* Copy all the frames we got into the adapter, and then extract at most
1584      * @wanted size of frames from it. This helps when ::GetBuffer returns more
1585      * data than we can handle right now. */
1586     {
1587       GstBuffer *tmp = gst_buffer_new_allocate (NULL, got_frames * bpf, NULL);
1588       /* If flags has AUDCLNT_BUFFERFLAGS_SILENT, we will ignore the actual
1589        * data and write out silence, see:
1590        * https://docs.microsoft.com/en-us/windows/win32/api/audioclient/ne-audioclient-_audclnt_bufferflags */
1591       if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
1592         memset (from, 0, got_frames * bpf);
1593       gst_buffer_fill (tmp, 0, from, got_frames * bpf);
1594       gst_adapter_push (client->adapter, tmp);
1595     }
1596
1597     /* Release all captured buffers; we copied them above */
1598     hr = capture_client->ReleaseBuffer (got_frames);
1599     from = NULL;
1600     if (!gst_wasapi2_result (hr)) {
1601       GST_ERROR_OBJECT (client, "Failed to release buffer");
1602       return -1;
1603     }
1604
1605     want_frames = wanted / bpf;
1606     avail_frames = gst_adapter_available (client->adapter) / bpf;
1607
1608     /* Only copy data that will fit into the allocated buffer of size @length */
1609     n_frames = MIN (avail_frames, want_frames);
1610     read_len = n_frames * bpf;
1611
1612     if (read_len == 0) {
1613       GST_WARNING_OBJECT (client, "No data to read");
1614       return 0;
1615     }
1616
1617     GST_LOG_OBJECT (client, "frames captured: %d (%d bytes), "
1618         "can read: %d (%d bytes), will read: %d (%d bytes), "
1619         "adapter has: %d (%d bytes)", got_frames, got_frames * bpf, want_frames,
1620         wanted, n_frames, read_len, avail_frames, avail_frames * bpf);
1621
1622     memcpy (data, gst_adapter_map (client->adapter, read_len), read_len);
1623     gst_adapter_flush (client->adapter, read_len);
1624     wanted -= read_len;
1625   }
1626
1627   return length;
1628 }
1629
1630 gint
1631 gst_wasapi2_client_write (GstWasapi2Client * client, gpointer data,
1632     guint length)
1633 {
1634   IAudioRenderClient *render_client;
1635   WAVEFORMATEX *mix_format;
1636   HRESULT hr;
1637   BYTE *dst = nullptr;
1638   DWORD dwWaitResult;
1639   guint can_frames, have_frames, n_frames, write_len = 0;
1640
1641   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), -1);
1642   g_return_val_if_fail (client->audio_render_client != NULL, -1);
1643   g_return_val_if_fail (client->mix_format != NULL, -1);
1644
1645   if (!client->running) {
1646     GST_WARNING_OBJECT (client, "client is not running now");
1647     return -1;
1648   }
1649
1650   render_client = client->audio_render_client;
1651   mix_format = client->mix_format;
1652
1653   /* We have N frames to be written out */
1654   have_frames = length / (mix_format->nBlockAlign);
1655
1656   /* In shared mode we can write parts of the buffer, so only wait
1657     * in case we can't write anything */
1658   can_frames = gst_wasapi2_client_get_can_frames (client);
1659   if (can_frames < 0) {
1660     GST_ERROR_OBJECT (client, "Error getting frames to write to");
1661     return -1;
1662   }
1663
1664   if (can_frames == 0) {
1665     HANDLE event_handle[2];
1666
1667     event_handle[0] = client->event_handle;
1668     event_handle[1] = client->cancellable;
1669
1670     dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
1671     if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
1672       GST_ERROR_OBJECT (client, "Error waiting for event handle: %x",
1673           (guint) dwWaitResult);
1674       return -1;
1675     }
1676
1677     if (!client->running) {
1678       GST_DEBUG_OBJECT (client, "Cancelled");
1679       return -1;
1680     }
1681
1682     can_frames = gst_wasapi2_client_get_can_frames (client);
1683     if (can_frames < 0) {
1684       GST_ERROR_OBJECT (client, "Error getting frames to write to");
1685       return -1;
1686     }
1687   }
1688
1689   /* We will write out these many frames, and this much length */
1690   n_frames = MIN (can_frames, have_frames);
1691   write_len = n_frames * mix_format->nBlockAlign;
1692
1693   GST_LOG_OBJECT (client, "total: %d, have_frames: %d (%d bytes), "
1694       "can_frames: %d, will write: %d (%d bytes)", client->buffer_frame_count,
1695       have_frames, length, can_frames, n_frames, write_len);
1696
1697   hr = render_client->GetBuffer (n_frames, &dst);
1698   if (!gst_wasapi2_result (hr)) {
1699     GST_ERROR_OBJECT (client, "Couldn't get buffer from client");
1700     return -1;
1701   }
1702
1703   memcpy (dst, data, write_len);
1704   hr = render_client->ReleaseBuffer (n_frames, 0);
1705
1706   return write_len;
1707 }
1708
1709 guint
1710 gst_wasapi2_client_delay (GstWasapi2Client * client)
1711 {
1712   HRESULT hr;
1713   UINT32 delay;
1714   IAudioClient3 *audio_client;
1715
1716   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), 0);
1717
1718   audio_client = client->audio_client;
1719
1720   if (!audio_client) {
1721     GST_WARNING_OBJECT (client, "IAudioClient3 wasn't configured");
1722     return 0;
1723   }
1724
1725   hr = audio_client->GetCurrentPadding (&delay);
1726   if (!gst_wasapi2_result (hr))
1727     return 0;
1728
1729   return delay;
1730 }
1731
1732 gboolean
1733 gst_wasapi2_client_set_mute (GstWasapi2Client * client, gboolean mute)
1734 {
1735   HRESULT hr;
1736   ISimpleAudioVolume *audio_volume;
1737
1738   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1739
1740   audio_volume = client->audio_volume;
1741
1742   if (!audio_volume) {
1743     GST_WARNING_OBJECT (client, "ISimpleAudioVolume object wasn't configured");
1744     return FALSE;
1745   }
1746
1747   hr = audio_volume->SetMute (mute, nullptr);
1748   GST_DEBUG_OBJECT (client, "Set mute %s, hr: 0x%x",
1749       mute ? "enabled" : "disabled", (gint) hr);
1750
1751   return gst_wasapi2_result (hr);
1752 }
1753
1754 gboolean
1755 gst_wasapi2_client_get_mute (GstWasapi2Client * client, gboolean * mute)
1756 {
1757   HRESULT hr;
1758   ISimpleAudioVolume *audio_volume;
1759   BOOL current_mute = FALSE;
1760
1761   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1762   g_return_val_if_fail (mute != NULL, FALSE);
1763
1764   audio_volume = client->audio_volume;
1765
1766   if (!audio_volume) {
1767     GST_WARNING_OBJECT (client, "ISimpleAudioVolume object wasn't configured");
1768     return FALSE;
1769   }
1770
1771   hr = audio_volume->GetMute (&current_mute);
1772   if (!gst_wasapi2_result (hr))
1773     return FALSE;
1774
1775   *mute = (gboolean) current_mute;
1776
1777   return TRUE;
1778 }
1779
1780 gboolean
1781 gst_wasapi2_client_set_volume (GstWasapi2Client * client, gfloat volume)
1782 {
1783   HRESULT hr;
1784   ISimpleAudioVolume *audio_volume;
1785
1786   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1787   g_return_val_if_fail (volume >= 0 && volume <= 1.0, FALSE);
1788
1789   audio_volume = client->audio_volume;
1790
1791   if (!audio_volume) {
1792     GST_WARNING_OBJECT (client, "ISimpleAudioVolume object wasn't configured");
1793     return FALSE;
1794   }
1795
1796   hr = audio_volume->SetMasterVolume (volume, nullptr);
1797   GST_DEBUG_OBJECT (client, "Set volume %.2f hr: 0x%x", volume, (gint) hr);
1798
1799   return gst_wasapi2_result (hr);
1800 }
1801
1802 gboolean
1803 gst_wasapi2_client_get_volume (GstWasapi2Client * client, gfloat * volume)
1804 {
1805   HRESULT hr;
1806   ISimpleAudioVolume *audio_volume;
1807   float current_volume = FALSE;
1808
1809   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1810   g_return_val_if_fail (volume != NULL, FALSE);
1811
1812   audio_volume = client->audio_volume;
1813
1814   if (!audio_volume) {
1815     GST_WARNING_OBJECT (client, "ISimpleAudioVolume object wasn't configured");
1816     return FALSE;
1817   }
1818
1819   hr = audio_volume->GetMasterVolume (&current_volume);
1820   if (!gst_wasapi2_result (hr))
1821     return FALSE;
1822
1823   *volume = current_volume;
1824
1825   return TRUE;
1826 }
1827
1828 gboolean
1829 gst_wasapi2_client_ensure_activation (GstWasapi2Client * client)
1830 {
1831   g_return_val_if_fail (GST_IS_WASAPI2_CLIENT (client), FALSE);
1832
1833   /* should not happen */
1834   g_assert (client->activate_state != GST_WASAPI2_CLIENT_ACTIVATE_INIT);
1835
1836   g_mutex_lock (&client->init_lock);
1837   while (client->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_WAIT)
1838     g_cond_wait (&client->init_cond, &client->init_lock);
1839   g_mutex_unlock (&client->init_lock);
1840
1841   return client->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_DONE;
1842 }
1843
1844 static HRESULT
1845 find_dispatcher (ICoreDispatcher ** dispatcher)
1846 {
1847   HStringReference hstr_core_app =
1848       HStringReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication);
1849   HRESULT hr;
1850
1851   ComPtr<ICoreApplication> core_app;
1852   hr = GetActivationFactory (hstr_core_app.Get(), &core_app);
1853   if (FAILED (hr))
1854     return hr;
1855
1856   ComPtr<ICoreApplicationView> core_app_view;
1857   hr = core_app->GetCurrentView (&core_app_view);
1858   if (FAILED (hr))
1859     return hr;
1860
1861   ComPtr<ICoreWindow> core_window;
1862   hr = core_app_view->get_CoreWindow (&core_window);
1863   if (FAILED (hr))
1864     return hr;
1865
1866   return core_window->get_Dispatcher (dispatcher);
1867 }
1868
1869 GstWasapi2Client *
1870 gst_wasapi2_client_new (GstWasapi2ClientDeviceClass device_class,
1871     gboolean low_latency, gint device_index, const gchar * device_id,
1872     gpointer dispatcher)
1873 {
1874   GstWasapi2Client *self;
1875   ComPtr<ICoreDispatcher> core_dispatcher;
1876   /* Multiple COM init is allowed */
1877   RoInitializeWrapper init_wrapper (RO_INIT_MULTITHREADED);
1878
1879   /* If application didn't pass ICoreDispatcher object,
1880    * try to get dispatcher object for the current thread */
1881   if (!dispatcher) {
1882     HRESULT hr;
1883
1884     hr = find_dispatcher (&core_dispatcher);
1885     if (SUCCEEDED (hr)) {
1886       GST_DEBUG ("UI dispatcher is available");
1887       dispatcher = core_dispatcher.Get ();
1888     } else {
1889       GST_DEBUG ("UI dispatcher is unavailable");
1890     }
1891   } else {
1892     GST_DEBUG ("Use user passed UI dispatcher");
1893   }
1894
1895   self = (GstWasapi2Client *) g_object_new (GST_TYPE_WASAPI2_CLIENT,
1896       "device-class", device_class, "low-latency", low_latency,
1897       "device-index", device_index, "device", device_id,
1898       "dispatcher", dispatcher, NULL);
1899
1900   /* Reset explicitly to ensure that it happens before
1901    * RoInitializeWrapper dtor is called */
1902   core_dispatcher.Reset ();
1903
1904   if (self->activate_state == GST_WASAPI2_CLIENT_ACTIVATE_FAILED) {
1905     gst_object_unref (self);
1906     return NULL;
1907   }
1908
1909   gst_object_ref_sink (self);
1910
1911   return self;
1912 }