2 * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
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.
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.
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.
20 #include "gstwasapi2ringbuffer.h"
25 GST_DEBUG_CATEGORY_STATIC (gst_wasapi2_ring_buffer_debug);
26 #define GST_CAT_DEFAULT gst_wasapi2_ring_buffer_debug
28 static HRESULT gst_wasapi2_ring_buffer_io_callback (GstWasapi2RingBuffer * buf);
30 gst_wasapi2_ring_buffer_loopback_callback (GstWasapi2RingBuffer * buf);
33 using namespace Microsoft::WRL;
35 class GstWasapiAsyncCallback : public IMFAsyncCallback
38 GstWasapiAsyncCallback(GstWasapi2RingBuffer *listener,
45 g_weak_ref_init (&listener_, listener);
48 virtual ~GstWasapiAsyncCallback ()
50 g_weak_ref_set (&listener_, nullptr);
57 GST_TRACE ("%p, %d", this, ref_count_);
58 return InterlockedIncrement (&ref_count_);
65 GST_TRACE ("%p, %d", this, ref_count_);
66 ref_count = InterlockedDecrement (&ref_count_);
69 GST_TRACE ("Delete instance %p", this);
77 QueryInterface (REFIID riid, void ** object)
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));
98 /* IMFAsyncCallback */
100 GetParameters(DWORD * pdwFlags, DWORD * pdwQueue)
103 *pdwQueue = queue_id_;
109 Invoke(IMFAsyncResult * pAsyncResult)
111 GstWasapi2RingBuffer *ringbuffer;
114 ringbuffer = (GstWasapi2RingBuffer *) g_weak_ref_get (&listener_);
116 GST_WARNING ("Listener was removed");
121 hr = gst_wasapi2_ring_buffer_loopback_callback (ringbuffer);
123 hr = gst_wasapi2_ring_buffer_io_callback (ringbuffer);
124 gst_object_unref (ringbuffer);
137 struct _GstWasapi2RingBuffer
139 GstAudioRingBuffer parent;
141 GstWasapi2ClientDeviceClass device_class;
143 gboolean low_latency;
147 gboolean can_auto_routing;
148 guint loopback_target_pid;
150 GstWasapi2Client *client;
151 GstWasapi2Client *loopback_client;
152 IAudioCaptureClient *capture_client;
153 IAudioRenderClient *render_client;
154 ISimpleAudioVolume *volume_object;
156 GstWasapiAsyncCallback *callback_object;
157 IMFAsyncResult *callback_result;
158 MFWORKITEM_KEY callback_key;
161 GstWasapiAsyncCallback *loopback_callback_object;
162 IMFAsyncResult *loopback_callback_result;
163 MFWORKITEM_KEY loopback_callback_key;
164 HANDLE loopback_event_handle;
166 guint64 expected_position;
170 UINT32 loopback_buffer_size;
173 guint64 write_frame_offset;
176 gboolean mute_changed;
177 gboolean volume_changed;
179 GstCaps *supported_caps;
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);
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);
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);
202 gst_wasapi2_ring_buffer_class_init (GstWasapi2RingBufferClass * klass)
204 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
205 GstAudioRingBufferClass *ring_buffer_class =
206 GST_AUDIO_RING_BUFFER_CLASS (klass);
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;
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);
227 GST_DEBUG_CATEGORY_INIT (gst_wasapi2_ring_buffer_debug,
228 "wasapi2ringbuffer", 0, "wasapi2ringbuffer");
232 gst_wasapi2_ring_buffer_init (GstWasapi2RingBuffer * self)
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);
243 gst_wasapi2_ring_buffer_constructed (GObject * object)
245 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (object);
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");
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);
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);
277 G_OBJECT_CLASS (parent_class)->constructed (object);
281 gst_wasapi2_ring_buffer_dispose (GObject * object)
283 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (object);
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);
293 gst_clear_object (&self->client);
294 gst_clear_object (&self->loopback_client);
295 gst_clear_caps (&self->supported_caps);
297 G_OBJECT_CLASS (parent_class)->dispose (object);
301 gst_wasapi2_ring_buffer_finalize (GObject * object)
303 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (object);
305 g_free (self->device_id);
306 CloseHandle (self->event_handle);
307 CloseHandle (self->loopback_event_handle);
308 g_mutex_clear (&self->volume_lock);
310 G_OBJECT_CLASS (parent_class)->finalize (object);
314 gst_wasapi2_ring_buffer_post_open_error (GstWasapi2RingBuffer * self)
316 GstElement *parent = (GstElement *) GST_OBJECT_PARENT (self);
319 GST_WARNING_OBJECT (self, "Cannot find parent");
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"));
327 GST_ELEMENT_ERROR (parent, RESOURCE, OPEN_READ,
328 (nullptr), ("Failed to open device"));
333 gst_wasapi2_ring_buffer_post_scheduling_error (GstWasapi2RingBuffer * self)
335 GstElement *parent = (GstElement *) GST_OBJECT_PARENT (self);
338 GST_WARNING_OBJECT (self, "Cannot find parent");
342 GST_ELEMENT_ERROR (parent, RESOURCE, FAILED,
343 (nullptr), ("Failed to schedule next I/O"));
347 gst_wasapi2_ring_buffer_post_io_error (GstWasapi2RingBuffer * self, HRESULT hr)
349 GstElement *parent = (GstElement *) GST_OBJECT_PARENT (self);
353 GST_WARNING_OBJECT (self, "Cannot find parent");
357 error_msg = gst_wasapi2_util_get_error_message (hr);
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));
364 GST_ELEMENT_ERROR (parent, RESOURCE, READ,
365 ("Failed to read from device"), ("%s hr: 0x%x", error_msg, hr));
372 gst_wasapi2_ring_buffer_open_device (GstAudioRingBuffer * buf)
374 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
376 GST_DEBUG_OBJECT (self, "Open");
379 GST_DEBUG_OBJECT (self, "Already opened");
383 self->client = gst_wasapi2_client_new (self->device_class,
384 -1, self->device_id, self->loopback_target_pid, self->dispatcher);
386 gst_wasapi2_ring_buffer_post_open_error (self);
390 g_object_get (self->client, "auto-routing", &self->can_auto_routing, nullptr);
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);
398 if (!self->loopback_client) {
399 gst_wasapi2_ring_buffer_post_open_error (self);
400 gst_clear_object (&self->client);
410 gst_wasapi2_ring_buffer_close_device_internal (GstAudioRingBuffer * buf)
412 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
414 GST_DEBUG_OBJECT (self, "Close device");
417 gst_wasapi2_ring_buffer_stop (buf);
419 GST_WASAPI2_CLEAR_COM (self->capture_client);
420 GST_WASAPI2_CLEAR_COM (self->render_client);
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);
428 gst_clear_object (&self->client);
429 gst_clear_object (&self->loopback_client);
435 gst_wasapi2_ring_buffer_close_device (GstAudioRingBuffer * buf)
437 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
439 GST_DEBUG_OBJECT (self, "Close");
441 gst_wasapi2_ring_buffer_close_device_internal (buf);
443 gst_clear_caps (&self->supported_caps);
449 gst_wasapi2_ring_buffer_read (GstWasapi2RingBuffer * self)
451 GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
452 BYTE *data = nullptr;
454 guint32 to_read_bytes;
458 GstAudioInfo *info = &ringbuffer->spec.info;
459 IAudioCaptureClient *capture_client = self->capture_client;
466 if (!capture_client) {
467 GST_ERROR_OBJECT (self, "IAudioCaptureClient is not available");
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");
478 to_read_bytes = to_read * GST_AUDIO_INFO_BPF (info);
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);
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;
490 if (position > self->expected_position) {
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);
498 self->expected_position = position + to_read;
500 } else if (self->mute) {
501 /* volume clinet might not be available in case of process loopback */
502 flags |= AUDCLNT_BUFFERFLAGS_SILENT;
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");
513 g_assert (self->segoffset >= 0);
515 len -= self->segoffset;
519 gst_audio_format_info_fill_silence (ringbuffer->spec.info.finfo,
520 readptr + self->segoffset, len);
522 self->segoffset += len;
525 if (self->segoffset == ringbuffer->spec.segsize) {
526 gst_audio_ring_buffer_advance (ringbuffer, 1);
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");
538 len -= self->segoffset;
539 if (len > to_read_bytes)
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);
546 memcpy (readptr + self->segoffset, data + offset, len);
549 self->segoffset += len;
551 to_read_bytes -= len;
553 if (self->segoffset == ringbuffer->spec.segsize) {
554 gst_audio_ring_buffer_advance (ringbuffer, 1);
560 hr = capture_client->ReleaseBuffer (to_read);
562 gst_wasapi2_result (hr);
568 gst_wasapi2_ring_buffer_write (GstWasapi2RingBuffer * self, gboolean preroll)
570 GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
572 IAudioClient *client_handle;
573 IAudioRenderClient *render_client;
574 guint32 padding_frames = 0;
576 guint32 can_write_bytes;
580 BYTE *data = nullptr;
582 client_handle = gst_wasapi2_client_get_handle (self->client);
583 if (!client_handle) {
584 GST_ERROR_OBJECT (self, "IAudioClient is not available");
588 render_client = self->render_client;
589 if (!render_client) {
590 GST_ERROR_OBJECT (self, "IAudioRenderClient is not available");
594 hr = client_handle->GetCurrentPadding (&padding_frames);
595 if (!gst_wasapi2_result (hr))
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);
605 can_write = self->buffer_size - padding_frames;
606 can_write_bytes = can_write * GST_AUDIO_INFO_BPF (&ringbuffer->spec.info);
608 GST_INFO_OBJECT (self, "Pre-fill %d frames with silence", can_write);
610 hr = render_client->GetBuffer (can_write, &data);
611 if (!gst_wasapi2_result (hr))
614 hr = render_client->ReleaseBuffer (can_write, AUDCLNT_BUFFERFLAGS_SILENT);
615 return gst_wasapi2_result (hr);
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;
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");
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
630 hr = render_client->GetBuffer (can_write, &data);
631 if (!gst_wasapi2_result (hr))
634 hr = render_client->ReleaseBuffer (can_write, AUDCLNT_BUFFERFLAGS_SILENT);
636 gst_wasapi2_result (hr);
640 len -= self->segoffset;
642 if (len > can_write_bytes)
643 len = can_write_bytes;
645 can_write = len / GST_AUDIO_INFO_BPF (&ringbuffer->spec.info);
649 hr = render_client->GetBuffer (can_write, &data);
650 if (!gst_wasapi2_result (hr))
653 memcpy (data, readptr + self->segoffset, len);
654 hr = render_client->ReleaseBuffer (can_write, 0);
656 self->segoffset += len;
657 can_write_bytes -= len;
659 if (self->segoffset == ringbuffer->spec.segsize) {
660 gst_audio_ring_buffer_clear (ringbuffer, segment);
661 gst_audio_ring_buffer_advance (ringbuffer, 1);
665 if (!gst_wasapi2_result (hr)) {
666 GST_WARNING_OBJECT (self, "Failed to release buffer");
675 gst_wasapi2_ring_buffer_io_callback (GstWasapi2RingBuffer * self)
679 g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (self), E_FAIL);
681 if (!self->running) {
682 GST_INFO_OBJECT (self, "We are not running now");
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);
693 case GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER:
694 hr = gst_wasapi2_ring_buffer_write (self, FALSE);
697 g_assert_not_reached ();
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
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");
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);
723 if (!gst_wasapi2_result (hr)) {
724 GST_ERROR_OBJECT (self, "Failed to put item");
725 gst_wasapi2_ring_buffer_post_scheduling_error (self);
731 GST_INFO_OBJECT (self, "We are not running now");
736 gst_wasapi2_ring_buffer_post_io_error (self, hr);
742 gst_wasapi2_ring_buffer_fill_loopback_silence (GstWasapi2RingBuffer * self)
745 IAudioClient *client_handle;
746 IAudioRenderClient *render_client;
747 guint32 padding_frames = 0;
749 BYTE *data = nullptr;
751 client_handle = gst_wasapi2_client_get_handle (self->loopback_client);
752 if (!client_handle) {
753 GST_ERROR_OBJECT (self, "IAudioClient is not available");
757 render_client = self->render_client;
758 if (!render_client) {
759 GST_ERROR_OBJECT (self, "IAudioRenderClient is not available");
763 hr = client_handle->GetCurrentPadding (&padding_frames);
764 if (!gst_wasapi2_result (hr))
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);
774 can_write = self->loopback_buffer_size - padding_frames;
776 GST_TRACE_OBJECT (self, "Writing %d silent frames", can_write);
778 hr = render_client->GetBuffer (can_write, &data);
779 if (!gst_wasapi2_result (hr))
782 hr = render_client->ReleaseBuffer (can_write, AUDCLNT_BUFFERFLAGS_SILENT);
783 return gst_wasapi2_result (hr);
787 gst_wasapi2_ring_buffer_loopback_callback (GstWasapi2RingBuffer * self)
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);
795 if (!self->running) {
796 GST_INFO_OBJECT (self, "We are not running now");
800 hr = gst_wasapi2_ring_buffer_fill_loopback_silence (self);
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
805 if (gst_wasapi2_result (hr))
806 hr = gst_wasapi2_ring_buffer_io_callback (self);
809 if (gst_wasapi2_result (hr)) {
810 hr = MFPutWaitingWorkItem (self->loopback_event_handle, 0,
811 self->loopback_callback_result, &self->loopback_callback_key);
813 if (!gst_wasapi2_result (hr)) {
814 GST_ERROR_OBJECT (self, "Failed to put item");
815 gst_wasapi2_ring_buffer_post_scheduling_error (self);
821 GST_INFO_OBJECT (self, "We are not running now");
826 gst_wasapi2_ring_buffer_post_io_error (self, hr);
832 gst_wasapi2_ring_buffer_initialize_audio_client3 (GstWasapi2RingBuffer * self,
833 IAudioClient * client_handle, WAVEFORMATEX * mix_format, guint * period)
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;
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");
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");
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);
859 *period = min_period;
861 hr = audio_client->InitializeSharedAudioStream (stream_flags, min_period,
862 mix_format, nullptr);
864 if (!gst_wasapi2_result (hr))
865 GST_WARNING_OBJECT (self, "Failed to initialize IAudioClient3");
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)
875 GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
876 REFERENCE_TIME default_period, min_period;
878 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST;
881 stream_flags |= extra_flags;
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");
890 GST_INFO_OBJECT (self, "wasapi2 default period: %" G_GINT64_FORMAT
891 ", min period: %" G_GINT64_FORMAT, default_period, min_period);
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 */
899 /* This must always be 0 in shared mode */
900 0, mix_format, nullptr);
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
906 default_period = (20 * GST_MSECOND) / 100;
907 hr = client_handle->Initialize (AUDCLNT_SHAREMODE_SHARED,
908 AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
910 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM, mix_format, nullptr);
913 if (!gst_wasapi2_result (hr)) {
914 GST_WARNING_OBJECT (self, "Couldn't initialize audioclient");
918 *period = gst_util_uint64_scale_round (default_period * 100,
919 GST_AUDIO_INFO_RATE (&ringbuffer->spec.info), GST_SECOND);
925 gst_wasapi2_ring_buffer_prepare_loopback_client (GstWasapi2RingBuffer * self)
927 IAudioClient *client_handle;
929 WAVEFORMATEX *mix_format = nullptr;
931 ComPtr < IAudioRenderClient > render_client;
933 if (!self->loopback_client) {
934 GST_ERROR_OBJECT (self, "No configured client object");
938 if (!gst_wasapi2_client_ensure_activation (self->loopback_client)) {
939 GST_ERROR_OBJECT (self, "Failed to activate audio client");
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");
949 hr = client_handle->GetMixFormat (&mix_format);
950 if (!gst_wasapi2_result (hr)) {
951 GST_ERROR_OBJECT (self, "Failed to get mix format");
955 hr = gst_wasapi2_ring_buffer_initialize_audio_client (self, client_handle,
956 mix_format, &period, 0, GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER);
958 if (!gst_wasapi2_result (hr)) {
959 GST_ERROR_OBJECT (self, "Failed to initialize audio client");
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");
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");
975 hr = client_handle->GetService (IID_PPV_ARGS (&render_client));
976 if (!gst_wasapi2_result (hr)) {
977 GST_ERROR_OBJECT (self, "IAudioRenderClient is unavailable");
981 self->render_client = render_client.Detach ();
987 gst_wasapi2_ring_buffer_acquire (GstAudioRingBuffer * buf,
988 GstAudioRingBufferSpec * spec)
990 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
991 IAudioClient *client_handle;
993 WAVEFORMATEX *mix_format = nullptr;
994 ComPtr < ISimpleAudioVolume > audio_volume;
995 GstAudioChannelPosition *position = nullptr;
998 GST_DEBUG_OBJECT (buf, "Acquire");
1000 if (!self->client && !gst_wasapi2_ring_buffer_open_device (buf))
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");
1010 if (!gst_wasapi2_client_ensure_activation (self->client)) {
1011 GST_ERROR_OBJECT (self, "Failed to activate audio client");
1015 client_handle = gst_wasapi2_client_get_handle (self->client);
1016 if (!client_handle) {
1017 GST_ERROR_OBJECT (self, "IAudioClient handle is not available");
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 ();
1027 GST_ERROR_OBJECT (self, "Failed to get mix format");
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 */
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);
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
1052 DWORD extra_flags = 0;
1053 if (gst_wasapi2_device_class_is_loopback (self->device_class))
1054 extra_flags = AUDCLNT_STREAMFLAGS_LOOPBACK;
1056 hr = gst_wasapi2_ring_buffer_initialize_audio_client (self, client_handle,
1057 mix_format, &period, extra_flags, self->device_class);
1060 if (!gst_wasapi2_result (hr)) {
1061 GST_ERROR_OBJECT (self, "Failed to initialize audio client");
1065 hr = client_handle->SetEventHandle (self->event_handle);
1066 if (!gst_wasapi2_result (hr)) {
1067 GST_ERROR_OBJECT (self, "Failed to set event handle");
1071 gst_wasapi2_util_waveformatex_to_channel_mask (mix_format, &position);
1073 gst_audio_ring_buffer_set_channel_positions (buf, position);
1076 CoTaskMemFree (mix_format);
1078 if (!gst_wasapi2_result (hr)) {
1079 GST_ERROR_OBJECT (self, "Failed to init audio client");
1083 hr = client_handle->GetBufferSize (&self->buffer_size);
1084 if (!gst_wasapi2_result (hr)) {
1085 GST_ERROR_OBJECT (self, "Failed to query buffer size");
1089 g_assert (period > 0);
1091 if (self->buffer_size > period) {
1092 GST_INFO_OBJECT (self, "Updating buffer size %d -> %d", self->buffer_size,
1094 self->buffer_size = period;
1097 spec->segsize = period * GST_AUDIO_INFO_BPF (&buf->spec.info);
1100 GST_INFO_OBJECT (self,
1101 "Buffer size: %d frames, period: %d frames, segsize: %d bytes",
1102 self->buffer_size, period, spec->segsize);
1104 if (self->device_class == GST_WASAPI2_CLIENT_DEVICE_CLASS_RENDER) {
1105 ComPtr < IAudioRenderClient > render_client;
1107 hr = client_handle->GetService (IID_PPV_ARGS (&render_client));
1108 if (!gst_wasapi2_result (hr)) {
1109 GST_ERROR_OBJECT (self, "IAudioRenderClient is unavailable");
1113 self->render_client = render_client.Detach ();
1115 ComPtr < IAudioCaptureClient > capture_client;
1117 hr = client_handle->GetService (IID_PPV_ARGS (&capture_client));
1118 if (!gst_wasapi2_result (hr)) {
1119 GST_ERROR_OBJECT (self, "IAudioCaptureClient is unavailable");
1123 self->capture_client = capture_client.Detach ();
1126 hr = client_handle->GetService (IID_PPV_ARGS (&audio_volume));
1127 if (!gst_wasapi2_result (hr)) {
1128 GST_WARNING_OBJECT (self, "ISimpleAudioVolume is unavailable");
1130 g_mutex_lock (&self->volume_lock);
1131 self->volume_object = audio_volume.Detach ();
1133 if (self->mute_changed) {
1134 self->volume_object->SetMute (self->mute, nullptr);
1135 self->mute_changed = FALSE;
1137 self->volume_object->SetMute (FALSE, nullptr);
1140 if (self->volume_changed) {
1141 self->volume_object->SetMasterVolume (self->volume, nullptr);
1142 self->volume_changed = FALSE;
1144 g_mutex_unlock (&self->volume_lock);
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);
1155 GST_WASAPI2_CLEAR_COM (self->render_client);
1156 GST_WASAPI2_CLEAR_COM (self->capture_client);
1157 GST_WASAPI2_CLEAR_COM (self->volume_object);
1159 gst_wasapi2_ring_buffer_post_open_error (self);
1165 gst_wasapi2_ring_buffer_release (GstAudioRingBuffer * buf)
1167 GST_DEBUG_OBJECT (buf, "Release");
1169 g_clear_pointer (&buf->memory, g_free);
1171 /* IAudioClient handle is not reusable once it's initialized */
1172 gst_wasapi2_ring_buffer_close_device_internal (buf);
1178 gst_wasapi2_ring_buffer_start_internal (GstWasapi2RingBuffer * self)
1180 IAudioClient *client_handle;
1183 if (self->running) {
1184 GST_INFO_OBJECT (self, "We are running already");
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;
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");
1204 case GST_WASAPI2_CLIENT_DEVICE_CLASS_LOOPBACK_CAPTURE:
1206 IAudioClient *loopback_client_handle;
1208 /* Start silence feed client first */
1209 loopback_client_handle =
1210 gst_wasapi2_client_get_handle (self->loopback_client);
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;
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;
1233 hr = client_handle->Start ();
1234 if (!gst_wasapi2_result (hr)) {
1235 GST_ERROR_OBJECT (self, "Failed to start client");
1236 self->running = FALSE;
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;
1254 gst_wasapi2_ring_buffer_post_open_error (self);
1259 gst_wasapi2_ring_buffer_start (GstAudioRingBuffer * buf)
1261 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1263 GST_DEBUG_OBJECT (self, "Start");
1265 return gst_wasapi2_ring_buffer_start_internal (self);
1269 gst_wasapi2_ring_buffer_resume (GstAudioRingBuffer * buf)
1271 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1273 GST_DEBUG_OBJECT (self, "Resume");
1275 return gst_wasapi2_ring_buffer_start_internal (self);
1279 gst_wasapi2_ring_buffer_stop_internal (GstWasapi2RingBuffer * self)
1281 IAudioClient *client_handle;
1284 if (!self->client) {
1285 GST_DEBUG_OBJECT (self, "No configured client");
1289 if (!self->running) {
1290 GST_DEBUG_OBJECT (self, "We are not running");
1294 client_handle = gst_wasapi2_client_get_handle (self->client);
1296 self->running = FALSE;
1297 MFCancelWorkItem (self->callback_key);
1299 hr = client_handle->Stop ();
1300 gst_wasapi2_result (hr);
1302 /* Call reset for later reuse case */
1303 hr = client_handle->Reset ();
1304 self->expected_position = 0;
1305 self->write_frame_offset = 0;
1307 if (self->loopback_client) {
1308 client_handle = gst_wasapi2_client_get_handle (self->loopback_client);
1310 MFCancelWorkItem (self->loopback_callback_key);
1312 hr = client_handle->Stop ();
1313 gst_wasapi2_result (hr);
1315 client_handle->Reset ();
1322 gst_wasapi2_ring_buffer_stop (GstAudioRingBuffer * buf)
1324 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1326 GST_DEBUG_OBJECT (buf, "Stop");
1328 return gst_wasapi2_ring_buffer_stop_internal (self);
1332 gst_wasapi2_ring_buffer_pause (GstAudioRingBuffer * buf)
1334 GstWasapi2RingBuffer *self = GST_WASAPI2_RING_BUFFER (buf);
1336 GST_DEBUG_OBJECT (buf, "Pause");
1338 return gst_wasapi2_ring_buffer_stop_internal (self);
1342 gst_wasapi2_ring_buffer_delay (GstAudioRingBuffer * buf)
1344 /* NOTE: WASAPI supports GetCurrentPadding() method for querying
1345 * currently unread buffer size, but it doesn't seem to be quite useful
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
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
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)
1368 GstWasapi2RingBuffer *self;
1370 self = (GstWasapi2RingBuffer *)
1371 g_object_new (GST_TYPE_WASAPI2_RING_BUFFER, "name", name, nullptr);
1373 if (!self->callback_object) {
1374 gst_object_unref (self);
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;
1384 return GST_AUDIO_RING_BUFFER_CAST (self);
1388 gst_wasapi2_ring_buffer_get_caps (GstWasapi2RingBuffer * buf)
1390 g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), nullptr);
1392 if (buf->supported_caps)
1393 return gst_caps_ref (buf->supported_caps);
1398 if (!gst_wasapi2_client_ensure_activation (buf->client)) {
1399 GST_ERROR_OBJECT (buf, "Failed to activate audio client");
1403 buf->supported_caps = gst_wasapi2_client_get_caps (buf->client);
1404 if (buf->supported_caps)
1405 return gst_caps_ref (buf->supported_caps);
1411 gst_wasapi2_ring_buffer_set_mute (GstWasapi2RingBuffer * buf, gboolean mute)
1414 g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1416 g_mutex_lock (&buf->volume_lock);
1418 if (buf->volume_object)
1419 hr = buf->volume_object->SetMute (mute, nullptr);
1421 buf->mute_changed = TRUE;
1422 g_mutex_unlock (&buf->volume_lock);
1428 gst_wasapi2_ring_buffer_get_mute (GstWasapi2RingBuffer * buf, gboolean * mute)
1433 g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1434 g_return_val_if_fail (mute != nullptr, E_INVALIDARG);
1436 mute_val = buf->mute;
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);
1443 *mute = mute_val ? TRUE : FALSE;
1449 gst_wasapi2_ring_buffer_set_volume (GstWasapi2RingBuffer * buf, gfloat volume)
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);
1456 g_mutex_lock (&buf->volume_lock);
1457 buf->volume = volume;
1458 if (buf->volume_object)
1459 hr = buf->volume_object->SetMasterVolume (volume, nullptr);
1461 buf->volume_changed = TRUE;
1462 g_mutex_unlock (&buf->volume_lock);
1468 gst_wasapi2_ring_buffer_get_volume (GstWasapi2RingBuffer * buf, gfloat * volume)
1473 g_return_val_if_fail (GST_IS_WASAPI2_RING_BUFFER (buf), E_INVALIDARG);
1474 g_return_val_if_fail (volume != nullptr, E_INVALIDARG);
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);
1482 *volume = volume_val;