mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 00:36:51 +00:00
Introduce Steinberg ASIO (Audio Streaming Input/Output) plugin
Adds a new plugin for ASIO devices. Although there is a standard low-level audio API, WASAPI, on Windows, ASIO is still being broadly used for audio devices which are aiming to professional use case. In case of such devices, ASIO API might be able to show better quality and latency performance depending on manufacturer's driver implementation. In order to build this plugin, user should provide path to ASIO SDK as a build option, "asio-sdk-path". Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2309>
This commit is contained in:
parent
4d1101d335
commit
dc787434bc
16 changed files with 4085 additions and 0 deletions
|
@ -81,6 +81,8 @@ option('aom', type : 'feature', value : 'auto', description : 'AOM AV1 video cod
|
|||
option('avtp', type : 'feature', value : 'auto', description : 'Audio/Video Transport Protocol (AVTP) plugin')
|
||||
option('androidmedia', type : 'feature', value : 'auto', description : 'Video capture and codec plugins for Android')
|
||||
option('applemedia', type : 'feature', value : 'auto', description : 'Video capture and codec access plugins for macOS and iOS')
|
||||
option('asio', type : 'feature', value : 'auto', description : 'Steinberg Audio Streaming Input Output (ASIO) plugin')
|
||||
option('asio-sdk-path', type : 'string', value : '', description : 'Full path to Steinberg Audio Streaming Input Output (ASIO) SDK')
|
||||
option('assrender', type : 'feature', value : 'auto', description : 'ASS/SSA subtitle renderer plugin')
|
||||
option('bluez', type : 'feature', value : 'auto', description : 'Bluetooth audio A2DP/AVDTP sink, AVDTP source plugin')
|
||||
option('bs2b', type : 'feature', value : 'auto', description : 'Bauer stereophonic-to-binaural audio plugin')
|
||||
|
|
273
sys/asio/gstasiodeviceprovider.cpp
Normal file
273
sys/asio/gstasiodeviceprovider.cpp
Normal file
|
@ -0,0 +1,273 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstasiodeviceprovider.h"
|
||||
#include "gstasioutils.h"
|
||||
#include "gstasioobject.h"
|
||||
#include <atlconv.h>
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_CLSID,
|
||||
};
|
||||
|
||||
struct _GstAsioDevice
|
||||
{
|
||||
GstDevice parent;
|
||||
|
||||
gchar *device_clsid;
|
||||
const gchar *factory_name;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstAsioDevice, gst_asio_device, GST_TYPE_DEVICE);
|
||||
|
||||
static void gst_asio_device_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_asio_device_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_asio_device_finalize (GObject * object);
|
||||
static GstElement *gst_asio_device_create_element (GstDevice * device,
|
||||
const gchar * name);
|
||||
|
||||
static void
|
||||
gst_asio_device_class_init (GstAsioDeviceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
|
||||
|
||||
dev_class->create_element = gst_asio_device_create_element;
|
||||
|
||||
gobject_class->get_property = gst_asio_device_get_property;
|
||||
gobject_class->set_property = gst_asio_device_set_property;
|
||||
gobject_class->finalize = gst_asio_device_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_DEVICE_CLSID,
|
||||
g_param_spec_string ("device-clsid", "Device CLSID",
|
||||
"ASIO device CLSID as string including curly brackets", NULL,
|
||||
(GParamFlags) (G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_device_init (GstAsioDevice * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_device_finalize (GObject * object)
|
||||
{
|
||||
GstAsioDevice *self = GST_ASIO_DEVICE (object);
|
||||
|
||||
g_free (self->device_clsid);
|
||||
|
||||
G_OBJECT_CLASS (gst_asio_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
gst_asio_device_create_element (GstDevice * device, const gchar * name)
|
||||
{
|
||||
GstAsioDevice *self = GST_ASIO_DEVICE (device);
|
||||
GstElement *elem;
|
||||
|
||||
elem = gst_element_factory_make (self->factory_name, name);
|
||||
|
||||
g_object_set (elem, "device-clsid", self->device_clsid, NULL);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_device_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstAsioDevice *self = GST_ASIO_DEVICE (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_CLSID:
|
||||
g_value_set_string (value, self->device_clsid);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_device_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstAsioDevice *self = GST_ASIO_DEVICE (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_CLSID:
|
||||
g_free (self->device_clsid);
|
||||
self->device_clsid = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct _GstAsioDeviceProvider
|
||||
{
|
||||
GstDeviceProvider parent;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstAsioDeviceProvider, gst_asio_device_provider,
|
||||
GST_TYPE_DEVICE_PROVIDER);
|
||||
|
||||
static GList *gst_asio_device_provider_probe (GstDeviceProvider * provider);
|
||||
|
||||
static void
|
||||
gst_asio_device_provider_class_init (GstAsioDeviceProviderClass * klass)
|
||||
{
|
||||
GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS (klass);
|
||||
|
||||
provider_class->probe = GST_DEBUG_FUNCPTR (gst_asio_device_provider_probe);
|
||||
|
||||
gst_device_provider_class_set_static_metadata (provider_class,
|
||||
"ASIO Device Provider",
|
||||
"Source/Sink/Audio", "List ASIO source and sink devices",
|
||||
"Seungha Yang <seungha@centricular.com>");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_device_provider_init (GstAsioDeviceProvider * provider)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_device_provider_probe_internal (GstAsioDeviceProvider * self,
|
||||
gboolean is_src, GList * asio_device_list, GList ** devices)
|
||||
{
|
||||
const gchar *device_class, *factory_name;
|
||||
GList *iter;
|
||||
|
||||
USES_CONVERSION;
|
||||
|
||||
if (is_src) {
|
||||
device_class = "Audio/Source";
|
||||
factory_name = "asiosrc";
|
||||
} else {
|
||||
device_class = "Audio/Sink";
|
||||
factory_name = "asiosink";
|
||||
}
|
||||
|
||||
for (iter = asio_device_list; iter; iter = g_list_next (iter)) {
|
||||
GstDevice *device;
|
||||
GstAsioDeviceInfo *info = (GstAsioDeviceInfo *) iter->data;
|
||||
GstAsioObject *obj;
|
||||
GstCaps *caps = nullptr;
|
||||
GstStructure *props = nullptr;
|
||||
long max_in_ch = 0;
|
||||
long max_out_ch = 0;
|
||||
HRESULT hr;
|
||||
LPOLESTR clsid_str = nullptr;
|
||||
glong min_buf_size = 0;
|
||||
glong max_buf_size = 0;
|
||||
glong preferred_buf_size = 0;
|
||||
glong buf_size_granularity = 0;
|
||||
|
||||
obj = gst_asio_object_new (info, FALSE);
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
if (!gst_asio_object_get_max_num_channels (obj, &max_in_ch, &max_out_ch))
|
||||
goto done;
|
||||
|
||||
if (is_src && max_in_ch <= 0)
|
||||
goto done;
|
||||
else if (!is_src && max_out_ch <= 0)
|
||||
goto done;
|
||||
|
||||
if (is_src) {
|
||||
caps = gst_asio_object_get_caps (obj,
|
||||
GST_ASIO_DEVICE_CLASS_CAPTURE, 1, max_in_ch);
|
||||
} else {
|
||||
caps = gst_asio_object_get_caps (obj,
|
||||
GST_ASIO_DEVICE_CLASS_RENDER, 1, max_out_ch);
|
||||
}
|
||||
if (!caps)
|
||||
goto done;
|
||||
|
||||
hr = StringFromIID (info->clsid, &clsid_str);
|
||||
if (FAILED (hr))
|
||||
goto done;
|
||||
|
||||
if (!gst_asio_object_get_buffer_size (obj, &min_buf_size, &max_buf_size,
|
||||
&preferred_buf_size, &buf_size_granularity))
|
||||
goto done;
|
||||
|
||||
props = gst_structure_new ("asio-proplist",
|
||||
"device.api", G_TYPE_STRING, "asio",
|
||||
"device.clsid", G_TYPE_STRING, OLE2A (clsid_str),
|
||||
"asio.device.description", G_TYPE_STRING, info->driver_desc,
|
||||
"asio.device.min-buf-size", G_TYPE_LONG, min_buf_size,
|
||||
"asio.device.max-buf-size", G_TYPE_LONG, max_buf_size,
|
||||
"asio.device.preferred-buf-size", G_TYPE_LONG, preferred_buf_size,
|
||||
"asio.device.buf-size-granularity", G_TYPE_LONG, buf_size_granularity,
|
||||
nullptr);
|
||||
|
||||
device = (GstDevice *) g_object_new (GST_TYPE_ASIO_DEVICE,
|
||||
"device-clsid", OLE2A (clsid_str),
|
||||
"display-name", info->driver_desc, "caps", caps,
|
||||
"device-class", device_class, "properties", props, nullptr);
|
||||
GST_ASIO_DEVICE (device)->factory_name = factory_name;
|
||||
|
||||
*devices = g_list_append (*devices, device);
|
||||
|
||||
done:
|
||||
gst_clear_caps (&caps);
|
||||
gst_clear_object (&obj);
|
||||
if (props)
|
||||
gst_structure_free (props);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static GList *
|
||||
gst_asio_device_provider_probe (GstDeviceProvider * provider)
|
||||
{
|
||||
GstAsioDeviceProvider *self = GST_ASIO_DEVICE_PROVIDER (provider);
|
||||
GList *devices = nullptr;
|
||||
guint num_device;
|
||||
GList *asio_device_list = nullptr;
|
||||
|
||||
num_device = gst_asio_enum (&asio_device_list);
|
||||
|
||||
if (num_device == 0)
|
||||
return nullptr;
|
||||
|
||||
gst_asio_device_provider_probe_internal (self,
|
||||
TRUE, asio_device_list, &devices);
|
||||
gst_asio_device_provider_probe_internal (self,
|
||||
FALSE, asio_device_list, &devices);
|
||||
|
||||
g_list_free_full (asio_device_list,
|
||||
(GDestroyNotify) gst_asio_device_info_free);
|
||||
|
||||
return devices;
|
||||
}
|
37
sys/asio/gstasiodeviceprovider.h
Normal file
37
sys/asio/gstasiodeviceprovider.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ASIO_DEVICE_PROVIDER_H__
|
||||
#define __GST_ASIO_DEVICE_PROVIDER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ASIO_DEVICE (gst_asio_device_get_type())
|
||||
#define GST_TYPE_ASIO_DEVICE_PROVIDER (gst_asio_device_provider_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GstAsioDevice, gst_asio_device,
|
||||
GST, ASIO_DEVICE, GstDevice);
|
||||
G_DECLARE_FINAL_TYPE (GstAsioDeviceProvider, gst_asio_device_provider,
|
||||
GST, ASIO_DEVICE_PROVIDER, GstDeviceProvider);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ASIO_DEVICE_PROVIDER_H__ */
|
1875
sys/asio/gstasioobject.cpp
Normal file
1875
sys/asio/gstasioobject.cpp
Normal file
File diff suppressed because it is too large
Load diff
104
sys/asio/gstasioobject.h
Normal file
104
sys/asio/gstasioobject.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License aglong with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ASIO_OBJECT_H__
|
||||
#define __GST_ASIO_OBJECT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <windows.h>
|
||||
#include "gstasioutils.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ASIO_OBJECT (gst_asio_object_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstAsioObject, gst_asio_object,
|
||||
GST, ASIO_OBJECT, GstObject);
|
||||
|
||||
typedef struct {
|
||||
gboolean (*buffer_switch) (GstAsioObject * obj,
|
||||
glong index,
|
||||
ASIOBufferInfo * infos,
|
||||
guint num_infos,
|
||||
ASIOChannelInfo * input_channel_infos,
|
||||
ASIOChannelInfo * output_channel_infos,
|
||||
ASIOSampleRate sample_rate,
|
||||
glong buffer_size,
|
||||
ASIOTime * time_info,
|
||||
gpointer user_data);
|
||||
|
||||
gpointer user_data;
|
||||
} GstAsioObjectCallbacks;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_ASIO_DEVICE_CLASS_CAPTURE,
|
||||
GST_ASIO_DEVICE_CLASS_RENDER,
|
||||
GST_ASIO_DEVICE_CLASS_LOOPBACK_CAPTURE,
|
||||
} GstAsioDeviceClassType;
|
||||
|
||||
GstAsioObject * gst_asio_object_new (const GstAsioDeviceInfo * info,
|
||||
gboolean occupy_all_channels);
|
||||
|
||||
GstCaps * gst_asio_object_get_caps (GstAsioObject * obj,
|
||||
GstAsioDeviceClassType type,
|
||||
guint num_min_channels,
|
||||
guint num_max_channels);
|
||||
|
||||
gboolean gst_asio_object_create_buffers (GstAsioObject * obj,
|
||||
GstAsioDeviceClassType type,
|
||||
guint * channel_indices,
|
||||
guint num_channels,
|
||||
guint * buffer_size);
|
||||
|
||||
gboolean gst_asio_object_start (GstAsioObject * obj);
|
||||
|
||||
gboolean gst_asio_object_install_callback (GstAsioObject * obj,
|
||||
GstAsioDeviceClassType type,
|
||||
GstAsioObjectCallbacks * callbacks,
|
||||
guint64 * callback_id);
|
||||
|
||||
void gst_asio_object_uninstall_callback (GstAsioObject * obj,
|
||||
guint64 callback_id);
|
||||
|
||||
gboolean gst_asio_object_get_max_num_channels (GstAsioObject * obj,
|
||||
glong * num_input_ch,
|
||||
glong * num_output_ch);
|
||||
|
||||
gboolean gst_asio_object_get_buffer_size (GstAsioObject * obj,
|
||||
glong * min_size,
|
||||
glong * max_size,
|
||||
glong * preferred_size,
|
||||
glong * granularity);
|
||||
|
||||
gboolean gst_asio_object_get_latencies (GstAsioObject * obj,
|
||||
glong * input_latency,
|
||||
glong * output_latency);
|
||||
|
||||
gboolean gst_asio_object_can_sample_rate (GstAsioObject * obj,
|
||||
ASIOSampleRate sample_rate);
|
||||
|
||||
gboolean gst_asio_object_get_sample_rate (GstAsioObject * obj,
|
||||
ASIOSampleRate * sample_rate);
|
||||
|
||||
gboolean gst_asio_object_set_sample_rate (GstAsioObject * obj,
|
||||
ASIOSampleRate sample_rate);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ASIO_OBJECT_H__ */
|
473
sys/asio/gstasioringbuffer.cpp
Normal file
473
sys/asio/gstasioringbuffer.cpp
Normal file
|
@ -0,0 +1,473 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include "gstasioringbuffer.h"
|
||||
#include <string.h>
|
||||
#include "gstasioutils.h"
|
||||
#include "gstasioobject.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_asio_ring_buffer_debug);
|
||||
#define GST_CAT_DEFAULT gst_asio_ring_buffer_debug
|
||||
|
||||
struct _GstAsioRingBuffer
|
||||
{
|
||||
GstAudioRingBuffer parent;
|
||||
|
||||
GstAsioDeviceClassType type;
|
||||
|
||||
GstAsioObject *asio_object;
|
||||
guint *channel_indices;
|
||||
guint num_channels;
|
||||
ASIOBufferInfo **infos;
|
||||
|
||||
guint64 callback_id;
|
||||
gboolean callback_installed;
|
||||
|
||||
gboolean running;
|
||||
guint buffer_size;
|
||||
|
||||
/* Used to detect sample gap */
|
||||
gboolean is_first;
|
||||
guint64 expected_sample_position;
|
||||
gboolean trace_sample_position;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_INFO,
|
||||
};
|
||||
|
||||
static void gst_asio_ring_buffer_dispose (GObject * object);
|
||||
|
||||
static gboolean gst_asio_ring_buffer_open_device (GstAudioRingBuffer * buf);
|
||||
static gboolean gst_asio_ring_buffer_close_device (GstAudioRingBuffer * buf);
|
||||
static gboolean gst_asio_ring_buffer_acquire (GstAudioRingBuffer * buf,
|
||||
GstAudioRingBufferSpec * spec);
|
||||
static gboolean gst_asio_ring_buffer_release (GstAudioRingBuffer * buf);
|
||||
static gboolean gst_asio_ring_buffer_start (GstAudioRingBuffer * buf);
|
||||
static gboolean gst_asio_ring_buffer_stop (GstAudioRingBuffer * buf);
|
||||
static guint gst_asio_ring_buffer_delay (GstAudioRingBuffer * buf);
|
||||
|
||||
static gboolean gst_asio_buffer_switch_cb (GstAsioObject * obj,
|
||||
glong index, ASIOBufferInfo * infos, guint num_infos,
|
||||
ASIOChannelInfo * input_channel_infos,
|
||||
ASIOChannelInfo * output_channel_infos,
|
||||
ASIOSampleRate sample_rate, glong buffer_size, gpointer user_data);
|
||||
|
||||
#define gst_asio_ring_buffer_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstAsioRingBuffer, gst_asio_ring_buffer,
|
||||
GST_TYPE_AUDIO_RING_BUFFER);
|
||||
|
||||
static void
|
||||
gst_asio_ring_buffer_class_init (GstAsioRingBufferClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstAudioRingBufferClass *ring_buffer_class =
|
||||
GST_AUDIO_RING_BUFFER_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gst_asio_ring_buffer_dispose;
|
||||
|
||||
ring_buffer_class->open_device =
|
||||
GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_open_device);
|
||||
ring_buffer_class->close_device =
|
||||
GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_close_device);
|
||||
ring_buffer_class->acquire = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_acquire);
|
||||
ring_buffer_class->release = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_release);
|
||||
ring_buffer_class->start = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_start);
|
||||
ring_buffer_class->resume = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_start);
|
||||
ring_buffer_class->stop = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_stop);
|
||||
ring_buffer_class->delay = GST_DEBUG_FUNCPTR (gst_asio_ring_buffer_delay);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_asio_ring_buffer_debug,
|
||||
"asioringbuffer", 0, "asioringbuffer");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_ring_buffer_init (GstAsioRingBuffer * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_ring_buffer_dispose (GObject * object)
|
||||
{
|
||||
GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (object);
|
||||
|
||||
gst_clear_object (&self->asio_object);
|
||||
g_clear_pointer (&self->channel_indices, g_free);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_asio_ring_buffer_open_device (GstAudioRingBuffer * buf)
|
||||
{
|
||||
GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Open");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_asio_ring_buffer_close_device (GstAudioRingBuffer * buf)
|
||||
{
|
||||
GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Close");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define PACK_ASIO_64(v) ((v).lo | ((guint64)((v).hi) << 32))
|
||||
|
||||
static gboolean
|
||||
gst_asio_buffer_switch_cb (GstAsioObject * obj, glong index,
|
||||
ASIOBufferInfo * infos, guint num_infos,
|
||||
ASIOChannelInfo * input_channel_infos,
|
||||
ASIOChannelInfo * output_channel_infos,
|
||||
ASIOSampleRate sample_rate, glong buffer_size,
|
||||
ASIOTime * time_info, gpointer user_data)
|
||||
{
|
||||
GstAsioRingBuffer *self = (GstAsioRingBuffer *) user_data;
|
||||
GstAudioRingBuffer *ringbuffer = GST_AUDIO_RING_BUFFER_CAST (self);
|
||||
gint segment;
|
||||
guint8 *readptr;
|
||||
gint len;
|
||||
guint i, j;
|
||||
guint num_channels = 0;
|
||||
guint bps = GST_AUDIO_INFO_WIDTH (&ringbuffer->spec.info) >> 3;
|
||||
|
||||
g_assert (index == 0 || index == 1);
|
||||
g_assert (num_infos >= self->num_channels);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Buffer Switch callback, index %ld", index);
|
||||
|
||||
if (!gst_audio_ring_buffer_prepare_read (ringbuffer,
|
||||
&segment, &readptr, &len)) {
|
||||
GST_WARNING_OBJECT (self, "No segment available");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_TRACE_OBJECT (self, "segment %d, length %d", segment, len);
|
||||
|
||||
/* Check missing frames */
|
||||
if (self->type == GST_ASIO_DEVICE_CLASS_CAPTURE) {
|
||||
if (self->is_first) {
|
||||
if (time_info) {
|
||||
self->expected_sample_position =
|
||||
PACK_ASIO_64 (time_info->timeInfo.samplePosition) + buffer_size;
|
||||
self->trace_sample_position = TRUE;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (self, "ASIOTime is not available");
|
||||
self->trace_sample_position = FALSE;
|
||||
}
|
||||
|
||||
self->is_first = FALSE;
|
||||
} else if (self->trace_sample_position) {
|
||||
if (!time_info) {
|
||||
GST_WARNING_OBJECT (self, "ASIOTime is not available");
|
||||
self->trace_sample_position = FALSE;
|
||||
} else {
|
||||
guint64 sample_position =
|
||||
PACK_ASIO_64 (time_info->timeInfo.samplePosition);
|
||||
if (self->expected_sample_position < sample_position) {
|
||||
guint64 gap_frames = sample_position - self->expected_sample_position;
|
||||
gint gap_size = gap_frames * bps;
|
||||
|
||||
GST_WARNING_OBJECT (self, "%" G_GUINT64_FORMAT " frames are missing");
|
||||
|
||||
while (gap_size >= len) {
|
||||
gst_audio_format_info_fill_silence (ringbuffer->spec.info.finfo,
|
||||
readptr, len);
|
||||
gst_audio_ring_buffer_advance (ringbuffer, 1);
|
||||
|
||||
gst_audio_ring_buffer_prepare_read (ringbuffer,
|
||||
&segment, &readptr, &len);
|
||||
|
||||
gap_size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
self->expected_sample_position = sample_position + buffer_size;
|
||||
GST_TRACE_OBJECT (self, "Sample Position %" G_GUINT64_FORMAT
|
||||
", next: %" G_GUINT64_FORMAT, sample_position,
|
||||
self->expected_sample_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Given @infos might contain more channel data, pick channels what we want to
|
||||
* read */
|
||||
for (i = 0; i < num_infos; i++) {
|
||||
ASIOBufferInfo *info = &infos[i];
|
||||
|
||||
if (self->type == GST_ASIO_DEVICE_CLASS_CAPTURE) {
|
||||
if (!info->isInput)
|
||||
continue;
|
||||
} else {
|
||||
if (info->isInput)
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < self->num_channels; j++) {
|
||||
if (self->channel_indices[j] != info->channelNum)
|
||||
continue;
|
||||
|
||||
g_assert (num_channels < self->num_channels);
|
||||
self->infos[num_channels++] = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_channels < self->num_channels) {
|
||||
GST_ERROR_OBJECT (self, "Too small number of channel %d (expected %d)",
|
||||
num_channels, self->num_channels);
|
||||
} else {
|
||||
if (self->type == GST_ASIO_DEVICE_CLASS_CAPTURE ||
|
||||
self->type == GST_ASIO_DEVICE_CLASS_LOOPBACK_CAPTURE) {
|
||||
if (num_channels == 1) {
|
||||
memcpy (readptr, self->infos[0]->buffers[index], len);
|
||||
} else {
|
||||
guint gst_offset = 0, asio_offset = 0;
|
||||
|
||||
/* Interleaves audio */
|
||||
while (gst_offset < len) {
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
ASIOBufferInfo *info = self->infos[i];
|
||||
|
||||
memcpy (readptr + gst_offset,
|
||||
((guint8 *) info->buffers[index]) + asio_offset, bps);
|
||||
|
||||
gst_offset += bps;
|
||||
}
|
||||
asio_offset += bps;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (num_channels == 1) {
|
||||
memcpy (self->infos[0]->buffers[index], readptr, len);
|
||||
} else {
|
||||
guint gst_offset = 0, asio_offset = 0;
|
||||
|
||||
/* Interleaves audio */
|
||||
while (gst_offset < len) {
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
ASIOBufferInfo *info = self->infos[i];
|
||||
|
||||
memcpy (((guint8 *) info->buffers[index]) + asio_offset,
|
||||
readptr + gst_offset, bps);
|
||||
|
||||
gst_offset += bps;
|
||||
}
|
||||
asio_offset += bps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->type == GST_ASIO_DEVICE_CLASS_RENDER)
|
||||
gst_audio_ring_buffer_clear (ringbuffer, segment);
|
||||
gst_audio_ring_buffer_advance (ringbuffer, 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_asio_ring_buffer_acquire (GstAudioRingBuffer * buf,
|
||||
GstAudioRingBufferSpec * spec)
|
||||
{
|
||||
GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
|
||||
|
||||
if (!self->asio_object) {
|
||||
GST_ERROR_OBJECT (self, "No configured ASIO object");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!self->channel_indices || self->num_channels == 0) {
|
||||
GST_ERROR_OBJECT (self, "No configured channels");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_asio_object_set_sample_rate (self->asio_object,
|
||||
GST_AUDIO_INFO_RATE (&spec->info))) {
|
||||
GST_ERROR_OBJECT (self, "Failed to set sample rate");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
spec->segsize = self->buffer_size *
|
||||
(GST_AUDIO_INFO_WIDTH (&spec->info) >> 3) *
|
||||
GST_AUDIO_INFO_CHANNELS (&spec->info);
|
||||
spec->segtotal = 2;
|
||||
|
||||
buf->size = spec->segtotal * spec->segsize;
|
||||
buf->memory = (guint8 *) g_malloc (buf->size);
|
||||
gst_audio_format_info_fill_silence (buf->spec.info.finfo,
|
||||
buf->memory, buf->size);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_asio_ring_buffer_release (GstAudioRingBuffer * buf)
|
||||
{
|
||||
GST_DEBUG_OBJECT (buf, "Release");
|
||||
|
||||
g_clear_pointer (&buf->memory, g_free);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_asio_ring_buffer_start (GstAudioRingBuffer * buf)
|
||||
{
|
||||
GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
|
||||
GstAsioObjectCallbacks callbacks;
|
||||
|
||||
GST_DEBUG_OBJECT (buf, "Start");
|
||||
|
||||
callbacks.buffer_switch = gst_asio_buffer_switch_cb;
|
||||
callbacks.user_data = self;
|
||||
|
||||
self->is_first = TRUE;
|
||||
self->expected_sample_position = 0;
|
||||
|
||||
if (!gst_asio_object_install_callback (self->asio_object, self->type,
|
||||
&callbacks, &self->callback_id)) {
|
||||
GST_ERROR_OBJECT (self, "Failed to install callback");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->callback_installed = TRUE;
|
||||
|
||||
if (!gst_asio_object_start (self->asio_object)) {
|
||||
GST_ERROR_OBJECT (self, "Failed to start");
|
||||
|
||||
gst_asio_ring_buffer_stop (buf);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->running = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_asio_ring_buffer_stop (GstAudioRingBuffer * buf)
|
||||
{
|
||||
GstAsioRingBuffer *self = GST_ASIO_RING_BUFFER (buf);
|
||||
|
||||
GST_DEBUG_OBJECT (buf, "Stop");
|
||||
|
||||
self->running = FALSE;
|
||||
|
||||
if (!self->asio_object)
|
||||
return TRUE;
|
||||
|
||||
if (self->callback_installed)
|
||||
gst_asio_object_uninstall_callback (self->asio_object, self->callback_id);
|
||||
|
||||
self->callback_installed = FALSE;
|
||||
self->callback_id = 0;
|
||||
self->is_first = TRUE;
|
||||
self->expected_sample_position = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
gst_asio_ring_buffer_delay (GstAudioRingBuffer * buf)
|
||||
{
|
||||
/* FIXME: impl. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
GstAsioRingBuffer *
|
||||
gst_asio_ring_buffer_new (GstAsioObject * object, GstAsioDeviceClassType type,
|
||||
const gchar * name)
|
||||
{
|
||||
GstAsioRingBuffer *self;
|
||||
|
||||
g_return_val_if_fail (GST_IS_ASIO_OBJECT (object), nullptr);
|
||||
|
||||
self =
|
||||
(GstAsioRingBuffer *) g_object_new (GST_TYPE_ASIO_RING_BUFFER,
|
||||
"name", name, nullptr);
|
||||
g_assert (self);
|
||||
|
||||
self->type = type;
|
||||
self->asio_object = (GstAsioObject *) gst_object_ref (object);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_asio_ring_buffer_configure (GstAsioRingBuffer * buf,
|
||||
guint * channel_indices, guint num_channles, guint preferred_buffer_size)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_ASIO_RING_BUFFER (buf), FALSE);
|
||||
g_return_val_if_fail (buf->asio_object != nullptr, FALSE);
|
||||
g_return_val_if_fail (num_channles > 0, FALSE);
|
||||
|
||||
GST_DEBUG_OBJECT (buf, "Configure");
|
||||
|
||||
buf->buffer_size = preferred_buffer_size;
|
||||
|
||||
if (!gst_asio_object_create_buffers (buf->asio_object, buf->type,
|
||||
channel_indices, num_channles, &buf->buffer_size)) {
|
||||
GST_ERROR_OBJECT (buf, "Failed to configure");
|
||||
|
||||
g_clear_pointer (&buf->channel_indices, g_free);
|
||||
buf->num_channels = 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (buf, "configured buffer size: %d", buf->buffer_size);
|
||||
|
||||
g_free (buf->channel_indices);
|
||||
buf->channel_indices = g_new0 (guint, num_channles);
|
||||
|
||||
for (guint i = 0; i < num_channles; i++)
|
||||
buf->channel_indices[i] = channel_indices[i];
|
||||
|
||||
buf->num_channels = num_channles;
|
||||
|
||||
g_clear_pointer (&buf->infos, g_free);
|
||||
buf->infos = g_new0 (ASIOBufferInfo *, num_channles);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
gst_asio_ring_buffer_get_caps (GstAsioRingBuffer * buf)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_ASIO_RING_BUFFER (buf), nullptr);
|
||||
g_assert (buf->asio_object != nullptr);
|
||||
|
||||
return gst_asio_object_get_caps (buf->asio_object,
|
||||
buf->type, buf->num_channels, buf->num_channels);
|
||||
}
|
47
sys/asio/gstasioringbuffer.h
Normal file
47
sys/asio/gstasioringbuffer.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ASIO_RING_BUFFER_H__
|
||||
#define __GST_ASIO_RING_BUFFER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include "gstasioutils.h"
|
||||
#include "gstasioobject.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ASIO_RING_BUFFER (gst_asio_ring_buffer_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstAsioRingBuffer, gst_asio_ring_buffer,
|
||||
GST, ASIO_RING_BUFFER, GstAudioRingBuffer);
|
||||
|
||||
GstAsioRingBuffer * gst_asio_ring_buffer_new (GstAsioObject * object,
|
||||
GstAsioDeviceClassType type,
|
||||
const gchar * name);
|
||||
|
||||
gboolean gst_asio_ring_buffer_configure (GstAsioRingBuffer * buf,
|
||||
guint * channel_indices,
|
||||
guint num_channles,
|
||||
guint preferred_buffer_size);
|
||||
|
||||
GstCaps * gst_asio_ring_buffer_get_caps (GstAsioRingBuffer * buf);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ASIO_RING_BUFFER_H__ */
|
361
sys/asio/gstasiosink.cpp
Normal file
361
sys/asio/gstasiosink.cpp
Normal file
|
@ -0,0 +1,361 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "gstasiosink.h"
|
||||
#include "gstasioobject.h"
|
||||
#include "gstasioringbuffer.h"
|
||||
#include <atlconv.h>
|
||||
#include <string.h>
|
||||
#include <set>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_asio_sink_debug);
|
||||
#define GST_CAT_DEFAULT gst_asio_sink_debug
|
||||
|
||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_ASIO_STATIC_CAPS));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_CLSID,
|
||||
PROP_OUTPUT_CHANNELS,
|
||||
PROP_BUFFER_SIZE,
|
||||
PROP_OCCUPY_ALL_CHANNELS,
|
||||
};
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 0
|
||||
#define DEFAULT_OCCUPY_ALL_CHANNELS TRUE
|
||||
|
||||
struct _GstAsioSink
|
||||
{
|
||||
GstAudioSink parent;
|
||||
|
||||
/* properties */
|
||||
gchar *device_clsid;
|
||||
gchar *output_channels;
|
||||
guint buffer_size;
|
||||
gboolean occupy_all_channels;
|
||||
};
|
||||
|
||||
static void gst_asio_sink_finalize (GObject * object);
|
||||
static void gst_asio_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_asio_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstCaps *gst_asio_sink_get_caps (GstBaseSink * sink, GstCaps * filter);
|
||||
|
||||
static GstAudioRingBuffer *gst_asio_sink_create_ringbuffer (GstAudioBaseSink *
|
||||
sink);
|
||||
|
||||
#define gst_asio_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstAsioSink, gst_asio_sink, GST_TYPE_AUDIO_BASE_SINK);
|
||||
|
||||
static void
|
||||
gst_asio_sink_class_init (GstAsioSinkClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
|
||||
GstAudioBaseSinkClass *audiobasesink_class =
|
||||
GST_AUDIO_BASE_SINK_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_asio_sink_finalize;
|
||||
gobject_class->set_property = gst_asio_sink_set_property;
|
||||
gobject_class->get_property = gst_asio_sink_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_DEVICE_CLSID,
|
||||
g_param_spec_string ("device-clsid", "Device CLSID",
|
||||
"ASIO device CLSID as a string", NULL,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_OUTPUT_CHANNELS,
|
||||
g_param_spec_string ("output-channels", "Output Channels",
|
||||
"Comma-separated list of ASIO channels to output", NULL,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
|
||||
g_param_spec_uint ("buffer-size", "Buffer Size",
|
||||
"Preferred buffer size (0 for default)",
|
||||
0, G_MAXINT32, DEFAULT_BUFFER_SIZE,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_OCCUPY_ALL_CHANNELS,
|
||||
g_param_spec_boolean ("occupy-all-channels",
|
||||
"Occupy All Channles",
|
||||
"When enabled, ASIO device will allocate resources for all in/output "
|
||||
"channles",
|
||||
DEFAULT_OCCUPY_ALL_CHANNELS,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
gst_element_class_add_static_pad_template (element_class, &sink_template);
|
||||
gst_element_class_set_static_metadata (element_class, "AsioSink",
|
||||
"Source/Audio/Hardware",
|
||||
"Stream audio from an audio capture device through ASIO",
|
||||
"Seungha Yang <seungha@centricular.com>");
|
||||
|
||||
basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_asio_sink_get_caps);
|
||||
|
||||
audiobasesink_class->create_ringbuffer =
|
||||
GST_DEBUG_FUNCPTR (gst_asio_sink_create_ringbuffer);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_asio_sink_debug, "asiosink", 0, "asiosink");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_sink_init (GstAsioSink * self)
|
||||
{
|
||||
self->buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
self->occupy_all_channels = DEFAULT_OCCUPY_ALL_CHANNELS;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_sink_finalize (GObject * object)
|
||||
{
|
||||
GstAsioSink *self = GST_ASIO_SINK (object);
|
||||
|
||||
g_free (self->device_clsid);
|
||||
g_free (self->output_channels);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_sink_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstAsioSink *self = GST_ASIO_SINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_CLSID:
|
||||
g_free (self->device_clsid);
|
||||
self->device_clsid = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_OUTPUT_CHANNELS:
|
||||
g_free (self->output_channels);
|
||||
self->output_channels = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_BUFFER_SIZE:
|
||||
self->buffer_size = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_OCCUPY_ALL_CHANNELS:
|
||||
self->occupy_all_channels = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_sink_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstAsioSink *self = GST_ASIO_SINK (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_CLSID:
|
||||
g_value_set_string (value, self->device_clsid);
|
||||
break;
|
||||
case PROP_OUTPUT_CHANNELS:
|
||||
g_value_set_string (value, self->output_channels);
|
||||
break;
|
||||
case PROP_BUFFER_SIZE:
|
||||
g_value_set_uint (value, self->buffer_size);
|
||||
break;
|
||||
case PROP_OCCUPY_ALL_CHANNELS:
|
||||
g_value_set_boolean (value, self->occupy_all_channels);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_asio_sink_get_caps (GstBaseSink * sink, GstCaps * filter)
|
||||
{
|
||||
GstAudioBaseSink *asink = GST_AUDIO_BASE_SINK (sink);
|
||||
GstAsioSink *self = GST_ASIO_SINK (sink);
|
||||
GstCaps *caps = nullptr;
|
||||
|
||||
if (asink->ringbuffer)
|
||||
caps =
|
||||
gst_asio_ring_buffer_get_caps (GST_ASIO_RING_BUFFER
|
||||
(asink->ringbuffer));
|
||||
|
||||
if (!caps)
|
||||
caps = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (sink));
|
||||
|
||||
if (filter) {
|
||||
GstCaps *filtered =
|
||||
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
|
||||
gst_caps_unref (caps);
|
||||
caps = filtered;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "returning caps %" GST_PTR_FORMAT, caps);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static GstAudioRingBuffer *
|
||||
gst_asio_sink_create_ringbuffer (GstAudioBaseSink * sink)
|
||||
{
|
||||
GstAsioSink *self = GST_ASIO_SINK (sink);
|
||||
GstAsioRingBuffer *ringbuffer = nullptr;
|
||||
HRESULT hr;
|
||||
CLSID clsid = GUID_NULL;
|
||||
GList *device_infos = nullptr;
|
||||
GstAsioDeviceInfo *info = nullptr;
|
||||
GstAsioObject *asio_object = nullptr;
|
||||
glong max_input_ch = 0;
|
||||
glong max_output_ch = 0;
|
||||
guint *channel_indices = nullptr;
|
||||
guint num_capture_channels = 0;
|
||||
std::set < guint > channel_list;
|
||||
guint i;
|
||||
gchar *ringbuffer_name;
|
||||
|
||||
USES_CONVERSION;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Create ringbuffer");
|
||||
|
||||
if (gst_asio_enum (&device_infos) == 0) {
|
||||
GST_WARNING_OBJECT (self, "No available ASIO devices");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (self->device_clsid) {
|
||||
hr = CLSIDFromString (A2COLE (self->device_clsid), &clsid);
|
||||
if (FAILED (hr)) {
|
||||
GST_WARNING_OBJECT (self, "Failed to convert %s to CLSID",
|
||||
self->device_clsid);
|
||||
clsid = GUID_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pick the first device */
|
||||
if (clsid == GUID_NULL) {
|
||||
info = (GstAsioDeviceInfo *) device_infos->data;
|
||||
} else {
|
||||
/* Find matching device */
|
||||
GList *iter;
|
||||
for (iter = device_infos; iter; iter = g_list_next (iter)) {
|
||||
GstAsioDeviceInfo *tmp = (GstAsioDeviceInfo *) iter->data;
|
||||
if (tmp->clsid == clsid) {
|
||||
info = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
GST_WARNING_OBJECT (self, "Failed to find matching device");
|
||||
goto out;
|
||||
}
|
||||
|
||||
asio_object = gst_asio_object_new (info, self->occupy_all_channels);
|
||||
if (!asio_object) {
|
||||
GST_WARNING_OBJECT (self, "Failed to create ASIO object");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Configure channels to use */
|
||||
if (!gst_asio_object_get_max_num_channels (asio_object, &max_input_ch,
|
||||
&max_output_ch) || max_input_ch <= 0) {
|
||||
GST_WARNING_OBJECT (self, "No available input channels");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if user requested specific channle(s) */
|
||||
if (self->output_channels) {
|
||||
gchar **ch;
|
||||
|
||||
ch = g_strsplit (self->output_channels, ",", 0);
|
||||
|
||||
num_capture_channels = g_strv_length (ch);
|
||||
if (num_capture_channels > max_input_ch) {
|
||||
GST_WARNING_OBJECT (self, "To many channels %d were requested",
|
||||
num_capture_channels);
|
||||
} else {
|
||||
for (i = 0; i < num_capture_channels; i++) {
|
||||
guint64 c = g_ascii_strtoull (ch[i], nullptr, 0);
|
||||
if (c >= (guint64) max_input_ch) {
|
||||
GST_WARNING_OBJECT (self, "Invalid channel index");
|
||||
num_capture_channels = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
channel_list.insert ((guint) c);
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (ch);
|
||||
}
|
||||
|
||||
channel_indices = (guint *) g_alloca (sizeof (guint) * max_input_ch);
|
||||
if (channel_list.size () == 0) {
|
||||
for (i = 0; i < max_input_ch; i++)
|
||||
channel_indices[i] = i;
|
||||
|
||||
num_capture_channels = max_input_ch;
|
||||
} else {
|
||||
num_capture_channels = (guint) channel_list.size ();
|
||||
i = 0;
|
||||
for (auto iter:channel_list) {
|
||||
channel_indices[i++] = iter;
|
||||
}
|
||||
}
|
||||
|
||||
ringbuffer_name = g_strdup_printf ("%s-asioringbuffer",
|
||||
GST_OBJECT_NAME (sink));
|
||||
|
||||
ringbuffer =
|
||||
(GstAsioRingBuffer *) gst_asio_ring_buffer_new (asio_object,
|
||||
GST_ASIO_DEVICE_CLASS_RENDER, ringbuffer_name);
|
||||
g_free (ringbuffer_name);
|
||||
|
||||
if (!ringbuffer) {
|
||||
GST_WARNING_OBJECT (self, "Couldn't create ringbuffer object");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gst_asio_ring_buffer_configure (ringbuffer, channel_indices,
|
||||
num_capture_channels, self->buffer_size)) {
|
||||
GST_WARNING_OBJECT (self, "Failed to configure ringbuffer");
|
||||
gst_clear_object (&ringbuffer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (device_infos)
|
||||
g_list_free_full (device_infos, (GDestroyNotify) gst_asio_device_info_free);
|
||||
|
||||
gst_clear_object (&asio_object);
|
||||
|
||||
return GST_AUDIO_RING_BUFFER_CAST (ringbuffer);
|
||||
}
|
34
sys/asio/gstasiosink.h
Normal file
34
sys/asio/gstasiosink.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ASIO_SINK_H__
|
||||
#define __GST_ASIO_SINK_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ASIO_SINK (gst_asio_sink_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GstAsioSink,
|
||||
gst_asio_sink, GST, ASIO_SINK, GstAudioBaseSink);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ASIO_SINK_H__ */
|
375
sys/asio/gstasiosrc.cpp
Normal file
375
sys/asio/gstasiosrc.cpp
Normal file
|
@ -0,0 +1,375 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "gstasiosrc.h"
|
||||
#include "gstasioobject.h"
|
||||
#include "gstasioringbuffer.h"
|
||||
#include <atlconv.h>
|
||||
#include <string.h>
|
||||
#include <set>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_asio_src_debug);
|
||||
#define GST_CAT_DEFAULT gst_asio_src_debug
|
||||
|
||||
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_ASIO_STATIC_CAPS));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_CLSID,
|
||||
PROP_CAPTURE_CHANNELS,
|
||||
PROP_BUFFER_SIZE,
|
||||
PROP_OCCUPY_ALL_CHANNELS,
|
||||
PROP_LOOPBACK,
|
||||
};
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 0
|
||||
#define DEFAULT_OCCUPY_ALL_CHANNELS TRUE
|
||||
#define DEFAULT_LOOPBACK FALSE
|
||||
|
||||
struct _GstAsioSrc
|
||||
{
|
||||
GstAudioSrc parent;
|
||||
|
||||
/* properties */
|
||||
gchar *device_clsid;
|
||||
gchar *capture_channles;
|
||||
guint buffer_size;
|
||||
gboolean occupy_all_channels;
|
||||
gboolean loopback;
|
||||
};
|
||||
|
||||
static void gst_asio_src_finalize (GObject * object);
|
||||
static void gst_asio_src_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_asio_src_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstCaps *gst_asio_src_get_caps (GstBaseSrc * src, GstCaps * filter);
|
||||
|
||||
static GstAudioRingBuffer *gst_asio_src_create_ringbuffer (GstAudioBaseSrc *
|
||||
src);
|
||||
|
||||
#define gst_asio_src_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstAsioSrc, gst_asio_src, GST_TYPE_AUDIO_BASE_SRC);
|
||||
|
||||
static void
|
||||
gst_asio_src_class_init (GstAsioSrcClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
|
||||
GstAudioBaseSrcClass *audiobasesrc_class = GST_AUDIO_BASE_SRC_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gst_asio_src_finalize;
|
||||
gobject_class->set_property = gst_asio_src_set_property;
|
||||
gobject_class->get_property = gst_asio_src_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_DEVICE_CLSID,
|
||||
g_param_spec_string ("device-clsid", "Device CLSID",
|
||||
"ASIO device CLSID as a string", NULL,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_CAPTURE_CHANNELS,
|
||||
g_param_spec_string ("input-channels", "Input Channels",
|
||||
"Comma-separated list of ASIO channels to capture", NULL,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
|
||||
g_param_spec_uint ("buffer-size", "Buffer Size",
|
||||
"Preferred buffer size (0 for default)",
|
||||
0, G_MAXINT32, DEFAULT_BUFFER_SIZE,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_OCCUPY_ALL_CHANNELS,
|
||||
g_param_spec_boolean ("occupy-all-channels",
|
||||
"Occupy All Channles",
|
||||
"When enabled, ASIO device will allocate resources for all in/output "
|
||||
"channles",
|
||||
DEFAULT_OCCUPY_ALL_CHANNELS,
|
||||
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_STRINGS)));
|
||||
g_object_class_install_property (gobject_class, PROP_LOOPBACK,
|
||||
g_param_spec_boolean ("loopback", "Loopback recording",
|
||||
"Open the sink device for loopback recording",
|
||||
DEFAULT_LOOPBACK,
|
||||
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
gst_element_class_add_static_pad_template (element_class, &src_template);
|
||||
gst_element_class_set_static_metadata (element_class, "AsioSrc",
|
||||
"Source/Audio/Hardware",
|
||||
"Stream audio from an audio capture device through ASIO",
|
||||
"Seungha Yang <seungha@centricular.com>");
|
||||
|
||||
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_asio_src_get_caps);
|
||||
|
||||
audiobasesrc_class->create_ringbuffer =
|
||||
GST_DEBUG_FUNCPTR (gst_asio_src_create_ringbuffer);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_asio_src_debug, "asiosrc", 0, "asiosrc");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_src_init (GstAsioSrc * self)
|
||||
{
|
||||
self->buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
self->occupy_all_channels = DEFAULT_OCCUPY_ALL_CHANNELS;
|
||||
self->loopback = DEFAULT_LOOPBACK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_src_finalize (GObject * object)
|
||||
{
|
||||
GstAsioSrc *self = GST_ASIO_SRC (object);
|
||||
|
||||
g_free (self->device_clsid);
|
||||
g_free (self->capture_channles);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_src_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstAsioSrc *self = GST_ASIO_SRC (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_CLSID:
|
||||
g_free (self->device_clsid);
|
||||
self->device_clsid = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_CAPTURE_CHANNELS:
|
||||
g_free (self->capture_channles);
|
||||
self->capture_channles = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_BUFFER_SIZE:
|
||||
self->buffer_size = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_OCCUPY_ALL_CHANNELS:
|
||||
self->occupy_all_channels = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_LOOPBACK:
|
||||
self->loopback = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_asio_src_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstAsioSrc *self = GST_ASIO_SRC (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_DEVICE_CLSID:
|
||||
g_value_set_string (value, self->device_clsid);
|
||||
break;
|
||||
case PROP_CAPTURE_CHANNELS:
|
||||
g_value_set_string (value, self->capture_channles);
|
||||
break;
|
||||
case PROP_BUFFER_SIZE:
|
||||
g_value_set_uint (value, self->buffer_size);
|
||||
break;
|
||||
case PROP_OCCUPY_ALL_CHANNELS:
|
||||
g_value_set_boolean (value, self->occupy_all_channels);
|
||||
break;
|
||||
case PROP_LOOPBACK:
|
||||
g_value_set_boolean (value, self->loopback);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_asio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
||||
{
|
||||
GstAudioBaseSrc *asrc = GST_AUDIO_BASE_SRC (src);
|
||||
GstAsioSrc *self = GST_ASIO_SRC (src);
|
||||
GstCaps *caps = nullptr;
|
||||
|
||||
if (asrc->ringbuffer)
|
||||
caps =
|
||||
gst_asio_ring_buffer_get_caps (GST_ASIO_RING_BUFFER (asrc->ringbuffer));
|
||||
|
||||
if (!caps)
|
||||
caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src));
|
||||
|
||||
if (filter) {
|
||||
GstCaps *filtered =
|
||||
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
|
||||
gst_caps_unref (caps);
|
||||
caps = filtered;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "returning caps %" GST_PTR_FORMAT, caps);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static GstAudioRingBuffer *
|
||||
gst_asio_src_create_ringbuffer (GstAudioBaseSrc * src)
|
||||
{
|
||||
GstAsioSrc *self = GST_ASIO_SRC (src);
|
||||
GstAsioRingBuffer *ringbuffer = nullptr;
|
||||
HRESULT hr;
|
||||
CLSID clsid = GUID_NULL;
|
||||
GList *device_infos = nullptr;
|
||||
GstAsioDeviceInfo *info = nullptr;
|
||||
GstAsioObject *asio_object = nullptr;
|
||||
glong max_input_ch = 0;
|
||||
glong max_output_ch = 0;
|
||||
guint *channel_indices = nullptr;
|
||||
guint num_capture_channels = 0;
|
||||
std::set < guint > channel_list;
|
||||
guint i;
|
||||
gchar *ringbuffer_name;
|
||||
|
||||
USES_CONVERSION;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Create ringbuffer");
|
||||
|
||||
if (gst_asio_enum (&device_infos) == 0) {
|
||||
GST_WARNING_OBJECT (self, "No available ASIO devices");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (self->device_clsid) {
|
||||
hr = CLSIDFromString (A2COLE (self->device_clsid), &clsid);
|
||||
if (FAILED (hr)) {
|
||||
GST_WARNING_OBJECT (self, "Failed to convert %s to CLSID",
|
||||
self->device_clsid);
|
||||
clsid = GUID_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pick the first device */
|
||||
if (clsid == GUID_NULL) {
|
||||
info = (GstAsioDeviceInfo *) device_infos->data;
|
||||
} else {
|
||||
/* Find matching device */
|
||||
GList *iter;
|
||||
for (iter = device_infos; iter; iter = g_list_next (iter)) {
|
||||
GstAsioDeviceInfo *tmp = (GstAsioDeviceInfo *) iter->data;
|
||||
if (tmp->clsid == clsid) {
|
||||
info = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
GST_WARNING_OBJECT (self, "Failed to find matching device");
|
||||
goto out;
|
||||
}
|
||||
|
||||
asio_object = gst_asio_object_new (info, self->occupy_all_channels);
|
||||
if (!asio_object) {
|
||||
GST_WARNING_OBJECT (self, "Failed to create ASIO object");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Configure channels to use */
|
||||
if (!gst_asio_object_get_max_num_channels (asio_object, &max_input_ch,
|
||||
&max_output_ch) || max_input_ch <= 0) {
|
||||
GST_WARNING_OBJECT (self, "No available input channels");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if user requested specific channel(s) */
|
||||
if (self->capture_channles) {
|
||||
gchar **ch;
|
||||
|
||||
ch = g_strsplit (self->capture_channles, ",", 0);
|
||||
|
||||
num_capture_channels = g_strv_length (ch);
|
||||
if (num_capture_channels > max_input_ch) {
|
||||
GST_WARNING_OBJECT (self, "To many channels %d were requested",
|
||||
num_capture_channels);
|
||||
} else {
|
||||
for (i = 0; i < num_capture_channels; i++) {
|
||||
guint64 c = g_ascii_strtoull (ch[i], nullptr, 0);
|
||||
if (c >= (guint64) max_input_ch) {
|
||||
GST_WARNING_OBJECT (self, "Invalid channel index");
|
||||
num_capture_channels = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
channel_list.insert ((guint) c);
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (ch);
|
||||
}
|
||||
|
||||
channel_indices = (guint *) g_alloca (sizeof (guint) * max_input_ch);
|
||||
if (channel_list.size () == 0) {
|
||||
for (i = 0; i < max_input_ch; i++)
|
||||
channel_indices[i] = i;
|
||||
|
||||
num_capture_channels = max_input_ch;
|
||||
} else {
|
||||
num_capture_channels = (guint) channel_list.size ();
|
||||
i = 0;
|
||||
for (auto iter:channel_list) {
|
||||
channel_indices[i++] = iter;
|
||||
}
|
||||
}
|
||||
|
||||
ringbuffer_name = g_strdup_printf ("%s-asioringbuffer",
|
||||
GST_OBJECT_NAME (src));
|
||||
|
||||
ringbuffer =
|
||||
(GstAsioRingBuffer *) gst_asio_ring_buffer_new (asio_object,
|
||||
self->loopback ? GST_ASIO_DEVICE_CLASS_LOOPBACK_CAPTURE :
|
||||
GST_ASIO_DEVICE_CLASS_CAPTURE, ringbuffer_name);
|
||||
g_free (ringbuffer_name);
|
||||
|
||||
if (!ringbuffer) {
|
||||
GST_WARNING_OBJECT (self, "Couldn't create ringbuffer object");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gst_asio_ring_buffer_configure (ringbuffer, channel_indices,
|
||||
num_capture_channels, self->buffer_size)) {
|
||||
GST_WARNING_OBJECT (self, "Failed to configure ringbuffer");
|
||||
gst_clear_object (&ringbuffer);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (device_infos)
|
||||
g_list_free_full (device_infos, (GDestroyNotify) gst_asio_device_info_free);
|
||||
|
||||
gst_clear_object (&asio_object);
|
||||
|
||||
return GST_AUDIO_RING_BUFFER_CAST (ringbuffer);
|
||||
}
|
34
sys/asio/gstasiosrc.h
Normal file
34
sys/asio/gstasiosrc.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ASIO_SRC_H__
|
||||
#define __GST_ASIO_SRC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ASIO_SRC (gst_asio_src_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GstAsioSrc,
|
||||
gst_asio_src, GST, ASIO_SRC, GstAudioBaseSrc);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ASIO_SRC_H__ */
|
282
sys/asio/gstasioutils.cpp
Normal file
282
sys/asio/gstasioutils.cpp
Normal file
|
@ -0,0 +1,282 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstasioutils.h"
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include <atlconv.h>
|
||||
|
||||
static gboolean
|
||||
gst_asio_enum_check_class_root (GstAsioDeviceInfo * info, LPCWSTR clsid)
|
||||
{
|
||||
LSTATUS status;
|
||||
HKEY root_key = nullptr;
|
||||
HKEY device_key = nullptr;
|
||||
HKEY proc_server_key = nullptr;
|
||||
DWORD type = REG_SZ;
|
||||
CHAR data[256];
|
||||
DWORD size = sizeof (data);
|
||||
gboolean ret = FALSE;
|
||||
|
||||
status = RegOpenKeyExW (HKEY_CLASSES_ROOT, L"clsid", 0, KEY_READ, &root_key);
|
||||
if (status != ERROR_SUCCESS)
|
||||
return FALSE;
|
||||
|
||||
/* Read registry HKEY_CLASS_ROOT/CLSID/{device-clsid} */
|
||||
status = RegOpenKeyExW (root_key, clsid, 0, KEY_READ, &device_key);
|
||||
if (status != ERROR_SUCCESS)
|
||||
goto done;
|
||||
|
||||
/* ThreadingModel describes COM apartment */
|
||||
status = RegOpenKeyExW (device_key,
|
||||
L"InprocServer32", 0, KEY_READ, &proc_server_key);
|
||||
if (status != ERROR_SUCCESS)
|
||||
goto done;
|
||||
|
||||
status = RegQueryValueExA (proc_server_key,
|
||||
"ThreadingModel", nullptr, &type, (LPBYTE) data, &size);
|
||||
if (status != ERROR_SUCCESS)
|
||||
goto done;
|
||||
|
||||
if (g_ascii_strcasecmp (data, "Both") == 0 ||
|
||||
g_ascii_strcasecmp (data, "Free") == 0) {
|
||||
info->sta_model = FALSE;
|
||||
} else {
|
||||
info->sta_model = TRUE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
done:
|
||||
if (proc_server_key)
|
||||
RegCloseKey (proc_server_key);
|
||||
|
||||
if (device_key)
|
||||
RegCloseKey (device_key);
|
||||
|
||||
if (root_key)
|
||||
RegCloseKey (root_key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstAsioDeviceInfo *
|
||||
gst_asio_enum_new_device_info_from_reg (HKEY reg_key, LPWSTR key_name)
|
||||
{
|
||||
LSTATUS status;
|
||||
HKEY sub_key = nullptr;
|
||||
WCHAR clsid_data[256];
|
||||
WCHAR desc_data[256];
|
||||
DWORD type = REG_SZ;
|
||||
DWORD size = sizeof (clsid_data);
|
||||
GstAsioDeviceInfo *ret = nullptr;
|
||||
CLSID id;
|
||||
HRESULT hr;
|
||||
|
||||
USES_CONVERSION;
|
||||
|
||||
status = RegOpenKeyExW (reg_key, key_name, 0, KEY_READ, &sub_key);
|
||||
if (status != ERROR_SUCCESS)
|
||||
return nullptr;
|
||||
|
||||
/* find CLSID value, used for CoCreateInstance */
|
||||
status = RegQueryValueExW (sub_key,
|
||||
L"clsid", 0, &type, (LPBYTE) clsid_data, &size);
|
||||
if (status != ERROR_SUCCESS)
|
||||
goto done;
|
||||
|
||||
hr = CLSIDFromString (W2COLE (clsid_data), &id);
|
||||
if (FAILED (hr))
|
||||
goto done;
|
||||
|
||||
ret = g_new0 (GstAsioDeviceInfo, 1);
|
||||
ret->clsid = id;
|
||||
ret->driver_name = g_utf16_to_utf8 ((gunichar2 *) key_name, -1,
|
||||
nullptr, nullptr, nullptr);
|
||||
|
||||
/* human readable device description */
|
||||
status = RegQueryValueExW (sub_key,
|
||||
L"description", 0, &type, (LPBYTE) desc_data, &size);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
GST_WARNING ("no description");
|
||||
ret->driver_desc = g_strdup (ret->driver_name);
|
||||
} else {
|
||||
ret->driver_desc = g_utf16_to_utf8 ((gunichar2 *) desc_data, -1,
|
||||
nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/* Check COM threading model */
|
||||
if (!gst_asio_enum_check_class_root (ret, clsid_data)) {
|
||||
gst_asio_device_info_free (ret);
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
done:
|
||||
if (sub_key)
|
||||
RegCloseKey (sub_key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
guint
|
||||
gst_asio_enum (GList ** infos)
|
||||
{
|
||||
GList *info_list = nullptr;
|
||||
DWORD index = 0;
|
||||
guint num_device = 0;
|
||||
LSTATUS status;
|
||||
HKEY reg_key = nullptr;
|
||||
WCHAR key_name[512];
|
||||
|
||||
g_return_val_if_fail (infos != nullptr, 0);
|
||||
|
||||
status = RegOpenKeyExW (HKEY_LOCAL_MACHINE, L"software\\asio", 0,
|
||||
KEY_READ, ®_key);
|
||||
while (status == ERROR_SUCCESS) {
|
||||
GstAsioDeviceInfo *info;
|
||||
|
||||
status = RegEnumKeyW (reg_key, index, key_name, 512);
|
||||
if (status != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
index++;
|
||||
info = gst_asio_enum_new_device_info_from_reg (reg_key, key_name);
|
||||
if (!info)
|
||||
continue;
|
||||
|
||||
info_list = g_list_append (info_list, info);
|
||||
num_device++;
|
||||
}
|
||||
|
||||
if (reg_key)
|
||||
RegCloseKey (reg_key);
|
||||
|
||||
*infos = info_list;
|
||||
|
||||
return num_device;
|
||||
}
|
||||
|
||||
GstAsioDeviceInfo *
|
||||
gst_asio_device_info_copy (const GstAsioDeviceInfo * info)
|
||||
{
|
||||
GstAsioDeviceInfo *new_info;
|
||||
|
||||
if (!info)
|
||||
return nullptr;
|
||||
|
||||
new_info = g_new0 (GstAsioDeviceInfo, 1);
|
||||
|
||||
new_info->clsid = info->clsid;
|
||||
new_info->sta_model = info->sta_model;
|
||||
new_info->driver_name = g_strdup (info->driver_name);
|
||||
new_info->driver_desc = g_strdup (info->driver_desc);
|
||||
|
||||
return new_info;
|
||||
}
|
||||
|
||||
void
|
||||
gst_asio_device_info_free (GstAsioDeviceInfo * info)
|
||||
{
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
g_free (info->driver_name);
|
||||
g_free (info->driver_desc);
|
||||
|
||||
g_free (info);
|
||||
}
|
||||
|
||||
GstAudioFormat
|
||||
gst_asio_sample_type_to_gst (ASIOSampleType type)
|
||||
{
|
||||
GstAudioFormat fmt;
|
||||
|
||||
switch (type) {
|
||||
/*~~ MSB means big endian ~~ */
|
||||
case ASIOSTInt16MSB:
|
||||
fmt = GST_AUDIO_FORMAT_S16BE;
|
||||
break;
|
||||
/* FIXME: also used for 20 bits packed in 24 bits, how do we detect that? */
|
||||
case ASIOSTInt24MSB:
|
||||
fmt = GST_AUDIO_FORMAT_S24BE;
|
||||
break;
|
||||
case ASIOSTInt32MSB:
|
||||
fmt = GST_AUDIO_FORMAT_S32BE;
|
||||
break;
|
||||
case ASIOSTFloat32MSB:
|
||||
fmt = GST_AUDIO_FORMAT_F32BE;
|
||||
break;
|
||||
case ASIOSTFloat64MSB:
|
||||
fmt = GST_AUDIO_FORMAT_F64BE;
|
||||
break;
|
||||
/* All these are aligned to a different boundary than the packing, not sure
|
||||
* how to handle it, let's try the normal S32BE format */
|
||||
case ASIOSTInt32MSB16:
|
||||
case ASIOSTInt32MSB18:
|
||||
case ASIOSTInt32MSB20:
|
||||
case ASIOSTInt32MSB24:
|
||||
fmt = GST_AUDIO_FORMAT_S32BE;
|
||||
break;
|
||||
|
||||
/*~~ LSB means little endian ~~ */
|
||||
case ASIOSTInt16LSB:
|
||||
fmt = GST_AUDIO_FORMAT_S16LE;
|
||||
break;
|
||||
/* FIXME: also used for 20 bits packed in 24 bits, how do we detect that? */
|
||||
case ASIOSTInt24LSB:
|
||||
fmt = GST_AUDIO_FORMAT_S24LE;
|
||||
break;
|
||||
case ASIOSTInt32LSB:
|
||||
fmt = GST_AUDIO_FORMAT_S32LE;
|
||||
break;
|
||||
case ASIOSTFloat32LSB:
|
||||
fmt = GST_AUDIO_FORMAT_F32LE;
|
||||
break;
|
||||
case ASIOSTFloat64LSB:
|
||||
fmt = GST_AUDIO_FORMAT_F64LE;
|
||||
break;
|
||||
/* All these are aligned to a different boundary than the packing, not sure
|
||||
* how to handle it, let's try the normal S32LE format */
|
||||
case ASIOSTInt32LSB16:
|
||||
case ASIOSTInt32LSB18:
|
||||
case ASIOSTInt32LSB20:
|
||||
case ASIOSTInt32LSB24:
|
||||
GST_WARNING ("weird alignment %ld, trying S32LE", type);
|
||||
fmt = GST_AUDIO_FORMAT_S32LE;
|
||||
break;
|
||||
|
||||
/*~~ ASIO DSD formats are don't have gstreamer mappings ~~ */
|
||||
case ASIOSTDSDInt8LSB1:
|
||||
case ASIOSTDSDInt8MSB1:
|
||||
case ASIOSTDSDInt8NER8:
|
||||
GST_ERROR ("ASIO DSD formats are not supported");
|
||||
fmt = GST_AUDIO_FORMAT_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
GST_ERROR ("Unknown asio sample type %ld", type);
|
||||
fmt = GST_AUDIO_FORMAT_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
55
sys/asio/gstasioutils.h
Normal file
55
sys/asio/gstasioutils.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ASIO_DEVICE_ENUM_H__
|
||||
#define __GST_ASIO_DEVICE_ENUM_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <windows.h>
|
||||
#include <asiosys.h>
|
||||
#include <asio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_ASIO_STATIC_CAPS "audio/x-raw, " \
|
||||
"format = (string) " GST_AUDIO_FORMATS_ALL ", " \
|
||||
"layout = (string) interleaved, " \
|
||||
"rate = " GST_AUDIO_RATE_RANGE ", " \
|
||||
"channels = " GST_AUDIO_CHANNELS_RANGE
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CLSID clsid;
|
||||
gboolean sta_model;
|
||||
gchar *driver_name;
|
||||
gchar *driver_desc;
|
||||
} GstAsioDeviceInfo;
|
||||
|
||||
guint gst_asio_enum (GList ** infos);
|
||||
|
||||
GstAsioDeviceInfo * gst_asio_device_info_copy (const GstAsioDeviceInfo * info);
|
||||
|
||||
void gst_asio_device_info_free (GstAsioDeviceInfo * info);
|
||||
|
||||
GstAudioFormat gst_asio_sample_type_to_gst (ASIOSampleType type);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ASIO_DEVICE_ENUM_H__ */
|
84
sys/asio/meson.build
Normal file
84
sys/asio/meson.build
Normal file
|
@ -0,0 +1,84 @@
|
|||
asio_sources = [
|
||||
'gstasiodeviceprovider.cpp',
|
||||
'gstasioobject.cpp',
|
||||
'gstasioringbuffer.cpp',
|
||||
'gstasiosink.cpp',
|
||||
'gstasiosrc.cpp',
|
||||
'gstasioutils.cpp',
|
||||
'plugin.c',
|
||||
]
|
||||
|
||||
asio_option = get_option('asio')
|
||||
if asio_option.disabled() or host_system != 'windows'
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
# FIXME: non-msvc is not tested, and unlikely supported yet because of
|
||||
# tool-chain issue
|
||||
if cxx.get_id() != 'msvc'
|
||||
if asio_option.enabled()
|
||||
error('asio plugin can only be built with MSVC')
|
||||
else
|
||||
subdir_done ()
|
||||
endif
|
||||
endif
|
||||
|
||||
winapi_desktop = cxx.compiles('''#include <winapifamily.h>
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
#error "not win32"
|
||||
#endif''',
|
||||
name: 'building for Win32')
|
||||
|
||||
if not winapi_desktop
|
||||
if asio_option.enabled()
|
||||
error('asio plugin requires WINAPI_PARTITION_DESKTOP')
|
||||
else
|
||||
subdir_done ()
|
||||
endif
|
||||
endif
|
||||
|
||||
avrt_lib = cc.find_library('avrt', required: asio_option)
|
||||
if not avrt_lib.found()
|
||||
subdir_done ()
|
||||
endif
|
||||
|
||||
winmm_lib = cc.find_library('winmm', required: asio_option)
|
||||
if not winmm_lib.found()
|
||||
subdir_done ()
|
||||
endif
|
||||
|
||||
# Checking SDK headers. User should install ASIO sdk on system, and
|
||||
# this plugin requires asio.h, asiosys.h and iasiodrv.h headers
|
||||
asio_sdk_root = get_option ('asio-sdk-path')
|
||||
if asio_sdk_root == ''
|
||||
if asio_option.enabled()
|
||||
error('asio sdk path is needed, pass with -Dasio-sdk-path=C:/path/to/sdk')
|
||||
else
|
||||
subdir_done ()
|
||||
endif
|
||||
endif
|
||||
|
||||
asio_inc_dir = include_directories(join_paths(asio_sdk_root, 'common'), is_system : true)
|
||||
has_asio_header = cxx.has_header('asio.h', include_directories: asio_inc_dir)
|
||||
has_asiosys_header = cxx.has_header('asiosys.h', include_directories: asio_inc_dir)
|
||||
has_iasiodrv_header = cxx.has_header('iasiodrv.h', include_directories: asio_inc_dir)
|
||||
if not has_asio_header or not has_asiosys_header or not has_iasiodrv_header
|
||||
if asio_option.enabled()
|
||||
error('Failed to find required SDK header(s)')
|
||||
else
|
||||
subdir_done ()
|
||||
endif
|
||||
endif
|
||||
|
||||
asio_deps = [gstaudio_dep, avrt_lib, winmm_lib]
|
||||
|
||||
gstasio = library('gstasio',
|
||||
asio_sources,
|
||||
include_directories : [configinc, asio_inc_dir],
|
||||
dependencies : asio_deps,
|
||||
c_args : gst_plugins_bad_args,
|
||||
cpp_args : gst_plugins_bad_args,
|
||||
install : true,
|
||||
install_dir : plugins_install_dir)
|
||||
pkgconfig.generate(gstasio, install_dir : plugins_pkgconfig_install_dir)
|
||||
plugins += [gstasio]
|
48
sys/asio/plugin.c
Normal file
48
sys/asio/plugin.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstasiodeviceprovider.h"
|
||||
#include "gstasiosrc.h"
|
||||
#include "gstasiosink.h"
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
GstRank rank = GST_RANK_SECONDARY;
|
||||
|
||||
if (!gst_element_register (plugin, "asiosrc", rank, GST_TYPE_ASIO_SRC))
|
||||
return FALSE;
|
||||
if (!gst_element_register (plugin, "asiosink", rank, GST_TYPE_ASIO_SINK))
|
||||
return FALSE;
|
||||
if (!gst_device_provider_register (plugin, "asiodeviceprovider",
|
||||
rank, GST_TYPE_ASIO_DEVICE_PROVIDER))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
asio,
|
||||
"Steinberg ASIO plugin",
|
||||
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
|
@ -1,5 +1,6 @@
|
|||
subdir('androidmedia')
|
||||
subdir('applemedia')
|
||||
subdir('asio')
|
||||
subdir('bluez')
|
||||
subdir('d3d11')
|
||||
subdir('d3dvideosink')
|
||||
|
|
Loading…
Reference in a new issue