wasapi2src: Fix loopback capture on Windows 10 Anniversary Update
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / wasapi2 / gstwasapi2ringbuffer.cpp
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 #include "gstwasapi2ringbuffer.h"
21 #include <string.h>
22 #include <mfapi.h>
23 #include <wrl.h>
24
25 GST_DEBUG_CATEGORY_STATIC (gst_wasapi2_ring_buffer_debug);
26 #define GST_CAT_DEFAULT gst_wasapi2_ring_buffer_debug
27
28 static HRESULT gst_wasapi2_ring_buffer_io_callback (GstWasapi2RingBuffer * buf);
29 static HRESULT
30 gst_wasapi2_ring_buffer_loopback_callback (GstWasapi2RingBuffer * buf);
31
32 /* *INDENT-OFF* */
33 using namespace Microsoft::WRL;
34
35 class GstWasapiAsyncCallback : public IMFAsyncCallback
36 {
37 public:
38   GstWasapiAsyncCallback(GstWasapi2RingBuffer *listener,
39                          DWORD queue_id,
40                          gboolean loopback)
41     : ref_count_(1)
42     , queue_id_(queue_id)
43     , loopback_(loopback)
44   {
45     g_weak_ref_init (&listener_, listener);
46   }
47
48   virtual ~GstWasapiAsyncCallback ()
49   {
50     g_weak_ref_set (&listener_, nullptr);
51   }
52
53   /* IUnknown */
54   STDMETHODIMP_ (ULONG)
55   AddRef (void)
56   {
57     GST_TRACE ("%p, %d", this, ref_count_);
58     return InterlockedIncrement (&ref_count_);
59   }
60   STDMETHODIMP_ (ULONG)
61   Release (void)
62   {
63     ULONG ref_count;
64
65     GST_TRACE ("%p, %d", this, ref_count_);
66     ref_count = InterlockedDecrement (&ref_count_);
67
68     if (ref_count == 0) {
69       GST_TRACE ("Delete instance %p", this);
70       delete this;
71     }
72
73     return ref_count;
74   }
75
76   STDMETHODIMP
77   QueryInterface (REFIID riid, void ** object)
78   {
79     if (!object)
80       return E_POINTER;
81
82     if (riid == IID_IUnknown) {
83       GST_TRACE ("query IUnknown interface %p", this);
84       *object = static_cast<IUnknown *> (static_cast<GstWasapiAsyncCallback *> (this));
85     } else if (riid == __uuidof (IMFAsyncCallback)) {
86       GST_TRACE ("query IUnknown interface %p", this);
87       *object = static_cast<IUnknown *> (static_cast<GstWasapiAsyncCallback *> (this));
88     } else {
89       *object = nullptr;
90       return E_NOINTERFACE;
91     }
92
93     AddRef ();
94
95     return S_OK;
96   }
97
98   /* IMFAsyncCallback */
99   STDMETHODIMP
100   GetParameters(DWORD * pdwFlags, DWORD * pdwQueue)
101   {
102     *pdwFlags = 0;
103     *pdwQueue = queue_id_;
104
105     return S_OK;
106   }
107
108   STDMETHODIMP
109   Invoke(IMFAsyncResult * pAsyncResult)
110   {
111     GstWasapi2RingBuffer *ringbuffer;
112     HRESULT hr;
113
114     ringbuffer = (GstWasapi2RingBuffer *) g_weak_ref_get (&listener_);
115     if (!ringbuffer) {
116       GST_WARNING ("Listener was removed");
117       return S_OK;
118     }
119
120     if (loopback_)
121       hr = gst_wasapi2_ring_buffer_loopback_callback (ringbuffer);
122     else
123       hr = gst_wasapi2_ring_buffer_io_callback (ringbuffer);
124     gst_object_unref (ringbuffer);
125
126     return hr;
127   }
128
129 private:
130   ULONG ref_count_;
131   DWORD queue_id_;
132   GWeakRef listener_;
133   gboolean loopback_;
134 };
135 /* *INDENT-ON* */
136
137 struct _GstWasapi2RingBuffer
138 {
139   GstAudioRingBuffer parent;
140
141   GstWasapi2ClientDeviceClass device_class;
142   gchar *device_id;
143   gboolean low_latency;
144   gboolean mute;
145   gdouble volume;
146   gpointer dispatcher;
147   gboolean can_auto_routing;
148   guint loopback_target_pid;
149
150   GstWasapi2Client *client;
151   GstWasapi2Client *loopback_client;
152   IAudioCaptureClient *capture_client;
153   IAudioRenderClient *render_client;
154   ISimpleAudioVolume *volume_object;
155
156   GstWasapiAsyncCallback *callback_object;
157   IMFAsyncResult *callback_result;
158   MFWORKITEM_KEY callback_key;
159   HANDLE event_handle;
160
161   GstWasapiAsyncCallback *loopback_callback_object;
162   IMFAsyncResult *loopback_callback_result;
163   MFWORKITEM_KEY loopback_callback_key;
164   HANDLE loopback_event_handle;
165
166   guint64 expected_position;
167   gboolean is_first;
168   gboolean running;
169   UINT32 buffer_size;
170   UINT32 loopback_buffer_size;
171
172   gint segoffset;
173   guint64 write_frame_offset;
174
175   GMutex volume_lock;
176   gboolean mute_changed;
177   gboolean volume_changed;
178
179   GstCaps *supported_caps;
180 };
181
182 static void gst_wasapi2_ring_buffer_constructed (GObject * object);
183 static void gst_wasapi2_ring_buffer_dispose (GObject * object);
184 static void gst_wasapi2_ring_buffer_finalize (GObject * object);
185
186 static gboolean gst_wasapi2_ring_buffer_open_device (GstAudioRingBuffer * buf);
187 static gboolean gst_wasapi2_ring_buffer_close_device (GstAudioRingBuffer * buf);
188 static gboolean gst_wasapi2_ring_buffer_acquire (GstAudioRingBuffer * buf,
189     GstAudioRingBufferSpec * spec);
190 static gboolean gst_wasapi2_ring_buffer_release (GstAudioRingBuffer * buf);
191 static gboolean gst_wasapi2_ring_buffer_start (GstAudioRingBuffer * buf);
192 static gboolean gst_wasapi2_ring_buffer_resume (GstAudioRingBuffer * buf);
193 static gboolean gst_wasapi2_ring_buffer_pause (GstAudioRingBuffer * buf);
194 static gboolean gst_wasapi2_ring_buffer_stop (GstAudioRingBuffer * buf);
195 static guint gst_wasapi2_ring_buffer_delay (GstAudioRingBuffer * buf);
196
197 #define gst_wasapi2_ring_buffer_parent_class parent_class
198 G_DEFINE_TYPE (GstWasapi2RingBuffer, gst_wasapi2_ring_buffer,
199     GST_TYPE_AUDIO_RING_BUFFER);
200
201 static void
202 gst_wasapi2_ring_buffer_class_init (GstWasapi2RingBufferClass * klass)
203 {
204   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
205   GstAudioRingBufferClass *ring_buffer_class =
206       GST_AUDIO_RING_BUFFER_CLASS (klass);
207
208   gobject_class->constructed = gst_wasapi2_ring_buffer_constructed;
209   gobject_class->dispose = gst_wasapi2_ring_buffer_dispose;
210   gobject_class->finalize = gst_wasapi2_ring_buffer_finalize;
211
212   ring_buffer_class->open_device =
213       GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_open_device);
214   ring_buffer_class->close_device =
215       GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_close_device);
216   ring_buffer_class->acquire =
217       GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_acquire);
218   ring_buffer_class->release =
219       GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_release);
220   ring_buffer_class->start = GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_start);
221   ring_buffer_class->resume =
222       GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_resume);
223   ring_buffer_class->pause = GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_pause);
224   ring_buffer_class->stop = GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_stop);
225   ring_buffer_class->delay = GST_DEBUG_FUNCPTR (gst_wasapi2_ring_buffer_delay);
226
227   GST_DEBUG_CATEGORY_INIT (gst_wasapi2_ring_buffer_debug,
228       "wasapi2ringbuffer", 0, "wasapi2ringbuffer");
229 }
230
231 static void
232 gst_wasapi2_ring_buffer_init (GstWasapi2RingBuffer * self)
233 {
234   self->volume = 1.0f;
235   self->mute = FALSE;
236
237   self->event_handle = CreateEvent (nullptr, FALSE, FALSE, nullptr);
238   self->loopback_event_handle = CreateEvent (nullptr, FALSE, FALSE, nullptr);
239   g_mutex_init (&self->volume_lock);
240 }
241
242 static void
243 gst_wasapi2_ring_buffer_constructed (GObject * object)
244 {
245   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (object);
246   HRESULT hr;
247   DWORD task_id = 0;
248   DWORD queue_id = 0;
249
250   hr = MFLockSharedWorkQueue (L"Pro Audio", 0, &task_id, &queue_id);
251   if (!gst_wasapi2_result (hr)) {
252     GST_WARNING_OBJECT (self, "Failed to get work queue id");
253     goto out;
254   }
255
256   self->callback_object = new GstWasapiAsyncCallback (self, queue_id, FALSE);
257   hr = MFCreateAsyncResult (nullptr, self->callback_object, nullptr,
258       &self->callback_result);
259   if (!gst_wasapi2_result (hr)) {
260     GST_WARNING_OBJECT (self, "Failed to create IAsyncResult");
261     GST_WASAPI2_CLEAR_COM (self->callback_object);
262   }
263
264   /* Create another callback object for loopback silence feed */
265   self->loopback_callback_object =
266       new GstWasapiAsyncCallback (self, queue_id, TRUE);
267   hr = MFCreateAsyncResult (nullptr, self->loopback_callback_object, nullptr,
268       &self->loopback_callback_result);
269   if (!gst_wasapi2_result (hr)) {
270     GST_WARNING_OBJECT (self, "Failed to create IAsyncResult");
271     GST_WASAPI2_CLEAR_COM (self->callback_object);
272     GST_WASAPI2_CLEAR_COM (self->callback_result);
273     GST_WASAPI2_CLEAR_COM (self->loopback_callback_object);
274   }
275
276 out:
277   G_OBJECT_CLASS (parent_class)->constructed (object);
278 }
279
280 static void
281 gst_wasapi2_ring_buffer_dispose (GObject * object)
282 {
283   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (object);
284
285   GST_WASAPI2_CLEAR_COM (self->render_client);
286   GST_WASAPI2_CLEAR_COM (self->capture_client);
287   GST_WASAPI2_CLEAR_COM (self->volume_object);
288   GST_WASAPI2_CLEAR_COM (self->callback_result);
289   GST_WASAPI2_CLEAR_COM (self->callback_object);
290   GST_WASAPI2_CLEAR_COM (self->loopback_callback_result);
291   GST_WASAPI2_CLEAR_COM (self->loopback_callback_object);
292
293   gst_clear_object (&self->client);
294   gst_clear_object (&self->loopback_client);
295   gst_clear_caps (&self->supported_caps);
296
297   G_OBJECT_CLASS (parent_class)->dispose (object);
298 }
299
300 static void
301 gst_wasapi2_ring_buffer_finalize (GObject * object)
302 {
303   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (object);
304
305   g_free (self->device_id);
306   CloseHandle (self->event_handle);
307   CloseHandle (self->loopback_event_handle);
308   g_mutex_clear (&self->volume_lock);
309
310   G_OBJECT_CLASS (parent_class)->finalize (object);
311 }
312
313 static void
314 gst_wasapi2_ring_buffer_post_open_error (GstWasapi2RingBuffer * self)
315 {
316   GstElement *parent = (GstElement *) GST_OBJECT_PARENT (self);
317
318   if (!parent) {
319     GST_WARNING_OBJECT (self, "Cannot find parent");
320     return;
321   }
322
323   if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) {
324     GST_ELEMENT_ERROR (parent, RESOURCE, OPEN_WRITE,
325         (nullptr), ("Failed to open device"));
326   } else {
327     GST_ELEMENT_ERROR (parent, RESOURCE, OPEN_READ,
328         (nullptr), ("Failed to open device"));
329   }
330 }
331
332 static void
333 gst_wasapi2_ring_buffer_post_scheduling_error (GstWasapi2RingBuffer * self)
334 {
335   GstElement *parent = (GstElement *) GST_OBJECT_PARENT (self);
336
337   if (!parent) {
338     GST_WARNING_OBJECT (self, "Cannot find parent");
339     return;
340   }
341
342   GST_ELEMENT_ERROR (parent, RESOURCE, FAILED,
343       (nullptr), ("Failed to schedule next I/O"));
344 }
345
346 static void
347 gst_wasapi2_ring_buffer_post_io_error (GstWasapi2RingBuffer * self, HRESULT hr)
348 {
349   GstElement *parent = (GstElement *) GST_OBJECT_PARENT (self);
350   gchar *error_msg;
351
352   if (!parent) {
353     GST_WARNING_OBJECT (self, "Cannot find parent");
354     return;
355   }
356
357   error_msg = gst_wasapi2_util_get_error_message (hr);
358
359   GST_ERROR_OBJECT (self, "Posting I/O error %s (hr: 0x%x)", error_msg, hr);
360   if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) {
361     GST_ELEMENT_ERROR (parent, RESOURCE, WRITE,
362         ("Failed to write to device"), ("%s, hr: 0x%x", error_msg, hr));
363   } else {
364     GST_ELEMENT_ERROR (parent, RESOURCE, READ,
365         ("Failed to read from device"), ("%s hr: 0x%x", error_msg, hr));
366   }
367
368   g_free (error_msg);
369 }
370
371 static gboolean
372 gst_wasapi2_ring_buffer_open_device (GstAudioRingBuffer * buf)
373 {
374   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
375
376   GST_DEBUG_OBJECT (self, "Open");
377
378   if (self->client) {
379     GST_DEBUG_OBJECT (self, "Already opened");
380     return TRUE;
381   }
382
383   self->client = gst_wasapi2_client_new (self->device_class,
384       -1, self->device_id, self->loopback_target_pid, self->dispatcher);
385   if (!self->client) {
386     gst_wasapi2_ring_buffer_post_open_error (self);
387     return FALSE;
388   }
389
390   g_object_get (self->client, "auto-routing", &self->can_auto_routing, nullptr);
391
392   /* Open another render client to feed silence */
393   if (gst_wasapi2_device_class_is_loopback (self->device_class)) {
394     self->loopback_client =
395         gst_wasapi2_client_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER,
396         -1, self->device_id, 0, self->dispatcher);
397
398     if (!self->loopback_client) {
399       gst_wasapi2_ring_buffer_post_open_error (self);
400       gst_clear_object (&self->client);
401
402       return FALSE;
403     }
404   }
405
406   return TRUE;
407 }
408
409 static gboolean
410 gst_wasapi2_ring_buffer_close_device_internal (GstAudioRingBuffer * buf)
411 {
412   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
413
414   GST_DEBUG_OBJECT (self, "Close device");
415
416   if (self->running)
417     gst_wasapi2_ring_buffer_stop (buf);
418
419   GST_WASAPI2_CLEAR_COM (self->capture_client);
420   GST_WASAPI2_CLEAR_COM (self->render_client);
421
422   g_mutex_lock (&self->volume_lock);
423   if (self->volume_object)
424     self->volume_object->SetMute (FALSE, nullptr);
425   GST_WASAPI2_CLEAR_COM (self->volume_object);
426   g_mutex_unlock (&self->volume_lock);
427
428   gst_clear_object (&self->client);
429   gst_clear_object (&self->loopback_client);
430
431   return TRUE;
432 }
433
434 static gboolean
435 gst_wasapi2_ring_buffer_close_device (GstAudioRingBuffer * buf)
436 {
437   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
438
439   GST_DEBUG_OBJECT (self, "Close");
440
441   gst_wasapi2_ring_buffer_close_device_internal (buf);
442
443   gst_clear_caps (&self->supported_caps);
444
445   return TRUE;
446 }
447
448 static HRESULT
449 gst_wasapi2_ring_buffer_read (GstWasapi2RingBuffer * self)
450 {
451   GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
452   BYTE *data = nullptr;
453   UINT32 to_read = 0;
454   guint32 to_read_bytes;
455   DWORD flags = 0;
456   HRESULT hr;
457   guint64 position;
458   GstAudioInfo *info = &ringbuffer->spec.info;
459   IAudioCaptureClient *capture_client = self->capture_client;
460   guint gap_size = 0;
461   guint offset = 0;
462   gint segment;
463   guint8 *readptr;
464   gint len;
465
466   if (!capture_client) {
467     GST_ERROR_OBJECT (self, "IAudioCaptureClient is not available");
468     return E_FAIL;
469   }
470
471   hr = capture_client->GetBuffer (&data, &to_read, &flags, &position, nullptr);
472   if (hr == AUDCLNT_S_BUFFER_EMPTY || to_read == 0) {
473     GST_LOG_OBJECT (self, "Empty buffer");
474     to_read = 0;
475     goto out;
476   }
477
478   to_read_bytes = to_read * GST_AUDIO_INFO_BPF (info);
479
480   GST_LOG_OBJECT (self, "Reading %d frames offset at %" G_GUINT64_FORMAT
481       ", expected position %" G_GUINT64_FORMAT, to_read, position,
482       self->expected_position);
483
484   /* XXX: position might not be increased in case of process loopback  */
485   if (!gst_wasapi2_device_class_is_process_loopback (self->device_class)) {
486     if (self->is_first) {
487       self->expected_position = position + to_read;
488       self->is_first = FALSE;
489     } else {
490       if (position > self->expected_position) {
491         guint gap_frames;
492
493         gap_frames = (guint) (position - self->expected_position);
494         GST_WARNING_OBJECT (self, "Found %u frames gap", gap_frames);
495         gap_size = gap_frames * GST_AUDIO_INFO_BPF (info);
496       }
497
498       self->expected_position = position + to_read;
499     }
500   } else if (self->mute) {
501     /* volume clinet might not be available in case of process loopback */
502     flags |= AUDCLNT_BUFFERFLAGS_SILENT;
503   }
504
505   /* Fill gap data if any */
506   while (gap_size > 0) {
507     if (!gst_audio_ring_buffer_prepare_read (ringbuffer,
508             &segment, &readptr, &len)) {
509       GST_INFO_OBJECT (self, "No segment available");
510       goto out;
511     }
512
513     g_assert (self->segoffset >= 0);
514
515     len -= self->segoffset;
516     if (len > gap_size)
517       len = gap_size;
518
519     gst_audio_format_info_fill_silence (ringbuffer->spec.info.finfo,
520         readptr + self->segoffset, len);
521
522     self->segoffset += len;
523     gap_size -= len;
524
525     if (self->segoffset == ringbuffer->spec.segsize) {
526       gst_audio_ring_buffer_advance (ringbuffer, 1);
527       self->segoffset = 0;
528     }
529   }
530
531   while (to_read_bytes) {
532     if (!gst_audio_ring_buffer_prepare_read (ringbuffer,
533             &segment, &readptr, &len)) {
534       GST_INFO_OBJECT (self, "No segment available");
535       goto out;
536     }
537
538     len -= self->segoffset;
539     if (len > to_read_bytes)
540       len = to_read_bytes;
541
542     if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == AUDCLNT_BUFFERFLAGS_SILENT) {
543       gst_audio_format_info_fill_silence (ringbuffer->spec.info.finfo,
544           readptr + self->segoffset, len);
545     } else {
546       memcpy (readptr + self->segoffset, data + offset, len);
547     }
548
549     self->segoffset += len;
550     offset += len;
551     to_read_bytes -= len;
552
553     if (self->segoffset == ringbuffer->spec.segsize) {
554       gst_audio_ring_buffer_advance (ringbuffer, 1);
555       self->segoffset = 0;
556     }
557   }
558
559 out:
560   hr = capture_client->ReleaseBuffer (to_read);
561   /* For debugging */
562   gst_wasapi2_result (hr);
563
564   return hr;
565 }
566
567 static HRESULT
568 gst_wasapi2_ring_buffer_write (GstWasapi2RingBuffer * self, gboolean preroll)
569 {
570   GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
571   HRESULT hr;
572   IAudioClient *client_handle;
573   IAudioRenderClient *render_client;
574   guint32 padding_frames = 0;
575   guint32 can_write;
576   guint32 can_write_bytes;
577   gint segment;
578   guint8 *readptr;
579   gint len;
580   BYTE *data = nullptr;
581
582   client_handle = gst_wasapi2_client_get_handle (self->client);
583   if (!client_handle) {
584     GST_ERROR_OBJECT (self, "IAudioClient is not available");
585     return E_FAIL;
586   }
587
588   render_client = self->render_client;
589   if (!render_client) {
590     GST_ERROR_OBJECT (self, "IAudioRenderClient is not available");
591     return E_FAIL;
592   }
593
594   hr = client_handle->GetCurrentPadding (&padding_frames);
595   if (!gst_wasapi2_result (hr))
596     return hr;
597
598   if (padding_frames >= self->buffer_size) {
599     GST_INFO_OBJECT (self,
600         "Padding size %d is larger than or equal to buffer size %d",
601         padding_frames, self->buffer_size);
602     return S_OK;
603   }
604
605   can_write = self->buffer_size - padding_frames;
606   can_write_bytes = can_write * GST_AUDIO_INFO_BPF (&ringbuffer->spec.info);
607   if (preroll) {
608     GST_INFO_OBJECT (self, "Pre-fill %d frames with silence", can_write);
609
610     hr = render_client->GetBuffer (can_write, &data);
611     if (!gst_wasapi2_result (hr))
612       return hr;
613
614     hr = render_client->ReleaseBuffer (can_write, AUDCLNT_BUFFERFLAGS_SILENT);
615     return gst_wasapi2_result (hr);
616   }
617
618   GST_LOG_OBJECT (self, "Writing %d frames offset at %" G_GUINT64_FORMAT,
619       can_write, self->write_frame_offset);
620   self->write_frame_offset += can_write;
621
622   while (can_write_bytes > 0) {
623     if (!gst_audio_ring_buffer_prepare_read (ringbuffer,
624             &segment, &readptr, &len)) {
625       GST_INFO_OBJECT (self, "No segment available, fill silence");
626
627       /* This would be case where in the middle of PAUSED state change.
628        * Just fill silent buffer to avoid immediate I/O callback after
629        * we return here */
630       hr = render_client->GetBuffer (can_write, &data);
631       if (!gst_wasapi2_result (hr))
632         return hr;
633
634       hr = render_client->ReleaseBuffer (can_write, AUDCLNT_BUFFERFLAGS_SILENT);
635       /* for debugging */
636       gst_wasapi2_result (hr);
637       return hr;
638     }
639
640     len -= self->segoffset;
641
642     if (len > can_write_bytes)
643       len = can_write_bytes;
644
645     can_write = len / GST_AUDIO_INFO_BPF (&ringbuffer->spec.info);
646     if (can_write == 0)
647       break;
648
649     hr = render_client->GetBuffer (can_write, &data);
650     if (!gst_wasapi2_result (hr))
651       return hr;
652
653     memcpy (data, readptr + self->segoffset, len);
654     hr = render_client->ReleaseBuffer (can_write, 0);
655
656     self->segoffset += len;
657     can_write_bytes -= len;
658
659     if (self->segoffset == ringbuffer->spec.segsize) {
660       gst_audio_ring_buffer_clear (ringbuffer, segment);
661       gst_audio_ring_buffer_advance (ringbuffer, 1);
662       self->segoffset = 0;
663     }
664
665     if (!gst_wasapi2_result (hr)) {
666       GST_WARNING_OBJECT (self, "Failed to release buffer");
667       break;
668     }
669   }
670
671   return S_OK;
672 }
673
674 static HRESULT
675 gst_wasapi2_ring_buffer_io_callback (GstWasapi2RingBuffer * self)
676 {
677   HRESULT hr = E_FAIL;
678
679   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (self), E_FAIL);
680
681   if (!self->running) {
682     GST_INFO_OBJECT (self, "We are not running now");
683     return S_OK;
684   }
685
686   switch (self->device_class) {
687     case GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE:
688     case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE:
689     case GST_WASAPI2_CLIENT_DEVICE_CLASS_INCLUDE_PROCESS_LOOPBACK_CAPTURE:
690     case GST_WASAPI2_CLIENT_DEVICE_CLASS_EXCLUDE_PROCESS_LOOPBACK_CAPTURE:
691       hr = gst_wasapi2_ring_buffer_read (self);
692       break;
693     case GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER:
694       hr = gst_wasapi2_ring_buffer_write (self, FALSE);
695       break;
696     default:
697       g_assert_not_reached ();
698       break;
699   }
700
701   /* We can ignore errors for device unplugged event if client can support
702    * automatic stream routing, but except for loopback capture.
703    * loopback capture client doesn't seem to be able to recover status from this
704    * situation */
705   if (self->can_auto_routing &&
706       !gst_wasapi2_device_class_is_loopback (self->device_class) &&
707       !gst_wasapi2_device_class_is_process_loopback (self->device_class) &&
708       (hr == AUDCLNT_E_ENDPOINT_CREATE_FAILED
709           || hr == AUDCLNT_E_DEVICE_INVALIDATED)) {
710     GST_WARNING_OBJECT (self,
711         "Device was unplugged but client can support automatic routing");
712     hr = S_OK;
713   }
714
715   if (self->running) {
716     if (gst_wasapi2_result (hr) &&
717         /* In case of normal loopback capture, this method is called from
718          * silence feeding thread. Don't schedule again in that case */
719         self->device_class != GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE) {
720       hr = MFPutWaitingWorkItem (self->event_handle, 0, self->callback_result,
721           &self->callback_key);
722
723       if (!gst_wasapi2_result (hr)) {
724         GST_ERROR_OBJECT (self, "Failed to put item");
725         gst_wasapi2_ring_buffer_post_scheduling_error (self);
726
727         return hr;
728       }
729     }
730   } else {
731     GST_INFO_OBJECT (self, "We are not running now");
732     return S_OK;
733   }
734
735   if (FAILED (hr))
736     gst_wasapi2_ring_buffer_post_io_error (self, hr);
737
738   return hr;
739 }
740
741 static HRESULT
742 gst_wasapi2_ring_buffer_fill_loopback_silence (GstWasapi2RingBuffer * self)
743 {
744   HRESULT hr;
745   IAudioClient *client_handle;
746   IAudioRenderClient *render_client;
747   guint32 padding_frames = 0;
748   guint32 can_write;
749   BYTE *data = nullptr;
750
751   client_handle = gst_wasapi2_client_get_handle (self->loopback_client);
752   if (!client_handle) {
753     GST_ERROR_OBJECT (self, "IAudioClient is not available");
754     return E_FAIL;
755   }
756
757   render_client = self->render_client;
758   if (!render_client) {
759     GST_ERROR_OBJECT (self, "IAudioRenderClient is not available");
760     return E_FAIL;
761   }
762
763   hr = client_handle->GetCurrentPadding (&padding_frames);
764   if (!gst_wasapi2_result (hr))
765     return hr;
766
767   if (padding_frames >= self->loopback_buffer_size) {
768     GST_INFO_OBJECT (self,
769         "Padding size %d is larger than or equal to buffer size %d",
770         padding_frames, self->loopback_buffer_size);
771     return S_OK;
772   }
773
774   can_write = self->loopback_buffer_size - padding_frames;
775
776   GST_TRACE_OBJECT (self, "Writing %d silent frames", can_write);
777
778   hr = render_client->GetBuffer (can_write, &data);
779   if (!gst_wasapi2_result (hr))
780     return hr;
781
782   hr = render_client->ReleaseBuffer (can_write, AUDCLNT_BUFFERFLAGS_SILENT);
783   return gst_wasapi2_result (hr);
784 }
785
786 static HRESULT
787 gst_wasapi2_ring_buffer_loopback_callback (GstWasapi2RingBuffer * self)
788 {
789   HRESULT hr = E_FAIL;
790
791   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (self), E_FAIL);
792   g_return_val_if_fail (gst_wasapi2_device_class_is_loopback
793       (self->device_class), E_FAIL);
794
795   if (!self->running) {
796     GST_INFO_OBJECT (self, "We are not running now");
797     return S_OK;
798   }
799
800   hr = gst_wasapi2_ring_buffer_fill_loopback_silence (self);
801
802   /* On Windows versions prior to Windows 10, a pull-mode capture client will
803    * not receive any events when a stream is initialized with event-driven
804    * buffering */
805   if (gst_wasapi2_result (hr))
806     hr = gst_wasapi2_ring_buffer_io_callback (self);
807
808   if (self->running) {
809     if (gst_wasapi2_result (hr)) {
810       hr = MFPutWaitingWorkItem (self->loopback_event_handle, 0,
811           self->loopback_callback_result, &self->loopback_callback_key);
812
813       if (!gst_wasapi2_result (hr)) {
814         GST_ERROR_OBJECT (self, "Failed to put item");
815         gst_wasapi2_ring_buffer_post_scheduling_error (self);
816
817         return hr;
818       }
819     }
820   } else {
821     GST_INFO_OBJECT (self, "We are not running now");
822     return S_OK;
823   }
824
825   if (FAILED (hr))
826     gst_wasapi2_ring_buffer_post_io_error (self, hr);
827
828   return hr;
829 }
830
831 static HRESULT
832 gst_wasapi2_ring_buffer_initialize_audio_client3 (GstWasapi2RingBuffer * self,
833     IAudioClient * client_handle, WAVEFORMATEX * mix_format, guint * period)
834 {
835   HRESULT hr = S_OK;
836   UINT32 default_period, fundamental_period, min_period, max_period;
837   /* AUDCLNT_STREAMFLAGS_NOPERSIST is not allowed for
838    * InitializeSharedAudioStream */
839   DWORD stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
840   ComPtr < IAudioClient3 > audio_client;
841
842   hr = client_handle->QueryInterface (IID_PPV_ARGS (&audio_client));
843   if (!gst_wasapi2_result (hr)) {
844     GST_INFO_OBJECT (self, "IAudioClient3 interface is unavailable");
845     return hr;
846   }
847
848   hr = audio_client->GetSharedModeEnginePeriod (mix_format,
849       &default_period, &fundamental_period, &min_period, &max_period);
850   if (!gst_wasapi2_result (hr)) {
851     GST_INFO_OBJECT (self, "Couldn't get period");
852     return hr;
853   }
854
855   GST_INFO_OBJECT (self, "Using IAudioClient3, default period %d frames, "
856       "fundamental period %d frames, minimum period %d frames, maximum period "
857       "%d frames", default_period, fundamental_period, min_period, max_period);
858
859   *period = min_period;
860
861   hr = audio_client->InitializeSharedAudioStream (stream_flags, min_period,
862       mix_format, nullptr);
863
864   if (!gst_wasapi2_result (hr))
865     GST_WARNING_OBJECT (self, "Failed to initialize IAudioClient3");
866
867   return hr;
868 }
869
870 static HRESULT
871 gst_wasapi2_ring_buffer_initialize_audio_client (GstWasapi2RingBuffer * self,
872     IAudioClient * client_handle, WAVEFORMATEX * mix_format, guint * period,
873     DWORD extra_flags, GstWasapi2ClientDeviceClass device_class)
874 {
875   GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
876   REFERENCE_TIME default_period, min_period;
877   DWORD stream_flags =
878       AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
879   HRESULT hr;
880
881   stream_flags |= extra_flags;
882
883   if (!gst_wasapi2_device_class_is_process_loopback (device_class)) {
884     hr = client_handle->GetDevicePeriod (&default_period, &min_period);
885     if (!gst_wasapi2_result (hr)) {
886       GST_WARNING_OBJECT (self, "Couldn't get device period info");
887       return hr;
888     }
889
890     GST_INFO_OBJECT (self, "wasapi2 default period: %" G_GINT64_FORMAT
891         ", min period: %" G_GINT64_FORMAT, default_period, min_period);
892
893     hr = client_handle->Initialize (AUDCLNT_SHAREMODE_SHARED, stream_flags,
894         /* hnsBufferDuration should be same as hnsPeriodicity
895          * when AUDCLNT_STREAMFLAGS_EVENTCALLBACK is used.
896          * And in case of shared mode, hnsPeriodicity should be zero, so
897          * this value should be zero as well */
898         0,
899         /* This must always be 0 in shared mode */
900         0, mix_format, nullptr);
901   } else {
902     /* XXX: virtual device will not report device period.
903      * Use hardcoded period 20ms, same as Microsoft sample code
904      * https://github.com/microsoft/windows-classic-samples/tree/main/Samples/ApplicationLoopback
905      */
906     default_period = (20 * GST_MSECOND) / 100;
907     hr = client_handle->Initialize (AUDCLNT_SHAREMODE_SHARED,
908         AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
909         default_period,
910         AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM, mix_format, nullptr);
911   }
912
913   if (!gst_wasapi2_result (hr)) {
914     GST_WARNING_OBJECT (self, "Couldn't initialize audioclient");
915     return hr;
916   }
917
918   *period = gst_util_uint64_scale_round (default_period * 100,
919       GST_AUDIO_INFO_RATE (&ringbuffer->spec.info), GST_SECOND);
920
921   return S_OK;
922 }
923
924 static gboolean
925 gst_wasapi2_ring_buffer_prepare_loopback_client (GstWasapi2RingBuffer * self)
926 {
927   IAudioClient *client_handle;
928   HRESULT hr;
929   WAVEFORMATEX *mix_format = nullptr;
930   guint period = 0;
931   ComPtr < IAudioRenderClient > render_client;
932
933   if (!self->loopback_client) {
934     GST_ERROR_OBJECT (self, "No configured client object");
935     return FALSE;
936   }
937
938   if (!gst_wasapi2_client_ensure_activation (self->loopback_client)) {
939     GST_ERROR_OBJECT (self, "Failed to activate audio client");
940     return FALSE;
941   }
942
943   client_handle = gst_wasapi2_client_get_handle (self->loopback_client);
944   if (!client_handle) {
945     GST_ERROR_OBJECT (self, "IAudioClient handle is not available");
946     return FALSE;
947   }
948
949   hr = client_handle->GetMixFormat (&mix_format);
950   if (!gst_wasapi2_result (hr)) {
951     GST_ERROR_OBJECT (self, "Failed to get mix format");
952     return FALSE;
953   }
954
955   hr = gst_wasapi2_ring_buffer_initialize_audio_client (self, client_handle,
956       mix_format, &period, 0, GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER);
957
958   if (!gst_wasapi2_result (hr)) {
959     GST_ERROR_OBJECT (self, "Failed to initialize audio client");
960     return FALSE;
961   }
962
963   hr = client_handle->SetEventHandle (self->loopback_event_handle);
964   if (!gst_wasapi2_result (hr)) {
965     GST_ERROR_OBJECT (self, "Failed to set event handle");
966     return FALSE;
967   }
968
969   hr = client_handle->GetBufferSize (&self->loopback_buffer_size);
970   if (!gst_wasapi2_result (hr)) {
971     GST_ERROR_OBJECT (self, "Failed to query buffer size");
972     return FALSE;
973   }
974
975   hr = client_handle->GetService (IID_PPV_ARGS (&render_client));
976   if (!gst_wasapi2_result (hr)) {
977     GST_ERROR_OBJECT (self, "IAudioRenderClient is unavailable");
978     return FALSE;
979   }
980
981   self->render_client = render_client.Detach ();
982
983   return TRUE;
984 }
985
986 static gboolean
987 gst_wasapi2_ring_buffer_acquire (GstAudioRingBuffer * buf,
988     GstAudioRingBufferSpec * spec)
989 {
990   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
991   IAudioClient *client_handle;
992   HRESULT hr;
993   WAVEFORMATEX *mix_format = nullptr;
994   ComPtr < ISimpleAudioVolume > audio_volume;
995   GstAudioChannelPosition *position = nullptr;
996   guint period = 0;
997
998   GST_DEBUG_OBJECT (buf, "Acquire");
999
1000   if (!self->client && !gst_wasapi2_ring_buffer_open_device (buf))
1001     return FALSE;
1002
1003   if (gst_wasapi2_device_class_is_loopback (self->device_class)) {
1004     if (!gst_wasapi2_ring_buffer_prepare_loopback_client (self)) {
1005       GST_ERROR_OBJECT (self, "Failed to prepare loopback client");
1006       goto error;
1007     }
1008   }
1009
1010   if (!gst_wasapi2_client_ensure_activation (self->client)) {
1011     GST_ERROR_OBJECT (self, "Failed to activate audio client");
1012     goto error;
1013   }
1014
1015   client_handle = gst_wasapi2_client_get_handle (self->client);
1016   if (!client_handle) {
1017     GST_ERROR_OBJECT (self, "IAudioClient handle is not available");
1018     goto error;
1019   }
1020
1021   /* TODO: convert given caps to mix format */
1022   hr = client_handle->GetMixFormat (&mix_format);
1023   if (!gst_wasapi2_result (hr)) {
1024     if (gst_wasapi2_device_class_is_process_loopback (self->device_class)) {
1025       mix_format = gst_wasapi2_get_default_mix_format ();
1026     } else {
1027       GST_ERROR_OBJECT (self, "Failed to get mix format");
1028       goto error;
1029     }
1030   }
1031
1032   /* Only use audioclient3 when low-latency is requested because otherwise
1033    * very slow machines and VMs with 1 CPU allocated will get glitches:
1034    * https://bugzilla.gnome.org/show_bug.cgi?id=794497 */
1035   hr = E_FAIL;
1036   if (self->low_latency &&
1037       /* AUDCLNT_STREAMFLAGS_LOOPBACK is not allowed for
1038        * InitializeSharedAudioStream */
1039       !gst_wasapi2_device_class_is_loopback (self->device_class) &&
1040       !gst_wasapi2_device_class_is_process_loopback (self->device_class)) {
1041     hr = gst_wasapi2_ring_buffer_initialize_audio_client3 (self, client_handle,
1042         mix_format, &period);
1043   }
1044
1045   /* Try again if IAudioClinet3 API is unavailable.
1046    * NOTE: IAudioClinet3:: methods might not be available for default device
1047    * NOTE: The default device is a special device which is needed for supporting
1048    * automatic stream routing
1049    * https://docs.microsoft.com/en-us/windows/win32/coreaudio/automatic-stream-routing
1050    */
1051   if (FAILED (hr)) {
1052     DWORD extra_flags = 0;
1053     if (gst_wasapi2_device_class_is_loopback (self->device_class))
1054       extra_flags = AUDCLNT_STREAMFLAGS_LOOPBACK;
1055
1056     hr = gst_wasapi2_ring_buffer_initialize_audio_client (self, client_handle,
1057         mix_format, &period, extra_flags, self->device_class);
1058   }
1059
1060   if (!gst_wasapi2_result (hr)) {
1061     GST_ERROR_OBJECT (self, "Failed to initialize audio client");
1062     goto error;
1063   }
1064
1065   hr = client_handle->SetEventHandle (self->event_handle);
1066   if (!gst_wasapi2_result (hr)) {
1067     GST_ERROR_OBJECT (self, "Failed to set event handle");
1068     goto error;
1069   }
1070
1071   gst_wasapi2_util_waveformatex_to_channel_mask (mix_format, &position);
1072   if (position)
1073     gst_audio_ring_buffer_set_channel_positions (buf, position);
1074   g_free (position);
1075
1076   CoTaskMemFree (mix_format);
1077
1078   if (!gst_wasapi2_result (hr)) {
1079     GST_ERROR_OBJECT (self, "Failed to init audio client");
1080     goto error;
1081   }
1082
1083   hr = client_handle->GetBufferSize (&self->buffer_size);
1084   if (!gst_wasapi2_result (hr)) {
1085     GST_ERROR_OBJECT (self, "Failed to query buffer size");
1086     goto error;
1087   }
1088
1089   g_assert (period > 0);
1090
1091   if (self->buffer_size > period) {
1092     GST_INFO_OBJECT (self, "Updating buffer size %d -> %d", self->buffer_size,
1093         period);
1094     self->buffer_size = period;
1095   }
1096
1097   spec->segsize = period * GST_AUDIO_INFO_BPF (&buf->spec.info);
1098   spec->segtotal = 2;
1099
1100   GST_INFO_OBJECT (self,
1101       "Buffer size: %d frames, period: %d frames, segsize: %d bytes",
1102       self->buffer_size, period, spec->segsize);
1103
1104   if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) {
1105     ComPtr < IAudioRenderClient > render_client;
1106
1107     hr = client_handle->GetService (IID_PPV_ARGS (&render_client));
1108     if (!gst_wasapi2_result (hr)) {
1109       GST_ERROR_OBJECT (self, "IAudioRenderClient is unavailable");
1110       goto error;
1111     }
1112
1113     self->render_client = render_client.Detach ();
1114   } else {
1115     ComPtr < IAudioCaptureClient > capture_client;
1116
1117     hr = client_handle->GetService (IID_PPV_ARGS (&capture_client));
1118     if (!gst_wasapi2_result (hr)) {
1119       GST_ERROR_OBJECT (self, "IAudioCaptureClient is unavailable");
1120       goto error;
1121     }
1122
1123     self->capture_client = capture_client.Detach ();
1124   }
1125
1126   hr = client_handle->GetService (IID_PPV_ARGS (&audio_volume));
1127   if (!gst_wasapi2_result (hr)) {
1128     GST_WARNING_OBJECT (self, "ISimpleAudioVolume is unavailable");
1129   } else {
1130     g_mutex_lock (&self->volume_lock);
1131     self->volume_object = audio_volume.Detach ();
1132
1133     if (self->mute_changed) {
1134       self->volume_object->SetMute (self->mute, nullptr);
1135       self->mute_changed = FALSE;
1136     } else {
1137       self->volume_object->SetMute (FALSE, nullptr);
1138     }
1139
1140     if (self->volume_changed) {
1141       self->volume_object->SetMasterVolume (self->volume, nullptr);
1142       self->volume_changed = FALSE;
1143     }
1144     g_mutex_unlock (&self->volume_lock);
1145   }
1146
1147   buf->size = spec->segtotal * spec->segsize;
1148   buf->memory = (guint8 *) g_malloc (buf->size);
1149   gst_audio_format_info_fill_silence (buf->spec.info.finfo,
1150       buf->memory, buf->size);
1151
1152   return TRUE;
1153
1154 error:
1155   GST_WASAPI2_CLEAR_COM (self->render_client);
1156   GST_WASAPI2_CLEAR_COM (self->capture_client);
1157   GST_WASAPI2_CLEAR_COM (self->volume_object);
1158
1159   gst_wasapi2_ring_buffer_post_open_error (self);
1160
1161   return FALSE;
1162 }
1163
1164 static gboolean
1165 gst_wasapi2_ring_buffer_release (GstAudioRingBuffer * buf)
1166 {
1167   GST_DEBUG_OBJECT (buf, "Release");
1168
1169   g_clear_pointer (&buf->memory, g_free);
1170
1171   /* IAudioClient handle is not reusable once it's initialized */
1172   gst_wasapi2_ring_buffer_close_device_internal (buf);
1173
1174   return TRUE;
1175 }
1176
1177 static gboolean
1178 gst_wasapi2_ring_buffer_start_internal (GstWasapi2RingBuffer * self)
1179 {
1180   IAudioClient *client_handle;
1181   HRESULT hr;
1182
1183   if (self->running) {
1184     GST_INFO_OBJECT (self, "We are running already");
1185     return TRUE;
1186   }
1187
1188   client_handle = gst_wasapi2_client_get_handle (self->client);
1189   self->is_first = TRUE;
1190   self->running = TRUE;
1191   self->segoffset = 0;
1192   self->write_frame_offset = 0;
1193
1194   switch (self->device_class) {
1195     case GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER:
1196       /* render client might read data from buffer immediately once it's prepared.
1197        * Pre-fill with silence in order to start-up glitch */
1198       hr = gst_wasapi2_ring_buffer_write (self, TRUE);
1199       if (!gst_wasapi2_result (hr)) {
1200         GST_ERROR_OBJECT (self, "Failed to pre-fill buffer with silence");
1201         goto error;
1202       }
1203       break;
1204     case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE:
1205     {
1206       IAudioClient *loopback_client_handle;
1207
1208       /* Start silence feed client first */
1209       loopback_client_handle =
1210           gst_wasapi2_client_get_handle (self->loopback_client);
1211
1212       hr = loopback_client_handle->Start ();
1213       if (!gst_wasapi2_result (hr)) {
1214         GST_ERROR_OBJECT (self, "Failed to start loopback client");
1215         self->running = FALSE;
1216         goto error;
1217       }
1218
1219       hr = MFPutWaitingWorkItem (self->loopback_event_handle,
1220           0, self->loopback_callback_result, &self->loopback_callback_key);
1221       if (!gst_wasapi2_result (hr)) {
1222         GST_ERROR_OBJECT (self, "Failed to put waiting item");
1223         loopback_client_handle->Stop ();
1224         self->running = FALSE;
1225         goto error;
1226       }
1227       break;
1228     }
1229     default:
1230       break;
1231   }
1232
1233   hr = client_handle->Start ();
1234   if (!gst_wasapi2_result (hr)) {
1235     GST_ERROR_OBJECT (self, "Failed to start client");
1236     self->running = FALSE;
1237     goto error;
1238   }
1239
1240   if (self->device_class != GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE) {
1241     hr = MFPutWaitingWorkItem (self->event_handle, 0, self->callback_result,
1242         &self->callback_key);
1243     if (!gst_wasapi2_result (hr)) {
1244       GST_ERROR_OBJECT (self, "Failed to put waiting item");
1245       client_handle->Stop ();
1246       self->running = FALSE;
1247       goto error;
1248     }
1249   }
1250
1251   return TRUE;
1252
1253 error:
1254   gst_wasapi2_ring_buffer_post_open_error (self);
1255   return FALSE;
1256 }
1257
1258 static gboolean
1259 gst_wasapi2_ring_buffer_start (GstAudioRingBuffer * buf)
1260 {
1261   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1262
1263   GST_DEBUG_OBJECT (self, "Start");
1264
1265   return gst_wasapi2_ring_buffer_start_internal (self);
1266 }
1267
1268 static gboolean
1269 gst_wasapi2_ring_buffer_resume (GstAudioRingBuffer * buf)
1270 {
1271   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1272
1273   GST_DEBUG_OBJECT (self, "Resume");
1274
1275   return gst_wasapi2_ring_buffer_start_internal (self);
1276 }
1277
1278 static gboolean
1279 gst_wasapi2_ring_buffer_stop_internal (GstWasapi2RingBuffer * self)
1280 {
1281   IAudioClient *client_handle;
1282   HRESULT hr;
1283
1284   if (!self->client) {
1285     GST_DEBUG_OBJECT (self, "No configured client");
1286     return TRUE;
1287   }
1288
1289   if (!self->running) {
1290     GST_DEBUG_OBJECT (self, "We are not running");
1291     return TRUE;
1292   }
1293
1294   client_handle = gst_wasapi2_client_get_handle (self->client);
1295
1296   self->running = FALSE;
1297   MFCancelWorkItem (self->callback_key);
1298
1299   hr = client_handle->Stop ();
1300   gst_wasapi2_result (hr);
1301
1302   /* Call reset for later reuse case */
1303   hr = client_handle->Reset ();
1304   self->expected_position = 0;
1305   self->write_frame_offset = 0;
1306
1307   if (self->loopback_client) {
1308     client_handle = gst_wasapi2_client_get_handle (self->loopback_client);
1309
1310     MFCancelWorkItem (self->loopback_callback_key);
1311
1312     hr = client_handle->Stop ();
1313     gst_wasapi2_result (hr);
1314
1315     client_handle->Reset ();
1316   }
1317
1318   return TRUE;
1319 }
1320
1321 static gboolean
1322 gst_wasapi2_ring_buffer_stop (GstAudioRingBuffer * buf)
1323 {
1324   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1325
1326   GST_DEBUG_OBJECT (buf, "Stop");
1327
1328   return gst_wasapi2_ring_buffer_stop_internal (self);
1329 }
1330
1331 static gboolean
1332 gst_wasapi2_ring_buffer_pause (GstAudioRingBuffer * buf)
1333 {
1334   GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1335
1336   GST_DEBUG_OBJECT (buf, "Pause");
1337
1338   return gst_wasapi2_ring_buffer_stop_internal (self);
1339 }
1340
1341 static guint
1342 gst_wasapi2_ring_buffer_delay (GstAudioRingBuffer * buf)
1343 {
1344   /* NOTE: WASAPI supports GetCurrentPadding() method for querying
1345    * currently unread buffer size, but it doesn't seem to be quite useful
1346    * here because:
1347    *
1348    * In case of capture client, GetCurrentPadding() will return the number of
1349    * unread frames which will be identical to pNumFramesToRead value of
1350    * IAudioCaptureClient::GetBuffer()'s return. Since we are running on
1351    * event-driven mode and whenever available, WASAPI will notify signal
1352    * so it's likely zero at this moment. And there is a chance to
1353    * return incorrect value here because our IO callback happens from
1354    * other thread.
1355    *
1356    * And render client's padding size will return the total size of buffer
1357    * which is likely larger than twice of our period. Which doesn't represent
1358    * the amount queued frame size in device correctly
1359    */
1360   return 0;
1361 }
1362
1363 GstAudioRingBuffer *
1364 gst_wasapi2_ring_buffer_new (GstWasapi2ClientDeviceClass device_class,
1365     gboolean low_latency, const gchar * device_id, gpointer dispatcher,
1366     const gchar * name, guint loopback_target_pid)
1367 {
1368   GstWasapi2RingBuffer *self;
1369
1370   self = (GstWasapi2RingBuffer *)
1371       g_object_new (GST_TYPE_WASAPI2_RING_BUFFER, "name", name, nullptr);
1372
1373   if (!self->callback_object) {
1374     gst_object_unref (self);
1375     return nullptr;
1376   }
1377
1378   self->device_class = device_class;
1379   self->low_latency = low_latency;
1380   self->device_id = g_strdup (device_id);
1381   self->dispatcher = dispatcher;
1382   self->loopback_target_pid = loopback_target_pid;
1383
1384   return GST_AUDIO_RING_BUFFER_CAST (self);
1385 }
1386
1387 GstCaps *
1388 gst_wasapi2_ring_buffer_get_caps (GstWasapi2RingBuffer * buf)
1389 {
1390   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), nullptr);
1391
1392   if (buf->supported_caps)
1393     return gst_caps_ref (buf->supported_caps);
1394
1395   if (!buf->client)
1396     return nullptr;
1397
1398   if (!gst_wasapi2_client_ensure_activation (buf->client)) {
1399     GST_ERROR_OBJECT (buf, "Failed to activate audio client");
1400     return nullptr;
1401   }
1402
1403   buf->supported_caps = gst_wasapi2_client_get_caps (buf->client);
1404   if (buf->supported_caps)
1405     return gst_caps_ref (buf->supported_caps);
1406
1407   return nullptr;
1408 }
1409
1410 HRESULT
1411 gst_wasapi2_ring_buffer_set_mute (GstWasapi2RingBuffer * buf, gboolean mute)
1412 {
1413   HRESULT hr = S_OK;
1414   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1415
1416   g_mutex_lock (&buf->volume_lock);
1417   buf->mute = mute;
1418   if (buf->volume_object)
1419     hr = buf->volume_object->SetMute (mute, nullptr);
1420   else
1421     buf->mute_changed = TRUE;
1422   g_mutex_unlock (&buf->volume_lock);
1423
1424   return S_OK;
1425 }
1426
1427 HRESULT
1428 gst_wasapi2_ring_buffer_get_mute (GstWasapi2RingBuffer * buf, gboolean * mute)
1429 {
1430   BOOL mute_val;
1431   HRESULT hr = S_OK;
1432
1433   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1434   g_return_val_if_fail (mute != nullptr, E_INVALIDARG);
1435
1436   mute_val = buf->mute;
1437
1438   g_mutex_lock (&buf->volume_lock);
1439   if (buf->volume_object)
1440     hr = buf->volume_object->GetMute (&mute_val);
1441   g_mutex_unlock (&buf->volume_lock);
1442
1443   *mute = mute_val ? TRUE : FALSE;
1444
1445   return hr;
1446 }
1447
1448 HRESULT
1449 gst_wasapi2_ring_buffer_set_volume (GstWasapi2RingBuffer * buf, gfloat volume)
1450 {
1451   HRESULT hr = S_OK;
1452
1453   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1454   g_return_val_if_fail (volume >= 0 && volume <= 1.0, E_INVALIDARG);
1455
1456   g_mutex_lock (&buf->volume_lock);
1457   buf->volume = volume;
1458   if (buf->volume_object)
1459     hr = buf->volume_object->SetMasterVolume (volume, nullptr);
1460   else
1461     buf->volume_changed = TRUE;
1462   g_mutex_unlock (&buf->volume_lock);
1463
1464   return hr;
1465 }
1466
1467 HRESULT
1468 gst_wasapi2_ring_buffer_get_volume (GstWasapi2RingBuffer * buf, gfloat * volume)
1469 {
1470   gfloat volume_val;
1471   HRESULT hr = S_OK;
1472
1473   g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1474   g_return_val_if_fail (volume != nullptr, E_INVALIDARG);
1475
1476   g_mutex_lock (&buf->volume_lock);
1477   volume_val = buf->volume;
1478   if (buf->volume_object)
1479     hr = buf->volume_object->GetMasterVolume (&volume_val);
1480   g_mutex_unlock (&buf->volume_lock);
1481
1482   *volume = volume_val;
1483
1484   return hr;
1485 }