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.
24 #include "gstasioobject.h"
33 GST_DEBUG_CATEGORY_STATIC (gst_asio_object_debug);
34 #define GST_CAT_DEFAULT gst_asio_object_debug
36 /* List of GstAsioObject */
37 static GList *asio_object_list = nullptr;
40 /* Protect asio_object_list and other global values */
41 std::mutex global_lock;
43 /* Protect callback slots */
47 static void gst_asio_object_buffer_switch (GstAsioObject * self,
48 glong index, ASIOBool process_now);
49 static void gst_asio_object_sample_rate_changed (GstAsioObject * self,
51 static glong gst_asio_object_messages (GstAsioObject * self, glong selector,
52 glong value, gpointer message, gdouble * opt);
53 static ASIOTime *gst_asio_object_buffer_switch_time_info (GstAsioObject * self,
54 ASIOTime * time_info, glong index, ASIOBool process_now);
57 /* Object to delegate ASIO callbacks to dedicated GstAsioObject */
58 class GstAsioCallbacks
61 GstAsioCallbacks (GstAsioObject * object)
63 g_weak_ref_init (&object_, object);
66 virtual ~GstAsioCallbacks ()
68 g_weak_ref_clear (&object_);
71 void BufferSwitch (glong index, ASIOBool process_now)
73 GstAsioObject *obj = (GstAsioObject *) g_weak_ref_get (&object_);
77 gst_asio_object_buffer_switch (obj, index, process_now);
78 gst_object_unref (obj);
81 void SampleRateChanged (ASIOSampleRate rate)
83 GstAsioObject *obj = (GstAsioObject *) g_weak_ref_get (&object_);
87 gst_asio_object_sample_rate_changed (obj, rate);
88 gst_object_unref (obj);
91 glong Messages (glong selector, glong value, gpointer message, gdouble *opt)
93 GstAsioObject *obj = (GstAsioObject *) g_weak_ref_get (&object_);
97 glong ret = gst_asio_object_messages (obj, selector, value, message, opt);
98 gst_object_unref (obj);
103 ASIOTime * BufferSwitchTimeInfo (ASIOTime * time_info,
104 glong index, ASIOBool process_now)
106 GstAsioObject *obj = (GstAsioObject *) g_weak_ref_get (&object_);
110 ASIOTime * ret = gst_asio_object_buffer_switch_time_info (obj,
111 time_info, index, process_now);
112 gst_object_unref (obj);
121 template <int instance_id>
122 class GstAsioCallbacksSlot
126 BufferSwitchStatic(glong index, ASIOBool process_now)
128 buffer_switch(index, process_now);
132 SampleRateChangedStatic (ASIOSampleRate rate)
134 sample_rate_changed(rate);
138 MessagesStatic(glong selector, glong value, gpointer message, gdouble *opt)
140 return messages(selector, value, message, opt);
144 BufferSwitchTimeInfoStatic(ASIOTime * time_info, glong index,
145 ASIOBool process_now)
147 return buffer_switch_time_info(time_info, index, process_now);
150 static std::function<void(glong, ASIOBool)> buffer_switch;
151 static std::function<void(ASIOSampleRate)> sample_rate_changed;
152 static std::function<glong(glong, glong, gpointer, gdouble *)> messages;
153 static std::function<ASIOTime *(ASIOTime *, glong, ASIOBool)> buffer_switch_time_info;
159 buffer_switch = nullptr;
160 sample_rate_changed = nullptr;
162 buffer_switch_time_info = nullptr;
166 static bool IsBound ()
171 static void Bind (GstAsioCallbacks * cb, ASIOCallbacks * driver_cb)
173 buffer_switch = std::bind(&GstAsioCallbacks::BufferSwitch, cb,
174 std::placeholders::_1, std::placeholders::_2);
175 sample_rate_changed = std::bind(&GstAsioCallbacks::SampleRateChanged, cb,
176 std::placeholders::_1);
177 messages = std::bind(&GstAsioCallbacks::Messages, cb,
178 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
179 std::placeholders::_4);
180 buffer_switch_time_info = std::bind(&GstAsioCallbacks::BufferSwitchTimeInfo,
181 cb, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
183 driver_cb->bufferSwitch = BufferSwitchStatic;
184 driver_cb->sampleRateDidChange = SampleRateChangedStatic;
185 driver_cb->asioMessage = MessagesStatic;
186 driver_cb->bufferSwitchTimeInfo = BufferSwitchTimeInfoStatic;
192 template <int instance_id>
193 std::function<void(glong, ASIOBool)> GstAsioCallbacksSlot<instance_id>::buffer_switch;
194 template <int instance_id>
195 std::function<void(ASIOSampleRate)> GstAsioCallbacksSlot<instance_id>::sample_rate_changed;
196 template <int instance_id>
197 std::function<glong(glong, glong, gpointer, gdouble *)> GstAsioCallbacksSlot<instance_id>::messages;
198 template <int instance_id>
199 std::function<ASIOTime *(ASIOTime *, glong, ASIOBool)> GstAsioCallbacksSlot<instance_id>::buffer_switch_time_info;
200 template <int instance_id>
201 bool GstAsioCallbacksSlot<instance_id>::bound;
203 /* XXX: Create global slot objects,
204 * because ASIO callback doesn't support user data, hum.... */
205 GstAsioCallbacksSlot<0> cb_slot_0;
206 GstAsioCallbacksSlot<1> cb_slot_1;
207 GstAsioCallbacksSlot<2> cb_slot_2;
208 GstAsioCallbacksSlot<3> cb_slot_3;
209 GstAsioCallbacksSlot<4> cb_slot_4;
210 GstAsioCallbacksSlot<5> cb_slot_5;
211 GstAsioCallbacksSlot<6> cb_slot_6;
212 GstAsioCallbacksSlot<7> cb_slot_7;
218 GstAsioObjectCallbacks callbacks;
220 } GstAsioObjectCallbacksPrivate;
230 GST_ASIO_OBJECT_STATE_LOADED,
231 GST_ASIO_OBJECT_STATE_INITIALIZED,
232 GST_ASIO_OBJECT_STATE_PREPARED,
233 GST_ASIO_OBJECT_STATE_RUNNING,
234 } GstAsioObjectState;
236 /* Protect singletone object */
237 struct _GstAsioObject
241 GstAsioDeviceInfo *device_info;
243 GstAsioObjectState state;
250 GMainContext *context;
258 /* called after init() done */
259 glong max_num_input_channels;
260 glong max_num_output_channels;
262 glong min_buffer_size;
263 glong max_buffer_size;
264 glong preferred_buffer_size;
265 glong buffer_size_granularity;
267 glong selected_buffer_size;
269 /* List of supported sample rate */
270 GArray *supported_sample_rates;
272 /* List of ASIOChannelInfo */
273 ASIOChannelInfo *input_channel_infos;
274 ASIOChannelInfo *output_channel_infos;
276 /* Selected sample rate */
277 ASIOSampleRate sample_rate;
279 /* Input/Output buffer infors */
280 ASIOBufferInfo *buffer_infos;
282 /* Store requested channel before createbuffer */
283 gboolean *input_channel_requested;
284 gboolean *output_channel_requested;
286 glong num_requested_input_channels;
287 glong num_requested_output_channels;
288 guint num_allocated_buffers;
290 GList *src_client_callbacks;
291 GList *sink_client_callbacks;
292 GList *loopback_client_callbacks;
293 guint64 next_callback_id;
295 GstAsioCallbacks *callbacks;
296 ASIOCallbacks driver_callbacks;
299 gboolean occupy_all_channels;
302 static void gst_asio_object_constructed (GObject * object);
303 static void gst_asio_object_finalize (GObject * object);
304 static void gst_asio_object_set_property (GObject * object, guint prop_id,
305 const GValue * value, GParamSpec * pspec);
307 static gpointer gst_asio_object_thread_func (GstAsioObject * self);
309 #define gst_asio_object_parent_class parent_class
310 G_DEFINE_TYPE (GstAsioObject, gst_asio_object, GST_TYPE_OBJECT);
313 gst_asio_object_class_init (GstAsioObjectClass * klass)
315 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
317 gobject_class->constructed = gst_asio_object_constructed;
318 gobject_class->finalize = gst_asio_object_finalize;
319 gobject_class->set_property = gst_asio_object_set_property;
321 g_object_class_install_property (gobject_class, PROP_DEVICE_INFO,
322 g_param_spec_pointer ("device-info", "Device Info",
323 "A pointer to GstAsioDeviceInfo struct",
324 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
325 G_PARAM_STATIC_STRINGS)));
327 GST_DEBUG_CATEGORY_INIT (gst_asio_object_debug,
328 "asioobject", 0, "asioobject");
332 gst_asio_object_init (GstAsioObject * self)
334 g_mutex_init (&self->lock);
335 g_cond_init (&self->cond);
337 g_mutex_init (&self->thread_lock);
338 g_cond_init (&self->thread_cond);
340 g_mutex_init (&self->api_lock);
342 self->supported_sample_rates = g_array_new (FALSE,
343 FALSE, sizeof (ASIOSampleRate));
349 gst_asio_object_constructed (GObject * object)
351 GstAsioObject *self = GST_ASIO_OBJECT (object);
353 if (!self->device_info) {
354 GST_ERROR_OBJECT (self, "Device info was not configured");
358 self->context = g_main_context_new ();
359 self->loop = g_main_loop_new (self->context, FALSE);
361 g_mutex_lock (&self->lock);
362 self->thread = g_thread_new ("GstAsioObject",
363 (GThreadFunc) gst_asio_object_thread_func, self);
364 while (!g_main_loop_is_running (self->loop))
365 g_cond_wait (&self->cond, &self->lock);
366 g_mutex_unlock (&self->lock);
370 gst_asio_object_finalize (GObject * object)
372 GstAsioObject *self = GST_ASIO_OBJECT (object);
375 g_main_loop_quit (self->loop);
376 g_thread_join (self->thread);
377 g_main_loop_unref (self->loop);
378 g_main_context_unref (self->context);
381 g_mutex_clear (&self->lock);
382 g_cond_clear (&self->cond);
384 g_mutex_clear (&self->thread_lock);
385 g_cond_clear (&self->thread_cond);
387 g_mutex_clear (&self->api_lock);
389 g_array_unref (self->supported_sample_rates);
391 gst_asio_device_info_free (self->device_info);
392 g_free (self->input_channel_infos);
393 g_free (self->output_channel_infos);
394 g_free (self->input_channel_requested);
395 g_free (self->output_channel_requested);
397 if (self->src_client_callbacks)
398 g_list_free_full (self->src_client_callbacks, (GDestroyNotify) g_free);
399 if (self->sink_client_callbacks)
400 g_list_free_full (self->sink_client_callbacks, (GDestroyNotify) g_free);
401 if (self->loopback_client_callbacks)
402 g_list_free_full (self->loopback_client_callbacks, (GDestroyNotify) g_free);
404 G_OBJECT_CLASS (parent_class)->finalize (object);
408 gst_asio_object_set_property (GObject * object, guint prop_id,
409 const GValue * value, GParamSpec * pspec)
411 GstAsioObject *self = GST_ASIO_OBJECT (object);
414 case PROP_DEVICE_INFO:
415 g_clear_pointer (&self->device_info, gst_asio_device_info_free);
416 self->device_info = gst_asio_device_info_copy ((GstAsioDeviceInfo *)
417 g_value_get_pointer (value));
420 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
426 gst_asio_object_create_internal_hwnd (GstAsioObject * self)
430 HINSTANCE hinstance = GetModuleHandle (NULL);
432 atom = GetClassInfoExW (hinstance, L"GstAsioInternalWindow", &wc);
434 GST_LOG_OBJECT (self, "Register internal window class");
435 ZeroMemory (&wc, sizeof (WNDCLASSEX));
437 wc.cbSize = sizeof (WNDCLASSEX);
438 wc.lpfnWndProc = DefWindowProc;
439 wc.hInstance = GetModuleHandle (nullptr);
441 wc.lpszClassName = L"GstAsioInternalWindow";
443 atom = RegisterClassExW (&wc);
446 GST_ERROR_OBJECT (self, "Failed to register window class 0x%x",
447 (unsigned int) GetLastError ());
452 return CreateWindowExW (0, L"GstAsioInternalWindow", L"GstAsioInternal",
453 WS_POPUP, 0, 0, 1, 1, nullptr, nullptr, GetModuleHandle (nullptr),
458 hwnd_msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
462 if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
463 return G_SOURCE_CONTINUE;
465 TranslateMessage (&msg);
466 DispatchMessage (&msg);
468 return G_SOURCE_CONTINUE;
472 gst_asio_object_main_loop_running_cb (GstAsioObject * self)
474 GST_INFO_OBJECT (self, "Main loop running now");
476 g_mutex_lock (&self->lock);
477 g_cond_signal (&self->cond);
478 g_mutex_unlock (&self->lock);
480 return G_SOURCE_REMOVE;
484 gst_asio_object_bind_callbacks (GstAsioObject * self)
486 std::lock_guard < std::mutex > lk (slot_lock);
489 if (!cb_slot_0.IsBound ()) {
490 cb_slot_0.Bind (self->callbacks, &self->driver_callbacks);
492 } else if (!cb_slot_1.IsBound ()) {
493 cb_slot_1.Bind (self->callbacks, &self->driver_callbacks);
495 } else if (!cb_slot_2.IsBound ()) {
496 cb_slot_2.Bind (self->callbacks, &self->driver_callbacks);
498 } else if (!cb_slot_3.IsBound ()) {
499 cb_slot_3.Bind (self->callbacks, &self->driver_callbacks);
501 } else if (!cb_slot_4.IsBound ()) {
502 cb_slot_4.Bind (self->callbacks, &self->driver_callbacks);
504 } else if (!cb_slot_5.IsBound ()) {
505 cb_slot_5.Bind (self->callbacks, &self->driver_callbacks);
507 } else if (!cb_slot_6.IsBound ()) {
508 cb_slot_6.Bind (self->callbacks, &self->driver_callbacks);
510 } else if (!cb_slot_7.IsBound ()) {
511 cb_slot_7.Bind (self->callbacks, &self->driver_callbacks);
522 gst_asio_object_unbind_callbacks (GstAsioObject * self)
524 std::lock_guard < std::mutex > lk (slot_lock);
526 if (!self->callbacks || self->slot_id < 0)
529 switch (self->slot_id) {
555 g_assert_not_reached ();
563 gst_asio_object_thread_func (GstAsioObject * self)
565 HANDLE avrt_handle = nullptr;
566 static DWORD task_idx = 0;
568 GSource *source = nullptr;
569 GSource *hwnd_msg_source = nullptr;
570 GIOChannel *msg_io_channel = nullptr;
573 IASIO *asio_handle = nullptr;
574 GstAsioDeviceInfo *device_info = self->device_info;
575 /* FIXME: check more sample rate */
576 static ASIOSampleRate sample_rate_to_check[] = {
577 48000.0, 44100.0, 192000.0, 96000.0, 88200.0,
580 g_assert (device_info);
582 GST_INFO_OBJECT (self,
583 "Enter loop, ThreadingModel: %s, driver-name: %s, driver-desc: %s",
584 device_info->sta_model ? "STA" : "MTA",
585 GST_STR_NULL (device_info->driver_name),
586 GST_STR_NULL (device_info->driver_desc));
588 if (device_info->sta_model)
589 CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
591 CoInitializeEx (NULL, COINIT_MULTITHREADED);
593 /* Our thread is unlikely different from driver's working thread though,
594 * let's do this. It should not cause any problem */
595 AvSetMmThreadCharacteristicsW (L"Pro Audio", &task_idx);
596 g_main_context_push_thread_default (self->context);
598 source = g_idle_source_new ();
599 g_source_set_callback (source,
600 (GSourceFunc) gst_asio_object_main_loop_running_cb, self, nullptr);
601 g_source_attach (source, self->context);
602 g_source_unref (source);
604 /* XXX: not sure why ASIO API wants Windows handle for init().
605 * Possibly it might be used for STA COM threading
606 * but it's undocummented... */
607 hwnd = gst_asio_object_create_internal_hwnd (self);
611 hr = CoCreateInstance (device_info->clsid, nullptr, CLSCTX_INPROC_SERVER,
612 device_info->clsid, (gpointer *) & asio_handle);
614 GST_WARNING_OBJECT (self, "Failed to create IASIO instance, hr: 0x%x",
619 if (!asio_handle->init (hwnd)) {
620 GST_WARNING_OBJECT (self, "Failed to init IASIO instance");
621 asio_handle->Release ();
622 asio_handle = nullptr;
626 /* Query device information */
627 asio_rst = asio_handle->getChannels (&self->max_num_input_channels,
628 &self->max_num_output_channels);
630 GST_WARNING_OBJECT (self, "Failed to query in/out channels, ret %ld",
632 asio_handle->Release ();
633 asio_handle = nullptr;
637 GST_INFO_OBJECT (self, "Input/Output channles: %ld/%ld",
638 self->max_num_input_channels, self->max_num_output_channels);
640 asio_rst = asio_handle->getBufferSize (&self->min_buffer_size,
641 &self->max_buffer_size, &self->preferred_buffer_size,
642 &self->buffer_size_granularity);
644 GST_WARNING_OBJECT (self, "Failed to get buffer size, ret %ld", asio_rst);
645 asio_handle->Release ();
646 asio_handle = nullptr;
650 /* Use preferreed buffer size by default */
651 self->selected_buffer_size = self->preferred_buffer_size;
653 GST_INFO_OBJECT (self, "min-buffer-size %ld, max-buffer-size %ld, "
654 "preferred-buffer-size %ld, buffer-size-granularity %ld",
655 self->min_buffer_size, self->max_buffer_size,
656 self->preferred_buffer_size, self->buffer_size_granularity);
658 for (guint i = 0; i < G_N_ELEMENTS (sample_rate_to_check); i++) {
659 asio_rst = asio_handle->canSampleRate (sample_rate_to_check[i]);
663 GST_INFO_OBJECT (self, "SampleRate %.1lf is supported",
664 sample_rate_to_check[i]);
665 g_array_append_val (self->supported_sample_rates, sample_rate_to_check[i]);
668 if (self->supported_sample_rates->len == 0) {
669 GST_WARNING_OBJECT (self, "Failed to query supported sample rate");
670 asio_handle->Release ();
671 asio_handle = nullptr;
675 /* Pick the first supported samplerate */
677 g_array_index (self->supported_sample_rates, ASIOSampleRate, 0);
678 if (asio_handle->setSampleRate (self->sample_rate) != 0) {
679 GST_WARNING_OBJECT (self, "Failed to set samplerate %.1lf",
681 asio_handle->Release ();
682 asio_handle = nullptr;
686 if (self->max_num_input_channels > 0) {
687 self->input_channel_infos = g_new0 (ASIOChannelInfo,
688 self->max_num_input_channels);
689 for (glong i = 0; i < self->max_num_input_channels; i++) {
690 ASIOChannelInfo *info = &self->input_channel_infos[i];
692 info->isInput = TRUE;
694 asio_rst = asio_handle->getChannelInfo (info);
696 GST_WARNING_OBJECT (self, "Failed to %ld input channel info, ret %ld",
698 asio_handle->Release ();
699 asio_handle = nullptr;
703 GST_INFO_OBJECT (self,
704 "InputChannelInfo %ld: isActive %s, channelGroup %ld, "
705 "ASIOSampleType %ld, name %s", i, info->isActive ? "true" : "false",
706 info->channelGroup, info->type, GST_STR_NULL (info->name));
709 self->input_channel_requested =
710 g_new0 (gboolean, self->max_num_input_channels);
713 if (self->max_num_output_channels > 0) {
714 self->output_channel_infos = g_new0 (ASIOChannelInfo,
715 self->max_num_output_channels);
716 for (glong i = 0; i < self->max_num_output_channels; i++) {
717 ASIOChannelInfo *info = &self->output_channel_infos[i];
719 info->isInput = FALSE;
721 asio_rst = asio_handle->getChannelInfo (info);
723 GST_WARNING_OBJECT (self, "Failed to %ld output channel info, ret %ld",
725 asio_handle->Release ();
726 asio_handle = nullptr;
730 GST_INFO_OBJECT (self,
731 "OutputChannelInfo %ld: isActive %s, channelGroup %ld, "
732 "ASIOSampleType %ld, name %s", i, info->isActive ? "true" : "false",
733 info->channelGroup, info->type, GST_STR_NULL (info->name));
736 self->output_channel_requested =
737 g_new0 (gboolean, self->max_num_input_channels);
740 asio_rst = asio_handle->getSampleRate (&self->sample_rate);
742 GST_WARNING_OBJECT (self,
743 "Failed to get current samplerate, ret %ld", asio_rst);
744 asio_handle->Release ();
745 asio_handle = nullptr;
749 GST_INFO_OBJECT (self, "Current samplerate %.1lf", self->sample_rate);
751 self->callbacks = new GstAsioCallbacks (self);
752 if (!gst_asio_object_bind_callbacks (self)) {
753 GST_ERROR_OBJECT (self, "Failed to bind callback to slot");
754 delete self->callbacks;
755 self->callbacks = nullptr;
757 asio_handle->Release ();
758 asio_handle = nullptr;
762 msg_io_channel = g_io_channel_win32_new_messages ((guintptr) hwnd);
763 hwnd_msg_source = g_io_create_watch (msg_io_channel, G_IO_IN);
764 g_source_set_callback (hwnd_msg_source, (GSourceFunc) hwnd_msg_cb,
765 self->context, nullptr);
766 g_source_attach (hwnd_msg_source, self->context);
768 self->state = GST_ASIO_OBJECT_STATE_INITIALIZED;
769 self->asio_handle = asio_handle;
772 g_main_loop_run (self->loop);
774 if (self->asio_handle) {
775 if (self->state > GST_ASIO_OBJECT_STATE_PREPARED)
776 self->asio_handle->stop ();
778 if (self->state > GST_ASIO_OBJECT_STATE_INITIALIZED)
779 self->asio_handle->disposeBuffers ();
782 gst_asio_object_unbind_callbacks (self);
783 if (self->callbacks) {
784 delete self->callbacks;
785 self->callbacks = nullptr;
788 if (hwnd_msg_source) {
789 g_source_destroy (hwnd_msg_source);
790 g_source_unref (hwnd_msg_source);
794 g_io_channel_unref (msg_io_channel);
797 DestroyWindow (hwnd);
799 g_main_context_pop_thread_default (self->context);
802 AvRevertMmThreadCharacteristics (avrt_handle);
805 asio_handle->Release ();
806 asio_handle = nullptr;
811 GST_INFO_OBJECT (self, "Exit loop");
817 gst_asio_object_weak_ref_notify (gpointer data, GstAsioObject * object)
819 std::lock_guard < std::mutex > lk (global_lock);
820 asio_object_list = g_list_remove (asio_object_list, object);
824 gst_asio_object_new (const GstAsioDeviceInfo * info,
825 gboolean occupy_all_channels)
827 GstAsioObject *self = nullptr;
829 std::lock_guard < std::mutex > lk (global_lock);
831 g_return_val_if_fail (info != nullptr, nullptr);
833 /* Check if we have object corresponding to CLSID, and if so return
834 * already existing object instead of allocating new one */
835 for (iter = asio_object_list; iter; iter = g_list_next (iter)) {
836 GstAsioObject *object = (GstAsioObject *) iter->data;
838 if (object->device_info->clsid == info->clsid) {
839 GST_DEBUG_OBJECT (object, "Found configured ASIO object");
840 self = (GstAsioObject *) gst_object_ref (object);
848 self = (GstAsioObject *) g_object_new (GST_TYPE_ASIO_OBJECT,
849 "device-info", info, nullptr);
851 if (!self->asio_handle) {
852 GST_WARNING_OBJECT (self, "ASIO handle is not available");
853 gst_object_unref (self);
858 self->occupy_all_channels = occupy_all_channels;
860 gst_object_ref_sink (self);
862 g_object_weak_ref (G_OBJECT (self),
863 (GWeakNotify) gst_asio_object_weak_ref_notify, nullptr);
864 asio_object_list = g_list_append (asio_object_list, self);
870 gst_asio_object_create_caps_from_channel_info (GstAsioObject * self,
871 ASIOChannelInfo * info, guint min_num_channels, guint max_num_channels)
874 std::string caps_str;
876 const gchar *fmt_str;
879 g_assert (max_num_channels >= min_num_channels);
881 fmt = gst_asio_sample_type_to_gst (info->type);
882 if (fmt == GST_AUDIO_FORMAT_UNKNOWN) {
883 GST_ERROR_OBJECT (self, "Unknown format");
887 fmt_str = gst_audio_format_to_string (fmt);
889 /* Actually we are non-interleaved, but element will interlave data */
890 caps_str = "audio/x-raw, layout = (string) interleaved, ";
891 caps_str += "format = (string) " + std::string (fmt_str) + ", ";
892 /* use fixated sample rate, otherwise get_caps/set_sample_rate() might
893 * be racy in case that multiple sink/src are used */
895 "rate = (int) " + std::to_string ((gint) self->sample_rate) + ", ";
897 if (max_num_channels == min_num_channels)
898 caps_str += "channels = (int) " + std::to_string (max_num_channels);
900 caps_str += "channels = (int) [ " + std::to_string (min_num_channels) +
901 ", " + std::to_string (max_num_channels) + " ]";
903 caps = gst_caps_from_string (caps_str.c_str ());
905 GST_ERROR_OBJECT (self, "Failed to create caps");
909 GST_DEBUG_OBJECT (self, "Create caps %" GST_PTR_FORMAT, caps);
914 /* FIXME: assuming all channels has the same format but it might not be true? */
916 gst_asio_object_get_caps (GstAsioObject * obj, GstAsioDeviceClassType type,
917 guint min_num_channels, guint max_num_channels)
919 ASIOChannelInfo *infos;
921 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), nullptr);
923 if (type == GST_ASIO_DEVICE_CLASS_CAPTURE) {
924 if (obj->max_num_input_channels == 0) {
925 GST_WARNING_OBJECT (obj, "Device doesn't support input");
929 /* max_num_channels == 0 means [1, max-allowed-channles] */
930 if (max_num_channels > 0) {
931 if (max_num_channels > obj->max_num_input_channels) {
932 GST_WARNING_OBJECT (obj, "Too many max channels");
936 max_num_channels = obj->max_num_input_channels;
939 if (min_num_channels > 0) {
940 if (min_num_channels > obj->max_num_input_channels) {
941 GST_WARNING_OBJECT (obj, "Too many min channels");
945 min_num_channels = 1;
948 infos = obj->input_channel_infos;
950 if (obj->max_num_output_channels == 0) {
951 GST_WARNING_OBJECT (obj, "Device doesn't support output");
955 /* max_num_channels == 0 means [1, max-allowed-channles] */
956 if (max_num_channels > 0) {
957 if (max_num_channels > obj->max_num_output_channels) {
958 GST_WARNING_OBJECT (obj, "Too many max channels");
962 max_num_channels = obj->max_num_output_channels;
965 if (min_num_channels > 0) {
966 if (min_num_channels > obj->max_num_output_channels) {
967 GST_WARNING_OBJECT (obj, "Too many min channels");
971 min_num_channels = 1;
974 infos = obj->output_channel_infos;
977 return gst_asio_object_create_caps_from_channel_info (obj,
978 infos, min_num_channels, max_num_channels);
982 gst_asio_object_get_max_num_channels (GstAsioObject * obj, glong * num_input_ch,
983 glong * num_output_ch)
985 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
988 *num_input_ch = obj->max_num_input_channels;
990 *num_output_ch = obj->max_num_output_channels;
996 gst_asio_object_get_buffer_size (GstAsioObject * obj, glong * min_size,
997 glong * max_size, glong * preferred_size, glong * granularity)
999 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1002 *min_size = obj->min_buffer_size;
1004 *max_size = obj->max_buffer_size;
1006 *preferred_size = obj->preferred_buffer_size;
1008 *granularity = obj->buffer_size_granularity;
1013 typedef void (*GstAsioObjectThreadFunc) (GstAsioObject * obj, gpointer data);
1017 GstAsioObject *self;
1018 GstAsioObjectThreadFunc func;
1021 } GstAsioObjectThreadRunData;
1024 gst_asio_object_thread_run_func (GstAsioObjectThreadRunData * data)
1026 GstAsioObject *self = data->self;
1029 data->func (self, data->data);
1031 g_mutex_lock (&self->thread_lock);
1033 g_cond_broadcast (&self->thread_cond);
1034 g_mutex_unlock (&self->thread_lock);
1036 return G_SOURCE_REMOVE;
1040 gst_asio_object_thread_add (GstAsioObject * self, GstAsioObjectThreadFunc func,
1043 GstAsioObjectThreadRunData thread_data;
1045 g_return_if_fail (GST_IS_ASIO_OBJECT (self));
1047 thread_data.self = self;
1048 thread_data.func = func;
1049 thread_data.data = data;
1050 thread_data.fired = FALSE;
1052 g_main_context_invoke (self->context,
1053 (GSourceFunc) gst_asio_object_thread_run_func, &thread_data);
1055 g_mutex_lock (&self->thread_lock);
1056 while (!thread_data.fired)
1057 g_cond_wait (&self->thread_cond, &self->thread_lock);
1058 g_mutex_unlock (&self->thread_lock);
1062 gst_asio_object_validate_channels (GstAsioObject * self, gboolean is_input,
1063 guint * channel_indices, guint num_channels)
1066 if (self->max_num_input_channels < num_channels) {
1067 GST_WARNING_OBJECT (self, "%d exceeds max input channels %ld",
1068 num_channels, self->max_num_input_channels);
1072 for (guint i = 0; i < num_channels; i++) {
1073 guint ch = channel_indices[i];
1074 if (self->max_num_input_channels <= ch) {
1075 GST_WARNING_OBJECT (self, "%d exceeds max input channels %ld",
1076 ch, self->max_num_input_channels);
1082 if (self->max_num_output_channels < num_channels) {
1083 GST_WARNING_OBJECT (self, "%d exceeds max output channels %ld",
1084 num_channels, self->max_num_output_channels);
1089 for (guint i = 0; i < num_channels; i++) {
1090 guint ch = channel_indices[i];
1091 if (self->max_num_output_channels <= ch) {
1092 GST_WARNING_OBJECT (self, "%d exceeds max output channels %ld",
1093 ch, self->max_num_output_channels);
1104 gst_asio_object_check_buffer_reuse (GstAsioObject * self, ASIOBool is_input,
1105 guint * channel_indices, guint num_channels)
1107 guint num_found = 0;
1109 g_assert (self->buffer_infos);
1110 g_assert (self->num_allocated_buffers > 0);
1112 for (guint i = 0; i < self->num_allocated_buffers; i++) {
1113 ASIOBufferInfo *info = &self->buffer_infos[i];
1115 if (info->isInput != is_input)
1118 for (guint j = 0; j < num_channels; j++) {
1119 if (info->channelNum == channel_indices[j]) {
1127 return num_found == num_channels;
1131 gst_asio_object_dispose_buffers_async (GstAsioObject * self, ASIOError * rst)
1133 g_assert (self->asio_handle);
1136 *rst = self->asio_handle->disposeBuffers ();
1140 gst_asio_object_dispose_buffers (GstAsioObject * self)
1143 g_assert (self->asio_handle);
1145 if (!self->buffer_infos)
1148 if (!self->device_info->sta_model) {
1149 rst = self->asio_handle->disposeBuffers ();
1151 gst_asio_object_thread_add (self,
1152 (GstAsioObjectThreadFunc) gst_asio_object_dispose_buffers_async, &rst);
1155 g_clear_pointer (&self->buffer_infos, g_free);
1156 self->num_allocated_buffers = 0;
1162 gst_asio_object_create_buffers_real (GstAsioObject * self, glong * buffer_size)
1166 g_assert (buffer_size);
1168 err = self->asio_handle->createBuffers (self->buffer_infos,
1169 self->num_requested_input_channels + self->num_requested_output_channels,
1170 *buffer_size, &self->driver_callbacks);
1172 /* It failed and buffer size is not equal to preferred size,
1173 * try again with preferred size */
1174 if (err != 0 && *buffer_size != self->preferred_buffer_size) {
1175 GST_WARNING_OBJECT (self,
1176 "Failed to create buffer with buffer size %ld, try again with %ld",
1177 *buffer_size, self->preferred_buffer_size);
1179 err = self->asio_handle->createBuffers (self->buffer_infos,
1180 self->num_requested_input_channels +
1181 self->num_requested_output_channels, self->preferred_buffer_size,
1182 &self->driver_callbacks);
1185 *buffer_size = self->preferred_buffer_size;
1196 } CreateBuffersAsyncData;
1199 gst_asio_object_create_buffers_async (GstAsioObject * self,
1200 CreateBuffersAsyncData * data)
1202 data->err = gst_asio_object_create_buffers_real (self, &data->buffer_size);
1206 gst_asio_object_create_buffers_internal (GstAsioObject * self,
1207 glong * buffer_size)
1210 g_assert (self->asio_handle);
1212 if (!self->device_info->sta_model) {
1213 err = gst_asio_object_create_buffers_real (self, buffer_size);
1215 CreateBuffersAsyncData data;
1216 data.buffer_size = *buffer_size;
1218 gst_asio_object_thread_add (self,
1219 (GstAsioObjectThreadFunc) gst_asio_object_create_buffers_async, &data);
1222 *buffer_size = data.buffer_size;
1229 gst_asio_object_create_buffers (GstAsioObject * obj,
1230 GstAsioDeviceClassType type,
1231 guint * channel_indices, guint num_channels, guint * buffer_size)
1233 gboolean can_reuse = FALSE;
1236 glong prev_buf_size = 0;
1239 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1240 g_return_val_if_fail (channel_indices != nullptr, FALSE);
1241 g_return_val_if_fail (num_channels > 0, FALSE);
1243 GST_DEBUG_OBJECT (obj, "Create buffers");
1245 if (type == GST_ASIO_DEVICE_CLASS_CAPTURE)
1250 g_mutex_lock (&obj->api_lock);
1251 if (!gst_asio_object_validate_channels (obj, is_src, channel_indices,
1253 GST_ERROR_OBJECT (obj, "Invalid request");
1254 g_mutex_unlock (&obj->api_lock);
1259 if (obj->buffer_infos) {
1260 GST_DEBUG_OBJECT (obj,
1261 "Have configured buffer infors, checking whether we can reuse it");
1262 can_reuse = gst_asio_object_check_buffer_reuse (obj,
1263 is_src ? TRUE : FALSE, channel_indices, num_channels);
1267 GST_DEBUG_OBJECT (obj, "We can reuse already allocated buffers");
1269 *buffer_size = obj->selected_buffer_size;
1271 g_mutex_unlock (&obj->api_lock);
1276 /* Cannot re-allocated buffers once started... */
1277 if (obj->state > GST_ASIO_OBJECT_STATE_PREPARED) {
1278 GST_WARNING_OBJECT (obj, "We are running already");
1279 g_mutex_unlock (&obj->api_lock);
1284 /* Use already configured buffer size */
1285 if (obj->buffer_infos)
1286 prev_buf_size = obj->selected_buffer_size;
1288 /* If we have configured buffers, dispose and re-allocate */
1289 if (!gst_asio_object_dispose_buffers (obj)) {
1290 GST_ERROR_OBJECT (obj, "Failed to dispose buffers");
1292 obj->state = GST_ASIO_OBJECT_STATE_INITIALIZED;
1294 g_mutex_unlock (&obj->api_lock);
1298 if (obj->occupy_all_channels) {
1299 GST_INFO_OBJECT (obj,
1300 "occupy-all-channels mode, will allocate buffers for all channels");
1301 /* In this case, we will allocate buffer for all available input/output
1302 * channles, regardless of what requested here */
1303 for (guint i = 0; i < (guint) obj->max_num_input_channels; i++)
1304 obj->input_channel_requested[i] = TRUE;
1305 for (guint i = 0; i < (guint) obj->max_num_output_channels; i++)
1306 obj->output_channel_requested[i] = TRUE;
1308 obj->num_requested_input_channels = obj->max_num_input_channels;
1309 obj->num_requested_output_channels = obj->max_num_output_channels;
1312 for (guint i = 0; i < num_channels; i++) {
1313 guint ch = channel_indices[i];
1315 obj->input_channel_requested[ch] = TRUE;
1318 obj->num_requested_input_channels = 0;
1319 for (guint i = 0; i < obj->max_num_input_channels; i++) {
1320 if (obj->input_channel_requested[i])
1321 obj->num_requested_input_channels++;
1324 for (guint i = 0; i < num_channels; i++) {
1325 guint ch = channel_indices[i];
1327 obj->output_channel_requested[ch] = TRUE;
1330 obj->num_requested_output_channels = 0;
1331 for (guint i = 0; i < obj->max_num_output_channels; i++) {
1332 if (obj->output_channel_requested[i])
1333 obj->num_requested_output_channels++;
1338 obj->num_allocated_buffers = obj->num_requested_input_channels +
1339 obj->num_requested_output_channels;
1341 obj->buffer_infos = g_new0 (ASIOBufferInfo, obj->num_allocated_buffers);
1342 for (i = 0, j = 0; i < obj->num_requested_input_channels; i++) {
1343 ASIOBufferInfo *info = &obj->buffer_infos[i];
1345 info->isInput = TRUE;
1346 while (!obj->input_channel_requested[j])
1349 info->channelNum = j;
1353 for (i = obj->num_requested_input_channels, j = 0;
1355 obj->num_requested_input_channels + obj->num_requested_output_channels;
1357 ASIOBufferInfo *info = &obj->buffer_infos[i];
1359 info->isInput = FALSE;
1360 while (!obj->output_channel_requested[j])
1363 info->channelNum = j;
1367 if (prev_buf_size > 0) {
1368 buf_size = prev_buf_size;
1369 } else if (buffer_size && *buffer_size > 0) {
1370 buf_size = *buffer_size;
1372 buf_size = obj->preferred_buffer_size;
1375 GST_INFO_OBJECT (obj, "Creating buffer with size %ld", buf_size);
1377 if (!gst_asio_object_create_buffers_internal (obj, &buf_size)) {
1378 GST_ERROR_OBJECT (obj, "Failed to create buffers");
1379 g_clear_pointer (&obj->buffer_infos, g_free);
1380 obj->num_allocated_buffers = 0;
1382 obj->state = GST_ASIO_OBJECT_STATE_INITIALIZED;
1384 g_mutex_unlock (&obj->api_lock);
1389 GST_INFO_OBJECT (obj, "Selected buffer size %ld", buf_size);
1391 obj->selected_buffer_size = buf_size;
1393 *buffer_size = buf_size;
1395 obj->state = GST_ASIO_OBJECT_STATE_PREPARED;
1397 g_mutex_unlock (&obj->api_lock);
1409 gst_asio_object_get_latencies_async (GstAsioObject * self, RunAsyncData * data)
1411 data->ret = self->asio_handle->getLatencies (&data->arg[0], &data->arg[1]);
1415 gst_asio_object_get_latencies (GstAsioObject * obj, glong * input_latency,
1416 glong * output_latency)
1418 RunAsyncData data = { 0 };
1421 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1422 g_assert (obj->asio_handle);
1424 if (!obj->device_info->sta_model) {
1425 err = obj->asio_handle->getLatencies (input_latency, output_latency);
1427 gst_asio_object_thread_add (obj,
1428 (GstAsioObjectThreadFunc) gst_asio_object_get_latencies_async, &data);
1430 *input_latency = data.arg[0];
1431 *output_latency = data.arg[1];
1440 ASIOSampleRate sample_rate;
1442 } SampleRateAsyncData;
1445 gst_asio_object_can_sample_rate_async (GstAsioObject * self,
1446 SampleRateAsyncData * data)
1448 data->err = self->asio_handle->canSampleRate (data->sample_rate);
1452 gst_asio_object_can_sample_rate (GstAsioObject * obj,
1453 ASIOSampleRate sample_rate)
1455 SampleRateAsyncData data = { 0 };
1458 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1459 g_assert (obj->asio_handle);
1461 g_mutex_lock (&obj->api_lock);
1462 for (guint i = 0; i < obj->supported_sample_rates->len; i++) {
1463 ASIOSampleRate val = g_array_index (obj->supported_sample_rates,
1465 if (val == sample_rate) {
1466 g_mutex_unlock (&obj->api_lock);
1471 if (!obj->device_info->sta_model) {
1472 err = obj->asio_handle->canSampleRate (sample_rate);
1475 g_array_append_val (obj->supported_sample_rates, sample_rate);
1477 g_mutex_unlock (&obj->api_lock);
1481 data.sample_rate = sample_rate;
1482 gst_asio_object_thread_add (obj,
1483 (GstAsioObjectThreadFunc) gst_asio_object_can_sample_rate_async, &data);
1486 g_array_append_val (obj->supported_sample_rates, sample_rate);
1488 g_mutex_unlock (&obj->api_lock);
1494 gst_asio_object_get_sample_rate (GstAsioObject * obj,
1495 ASIOSampleRate * sample_rate)
1497 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1499 *sample_rate = obj->sample_rate;
1505 gst_asio_object_set_sample_rate_async (GstAsioObject * self,
1506 SampleRateAsyncData * data)
1508 data->err = self->asio_handle->setSampleRate (data->sample_rate);
1510 self->sample_rate = data->sample_rate;
1514 gst_asio_object_set_sample_rate (GstAsioObject * obj,
1515 ASIOSampleRate sample_rate)
1517 SampleRateAsyncData data = { 0 };
1520 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1521 g_assert (obj->asio_handle);
1523 g_mutex_lock (&obj->api_lock);
1524 if (sample_rate == obj->sample_rate) {
1525 g_mutex_unlock (&obj->api_lock);
1529 if (!obj->device_info->sta_model) {
1530 err = obj->asio_handle->setSampleRate (sample_rate);
1532 obj->sample_rate = sample_rate;
1534 g_mutex_unlock (&obj->api_lock);
1538 data.sample_rate = sample_rate;
1539 gst_asio_object_thread_add (obj,
1540 (GstAsioObjectThreadFunc) gst_asio_object_set_sample_rate_async, &data);
1541 g_mutex_unlock (&obj->api_lock);
1547 gst_asio_object_buffer_switch (GstAsioObject * self,
1548 glong index, ASIOBool process_now)
1551 ASIOTime *our_time_info = nullptr;
1554 memset (&time_info, 0, sizeof (ASIOTime));
1557 self->asio_handle->getSamplePosition (&time_info.timeInfo.samplePosition,
1558 &time_info.timeInfo.systemTime);
1560 our_time_info = &time_info;
1562 gst_asio_object_buffer_switch_time_info (self,
1563 our_time_info, index, process_now);
1567 gst_asio_object_sample_rate_changed (GstAsioObject * self, ASIOSampleRate rate)
1569 GST_INFO_OBJECT (self, "SampleRate changed to %lf", rate);
1573 gst_asio_object_messages (GstAsioObject * self,
1574 glong selector, glong value, gpointer message, gdouble * opt)
1576 GST_DEBUG_OBJECT (self, "ASIO message: %ld, %ld", selector, value);
1579 case kAsioSelectorSupported:
1580 if (value == kAsioResetRequest || value == kAsioEngineVersion ||
1581 value == kAsioResyncRequest || value == kAsioLatenciesChanged ||
1582 value == kAsioSupportsTimeCode || value == kAsioSupportsInputMonitor)
1584 else if (value == kAsioSupportsTimeInfo)
1586 GST_WARNING_OBJECT (self, "Unsupported ASIO selector: %li", value);
1588 case kAsioBufferSizeChange:
1589 GST_WARNING_OBJECT (self,
1590 "Unsupported ASIO message: kAsioBufferSizeChange");
1592 case kAsioResetRequest:
1593 GST_WARNING_OBJECT (self, "Unsupported ASIO message: kAsioResetRequest");
1595 case kAsioResyncRequest:
1596 GST_WARNING_OBJECT (self, "Unsupported ASIO message: kAsioResyncRequest");
1598 case kAsioLatenciesChanged:
1599 GST_WARNING_OBJECT (self,
1600 "Unsupported ASIO message: kAsioLatenciesChanged");
1602 case kAsioEngineVersion:
1603 /* We target the ASIO v2 API, which includes ASIOOutputReady() */
1605 case kAsioSupportsTimeInfo:
1606 /* We use the new time info buffer switch callback */
1608 case kAsioSupportsTimeCode:
1609 /* We don't use the time code info right now */
1612 GST_WARNING_OBJECT (self, "Unsupported ASIO message: %li, %li", selector,
1620 #define PACK_ASIO_64(v) ((v).lo | ((guint64)((v).hi) << 32))
1623 gst_asio_object_buffer_switch_time_info (GstAsioObject * self,
1624 ASIOTime * time_info, glong index, ASIOBool process_now)
1630 guint64 system_time;
1632 pos = PACK_ASIO_64 (time_info->timeInfo.samplePosition);
1633 system_time = PACK_ASIO_64 (time_info->timeInfo.systemTime);
1635 GST_TRACE_OBJECT (self, "Sample Position: %" G_GUINT64_FORMAT
1636 ", System Time: %" GST_TIME_FORMAT, pos, GST_TIME_ARGS (system_time));
1639 g_mutex_lock (&self->api_lock);
1640 if (!self->src_client_callbacks && !self->sink_client_callbacks &&
1641 !self->loopback_client_callbacks) {
1642 GST_WARNING_OBJECT (self, "No installed client callback");
1646 for (iter = self->src_client_callbacks; iter;) {
1647 GstAsioObjectCallbacksPrivate *cb =
1648 (GstAsioObjectCallbacksPrivate *) iter->data;
1651 ret = cb->callbacks.buffer_switch (self, index, self->buffer_infos,
1652 self->num_allocated_buffers, self->input_channel_infos,
1653 self->output_channel_infos, self->sample_rate,
1654 self->selected_buffer_size, time_info, cb->callbacks.user_data);
1656 GST_INFO_OBJECT (self, "Remove callback for id %" G_GUINT64_FORMAT,
1658 GList *to_remove = iter;
1659 iter = g_list_next (iter);
1661 g_free (to_remove->data);
1662 g_list_free (to_remove);
1665 iter = g_list_next (iter);
1668 for (iter = self->sink_client_callbacks; iter;) {
1669 GstAsioObjectCallbacksPrivate *cb =
1670 (GstAsioObjectCallbacksPrivate *) iter->data;
1673 ret = cb->callbacks.buffer_switch (self, index, self->buffer_infos,
1674 self->num_allocated_buffers, self->input_channel_infos,
1675 self->output_channel_infos, self->sample_rate,
1676 self->selected_buffer_size, time_info, cb->callbacks.user_data);
1678 GST_INFO_OBJECT (self, "Remove callback for id %" G_GUINT64_FORMAT,
1680 GList *to_remove = iter;
1681 iter = g_list_next (iter);
1683 g_free (to_remove->data);
1684 g_list_free (to_remove);
1687 iter = g_list_next (iter);
1690 for (iter = self->loopback_client_callbacks; iter;) {
1691 GstAsioObjectCallbacksPrivate *cb =
1692 (GstAsioObjectCallbacksPrivate *) iter->data;
1695 ret = cb->callbacks.buffer_switch (self, index, self->buffer_infos,
1696 self->num_allocated_buffers, self->input_channel_infos,
1697 self->output_channel_infos, self->sample_rate,
1698 self->selected_buffer_size, time_info, cb->callbacks.user_data);
1700 GST_INFO_OBJECT (self, "Remove callback for id %" G_GUINT64_FORMAT,
1702 GList *to_remove = iter;
1703 iter = g_list_next (iter);
1705 g_free (to_remove->data);
1706 g_list_free (to_remove);
1709 iter = g_list_next (iter);
1712 self->asio_handle->outputReady ();
1715 g_mutex_unlock (&self->api_lock);
1721 gst_asio_object_start_async (GstAsioObject * self, ASIOError * rst)
1723 *rst = self->asio_handle->start ();
1727 gst_asio_object_start (GstAsioObject * obj)
1731 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1733 g_mutex_lock (&obj->api_lock);
1734 if (obj->state > GST_ASIO_OBJECT_STATE_PREPARED) {
1735 GST_DEBUG_OBJECT (obj, "We are running already");
1736 g_mutex_unlock (&obj->api_lock);
1739 } else if (obj->state < GST_ASIO_OBJECT_STATE_PREPARED) {
1740 GST_ERROR_OBJECT (obj, "We are not prepared");
1741 g_mutex_unlock (&obj->api_lock);
1747 if (!obj->device_info->sta_model) {
1748 ret = obj->asio_handle->start ();
1750 gst_asio_object_thread_add (obj,
1751 (GstAsioObjectThreadFunc) gst_asio_object_start_async, &ret);
1755 GST_ERROR_OBJECT (obj, "Failed to start object");
1756 g_mutex_unlock (&obj->api_lock);
1761 obj->state = GST_ASIO_OBJECT_STATE_RUNNING;
1762 g_mutex_unlock (&obj->api_lock);
1768 gst_asio_object_install_callback (GstAsioObject * obj,
1769 GstAsioDeviceClassType type,
1770 GstAsioObjectCallbacks * callbacks, guint64 * callback_id)
1772 GstAsioObjectCallbacksPrivate *cb;
1774 g_return_val_if_fail (GST_IS_ASIO_OBJECT (obj), FALSE);
1775 g_return_val_if_fail (callbacks != nullptr, FALSE);
1776 g_return_val_if_fail (callback_id != nullptr, FALSE);
1778 g_mutex_lock (&obj->api_lock);
1779 cb = g_new0 (GstAsioObjectCallbacksPrivate, 1);
1780 cb->callbacks = *callbacks;
1781 cb->callback_id = obj->next_callback_id;
1784 case GST_ASIO_DEVICE_CLASS_CAPTURE:
1785 obj->src_client_callbacks = g_list_append (obj->src_client_callbacks, cb);
1787 case GST_ASIO_DEVICE_CLASS_RENDER:
1788 obj->sink_client_callbacks =
1789 g_list_append (obj->sink_client_callbacks, cb);
1791 case GST_ASIO_DEVICE_CLASS_LOOPBACK_CAPTURE:
1792 obj->loopback_client_callbacks =
1793 g_list_append (obj->loopback_client_callbacks, cb);
1796 g_assert_not_reached ();
1801 *callback_id = cb->callback_id;
1802 g_mutex_unlock (&obj->api_lock);
1808 gst_asio_object_uninstall_callback (GstAsioObject * obj, guint64 callback_id)
1812 g_return_if_fail (GST_IS_ASIO_OBJECT (obj));
1814 g_mutex_lock (&obj->api_lock);
1816 GST_DEBUG_OBJECT (obj, "Removing callback id %" G_GUINT64_FORMAT,
1819 for (iter = obj->src_client_callbacks; iter; iter = g_list_next (iter)) {
1820 GstAsioObjectCallbacksPrivate *cb =
1821 (GstAsioObjectCallbacksPrivate *) iter->data;
1823 if (cb->callback_id != callback_id)
1826 GST_DEBUG_OBJECT (obj, "Found src callback for id %" G_GUINT64_FORMAT,
1829 obj->src_client_callbacks =
1830 g_list_remove_link (obj->src_client_callbacks, iter);
1831 g_free (iter->data);
1833 g_mutex_unlock (&obj->api_lock);
1838 for (iter = obj->sink_client_callbacks; iter; iter = g_list_next (iter)) {
1839 GstAsioObjectCallbacksPrivate *cb =
1840 (GstAsioObjectCallbacksPrivate *) iter->data;
1842 if (cb->callback_id != callback_id)
1845 GST_DEBUG_OBJECT (obj, "Found sink callback for id %" G_GUINT64_FORMAT,
1848 obj->sink_client_callbacks =
1849 g_list_remove_link (obj->sink_client_callbacks, iter);
1850 g_free (iter->data);
1852 g_mutex_unlock (&obj->api_lock);
1857 for (iter = obj->loopback_client_callbacks; iter; iter = g_list_next (iter)) {
1858 GstAsioObjectCallbacksPrivate *cb =
1859 (GstAsioObjectCallbacksPrivate *) iter->data;
1861 if (cb->callback_id != callback_id)
1864 GST_DEBUG_OBJECT (obj, "Found loopback callback for id %" G_GUINT64_FORMAT,
1867 obj->loopback_client_callbacks =
1868 g_list_remove_link (obj->loopback_client_callbacks, iter);
1869 g_free (iter->data);
1874 g_mutex_unlock (&obj->api_lock);