1 /* GStreamer
2 * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstasiodeviceprovider.h"
25 #include "gstasioutils.h"
26 #include "gstasioobject.h"
27 #include <atlconv.h>
28
29 enum
30 {
31 PROP_0,
32 PROP_DEVICE_CLSID,
33 };
34
35 struct _GstAsioDevice
36 {
37 GstDevice parent;
38
39 gchar *device_clsid;
40 const gchar *factory_name;
41 };
42
43 G_DEFINE_TYPE (GstAsioDevice, gst_asio_device, GST_TYPE_DEVICE);
44
45 static void gst_asio_device_get_property (GObject * object,
46 guint prop_id, GValue * value, GParamSpec * pspec);
47 static void gst_asio_device_set_property (GObject * object,
48 guint prop_id, const GValue * value, GParamSpec * pspec);
49 static void gst_asio_device_finalize (GObject * object);
50 static GstElement *gst_asio_device_create_element (GstDevice * device,
51 const gchar * name);
52
53 static void
gst_asio_device_class_init(GstAsioDeviceClass * klass)54 gst_asio_device_class_init (GstAsioDeviceClass * klass)
55 {
56 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
57 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
58
59 dev_class->create_element = gst_asio_device_create_element;
60
61 gobject_class->get_property = gst_asio_device_get_property;
62 gobject_class->set_property = gst_asio_device_set_property;
63 gobject_class->finalize = gst_asio_device_finalize;
64
65 g_object_class_install_property (gobject_class, PROP_DEVICE_CLSID,
66 g_param_spec_string ("device-clsid", "Device CLSID",
67 "ASIO device CLSID as string including curly brackets", NULL,
68 (GParamFlags) (G_PARAM_READWRITE |
69 G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)));
70 }
71
72 static void
gst_asio_device_init(GstAsioDevice * self)73 gst_asio_device_init (GstAsioDevice * self)
74 {
75 }
76
77 static void
gst_asio_device_finalize(GObject * object)78 gst_asio_device_finalize (GObject * object)
79 {
80 GstAsioDevice *self = GST_ASIO_DEVICE (object);
81
82 g_free (self->device_clsid);
83
84 G_OBJECT_CLASS (gst_asio_device_parent_class)->finalize (object);
85 }
86
87 static GstElement *
gst_asio_device_create_element(GstDevice * device,const gchar * name)88 gst_asio_device_create_element (GstDevice * device, const gchar * name)
89 {
90 GstAsioDevice *self = GST_ASIO_DEVICE (device);
91 GstElement *elem;
92
93 elem = gst_element_factory_make (self->factory_name, name);
94
95 g_object_set (elem, "device-clsid", self->device_clsid, NULL);
96
97 return elem;
98 }
99
100 static void
gst_asio_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)101 gst_asio_device_get_property (GObject * object, guint prop_id,
102 GValue * value, GParamSpec * pspec)
103 {
104 GstAsioDevice *self = GST_ASIO_DEVICE (object);
105
106 switch (prop_id) {
107 case PROP_DEVICE_CLSID:
108 g_value_set_string (value, self->device_clsid);
109 break;
110 default:
111 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
112 break;
113 }
114 }
115
116 static void
gst_asio_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)117 gst_asio_device_set_property (GObject * object, guint prop_id,
118 const GValue * value, GParamSpec * pspec)
119 {
120 GstAsioDevice *self = GST_ASIO_DEVICE (object);
121
122 switch (prop_id) {
123 case PROP_DEVICE_CLSID:
124 g_free (self->device_clsid);
125 self->device_clsid = g_value_dup_string (value);
126 break;
127 default:
128 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129 break;
130 }
131 }
132
133 struct _GstAsioDeviceProvider
134 {
135 GstDeviceProvider parent;
136 };
137
138 G_DEFINE_TYPE (GstAsioDeviceProvider, gst_asio_device_provider,
139 GST_TYPE_DEVICE_PROVIDER);
140
141 static GList *gst_asio_device_provider_probe (GstDeviceProvider * provider);
142
143 static void
gst_asio_device_provider_class_init(GstAsioDeviceProviderClass * klass)144 gst_asio_device_provider_class_init (GstAsioDeviceProviderClass * klass)
145 {
146 GstDeviceProviderClass *provider_class = GST_DEVICE_PROVIDER_CLASS (klass);
147
148 provider_class->probe = GST_DEBUG_FUNCPTR (gst_asio_device_provider_probe);
149
150 gst_device_provider_class_set_static_metadata (provider_class,
151 "ASIO Device Provider",
152 "Source/Sink/Audio", "List ASIO source and sink devices",
153 "Seungha Yang <seungha@centricular.com>");
154 }
155
156 static void
gst_asio_device_provider_init(GstAsioDeviceProvider * provider)157 gst_asio_device_provider_init (GstAsioDeviceProvider * provider)
158 {
159 }
160
161 static void
gst_asio_device_provider_probe_internal(GstAsioDeviceProvider * self,gboolean is_src,GList * asio_device_list,GList ** devices)162 gst_asio_device_provider_probe_internal (GstAsioDeviceProvider * self,
163 gboolean is_src, GList * asio_device_list, GList ** devices)
164 {
165 const gchar *device_class, *factory_name;
166 GList *iter;
167
168 USES_CONVERSION;
169
170 if (is_src) {
171 device_class = "Audio/Source";
172 factory_name = "asiosrc";
173 } else {
174 device_class = "Audio/Sink";
175 factory_name = "asiosink";
176 }
177
178 for (iter = asio_device_list; iter; iter = g_list_next (iter)) {
179 GstDevice *device;
180 GstAsioDeviceInfo *info = (GstAsioDeviceInfo *) iter->data;
181 GstAsioObject *obj;
182 GstCaps *caps = nullptr;
183 GstStructure *props = nullptr;
184 long max_in_ch = 0;
185 long max_out_ch = 0;
186 HRESULT hr;
187 LPOLESTR clsid_str = nullptr;
188 glong min_buf_size = 0;
189 glong max_buf_size = 0;
190 glong preferred_buf_size = 0;
191 glong buf_size_granularity = 0;
192
193 obj = gst_asio_object_new (info, FALSE);
194 if (!obj)
195 continue;
196
197 if (!gst_asio_object_get_max_num_channels (obj, &max_in_ch, &max_out_ch))
198 goto done;
199
200 if (is_src && max_in_ch <= 0)
201 goto done;
202 else if (!is_src && max_out_ch <= 0)
203 goto done;
204
205 if (is_src) {
206 caps = gst_asio_object_get_caps (obj,
207 GST_ASIO_DEVICE_CLASS_CAPTURE, 1, max_in_ch);
208 } else {
209 caps = gst_asio_object_get_caps (obj,
210 GST_ASIO_DEVICE_CLASS_RENDER, 1, max_out_ch);
211 }
212 if (!caps)
213 goto done;
214
215 hr = StringFromIID (info->clsid, &clsid_str);
216 if (FAILED (hr))
217 goto done;
218
219 if (!gst_asio_object_get_buffer_size (obj, &min_buf_size, &max_buf_size,
220 &preferred_buf_size, &buf_size_granularity))
221 goto done;
222
223 props = gst_structure_new ("asio-proplist",
224 "device.api", G_TYPE_STRING, "asio",
225 "device.clsid", G_TYPE_STRING, OLE2A (clsid_str),
226 "asio.device.description", G_TYPE_STRING, info->driver_desc,
227 "asio.device.min-buf-size", G_TYPE_LONG, min_buf_size,
228 "asio.device.max-buf-size", G_TYPE_LONG, max_buf_size,
229 "asio.device.preferred-buf-size", G_TYPE_LONG, preferred_buf_size,
230 "asio.device.buf-size-granularity", G_TYPE_LONG, buf_size_granularity,
231 nullptr);
232
233 device = (GstDevice *) g_object_new (GST_TYPE_ASIO_DEVICE,
234 "device-clsid", OLE2A (clsid_str),
235 "display-name", info->driver_desc, "caps", caps,
236 "device-class", device_class, "properties", props, nullptr);
237 GST_ASIO_DEVICE (device)->factory_name = factory_name;
238
239 *devices = g_list_append (*devices, device);
240
241 done:
242 gst_clear_caps (&caps);
243 gst_clear_object (&obj);
244 if (props)
245 gst_structure_free (props);
246 }
247
248 return;
249 }
250
251 static GList *
gst_asio_device_provider_probe(GstDeviceProvider * provider)252 gst_asio_device_provider_probe (GstDeviceProvider * provider)
253 {
254 GstAsioDeviceProvider *self = GST_ASIO_DEVICE_PROVIDER (provider);
255 GList *devices = nullptr;
256 guint num_device;
257 GList *asio_device_list = nullptr;
258
259 num_device = gst_asio_enum (&asio_device_list);
260
261 if (num_device == 0)
262 return nullptr;
263
264 gst_asio_device_provider_probe_internal (self,
265 TRUE, asio_device_list, &devices);
266 gst_asio_device_provider_probe_internal (self,
267 FALSE, asio_device_list, &devices);
268
269 g_list_free_full (asio_device_list,
270 (GDestroyNotify) gst_asio_device_info_free);
271
272 return devices;
273 }
274