• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gstasioutils.h"
25 #include <windows.h>
26 #include <string.h>
27 #include <atlconv.h>
28 
29 static gboolean
gst_asio_enum_check_class_root(GstAsioDeviceInfo * info,LPCWSTR clsid)30 gst_asio_enum_check_class_root (GstAsioDeviceInfo * info, LPCWSTR clsid)
31 {
32   LSTATUS status;
33   HKEY root_key = nullptr;
34   HKEY device_key = nullptr;
35   HKEY proc_server_key = nullptr;
36   DWORD type = REG_SZ;
37   CHAR data[256];
38   DWORD size = sizeof (data);
39   gboolean ret = FALSE;
40 
41   status = RegOpenKeyExW (HKEY_CLASSES_ROOT, L"clsid", 0, KEY_READ, &root_key);
42   if (status != ERROR_SUCCESS)
43     return FALSE;
44 
45   /* Read registry HKEY_CLASS_ROOT/CLSID/{device-clsid} */
46   status = RegOpenKeyExW (root_key, clsid, 0, KEY_READ, &device_key);
47   if (status != ERROR_SUCCESS)
48     goto done;
49 
50   /* ThreadingModel describes COM apartment */
51   status = RegOpenKeyExW (device_key,
52       L"InprocServer32", 0, KEY_READ, &proc_server_key);
53   if (status != ERROR_SUCCESS)
54     goto done;
55 
56   status = RegQueryValueExA (proc_server_key,
57       "ThreadingModel", nullptr, &type, (LPBYTE) data, &size);
58   if (status != ERROR_SUCCESS)
59     goto done;
60 
61   if (g_ascii_strcasecmp (data, "Both") == 0 ||
62       g_ascii_strcasecmp (data, "Free") == 0) {
63     info->sta_model = FALSE;
64   } else {
65     info->sta_model = TRUE;
66   }
67 
68   ret = TRUE;
69 
70 done:
71   if (proc_server_key)
72     RegCloseKey (proc_server_key);
73 
74   if (device_key)
75     RegCloseKey (device_key);
76 
77   if (root_key)
78     RegCloseKey (root_key);
79 
80   return ret;
81 }
82 
83 static GstAsioDeviceInfo *
gst_asio_enum_new_device_info_from_reg(HKEY reg_key,LPWSTR key_name)84 gst_asio_enum_new_device_info_from_reg (HKEY reg_key, LPWSTR key_name)
85 {
86   LSTATUS status;
87   HKEY sub_key = nullptr;
88   WCHAR clsid_data[256];
89   WCHAR desc_data[256];
90   DWORD type = REG_SZ;
91   DWORD size = sizeof (clsid_data);
92   GstAsioDeviceInfo *ret = nullptr;
93   CLSID id;
94   HRESULT hr;
95 
96   USES_CONVERSION;
97 
98   status = RegOpenKeyExW (reg_key, key_name, 0, KEY_READ, &sub_key);
99   if (status != ERROR_SUCCESS)
100     return nullptr;
101 
102   /* find CLSID value, used for CoCreateInstance */
103   status = RegQueryValueExW (sub_key,
104       L"clsid", 0, &type, (LPBYTE) clsid_data, &size);
105   if (status != ERROR_SUCCESS)
106     goto done;
107 
108   hr = CLSIDFromString (W2COLE (clsid_data), &id);
109   if (FAILED (hr))
110     goto done;
111 
112   ret = g_new0 (GstAsioDeviceInfo, 1);
113   ret->clsid = id;
114   ret->driver_name = g_utf16_to_utf8 ((gunichar2 *) key_name, -1,
115       nullptr, nullptr, nullptr);
116 
117   /* human readable device description */
118   status = RegQueryValueExW (sub_key,
119       L"description", 0, &type, (LPBYTE) desc_data, &size);
120   if (status != ERROR_SUCCESS) {
121     GST_WARNING ("no description");
122     ret->driver_desc = g_strdup (ret->driver_name);
123   } else {
124     ret->driver_desc = g_utf16_to_utf8 ((gunichar2 *) desc_data, -1,
125         nullptr, nullptr, nullptr);
126   }
127 
128   /* Check COM threading model */
129   if (!gst_asio_enum_check_class_root (ret, clsid_data)) {
130     gst_asio_device_info_free (ret);
131     ret = nullptr;
132   }
133 
134 done:
135   if (sub_key)
136     RegCloseKey (sub_key);
137 
138   return ret;
139 }
140 
141 guint
gst_asio_enum(GList ** infos)142 gst_asio_enum (GList ** infos)
143 {
144   GList *info_list = nullptr;
145   DWORD index = 0;
146   guint num_device = 0;
147   LSTATUS status;
148   HKEY reg_key = nullptr;
149   WCHAR key_name[512];
150 
151   g_return_val_if_fail (infos != nullptr, 0);
152 
153   status = RegOpenKeyExW (HKEY_LOCAL_MACHINE, L"software\\asio", 0,
154       KEY_READ, &reg_key);
155   while (status == ERROR_SUCCESS) {
156     GstAsioDeviceInfo *info;
157 
158     status = RegEnumKeyW (reg_key, index, key_name, 512);
159     if (status != ERROR_SUCCESS)
160       break;
161 
162     index++;
163     info = gst_asio_enum_new_device_info_from_reg (reg_key, key_name);
164     if (!info)
165       continue;
166 
167     info_list = g_list_append (info_list, info);
168     num_device++;
169   }
170 
171   if (reg_key)
172     RegCloseKey (reg_key);
173 
174   *infos = info_list;
175 
176   return num_device;
177 }
178 
179 GstAsioDeviceInfo *
gst_asio_device_info_copy(const GstAsioDeviceInfo * info)180 gst_asio_device_info_copy (const GstAsioDeviceInfo * info)
181 {
182   GstAsioDeviceInfo *new_info;
183 
184   if (!info)
185     return nullptr;
186 
187   new_info = g_new0 (GstAsioDeviceInfo, 1);
188 
189   new_info->clsid = info->clsid;
190   new_info->sta_model = info->sta_model;
191   new_info->driver_name = g_strdup (info->driver_name);
192   new_info->driver_desc = g_strdup (info->driver_desc);
193 
194   return new_info;
195 }
196 
197 void
gst_asio_device_info_free(GstAsioDeviceInfo * info)198 gst_asio_device_info_free (GstAsioDeviceInfo * info)
199 {
200   if (!info)
201     return;
202 
203   g_free (info->driver_name);
204   g_free (info->driver_desc);
205 
206   g_free (info);
207 }
208 
209 GstAudioFormat
gst_asio_sample_type_to_gst(ASIOSampleType type)210 gst_asio_sample_type_to_gst (ASIOSampleType type)
211 {
212   GstAudioFormat fmt;
213 
214   switch (type) {
215       /*~~ MSB means big endian ~~ */
216     case ASIOSTInt16MSB:
217       fmt = GST_AUDIO_FORMAT_S16BE;
218       break;
219       /* FIXME: also used for 20 bits packed in 24 bits, how do we detect that? */
220     case ASIOSTInt24MSB:
221       fmt = GST_AUDIO_FORMAT_S24BE;
222       break;
223     case ASIOSTInt32MSB:
224       fmt = GST_AUDIO_FORMAT_S32BE;
225       break;
226     case ASIOSTFloat32MSB:
227       fmt = GST_AUDIO_FORMAT_F32BE;
228       break;
229     case ASIOSTFloat64MSB:
230       fmt = GST_AUDIO_FORMAT_F64BE;
231       break;
232       /* All these are aligned to a different boundary than the packing, not sure
233        * how to handle it, let's try the normal S32BE format */
234     case ASIOSTInt32MSB16:
235     case ASIOSTInt32MSB18:
236     case ASIOSTInt32MSB20:
237     case ASIOSTInt32MSB24:
238       fmt = GST_AUDIO_FORMAT_S32BE;
239       break;
240 
241       /*~~ LSB means little endian ~~ */
242     case ASIOSTInt16LSB:
243       fmt = GST_AUDIO_FORMAT_S16LE;
244       break;
245       /* FIXME: also used for 20 bits packed in 24 bits, how do we detect that? */
246     case ASIOSTInt24LSB:
247       fmt = GST_AUDIO_FORMAT_S24LE;
248       break;
249     case ASIOSTInt32LSB:
250       fmt = GST_AUDIO_FORMAT_S32LE;
251       break;
252     case ASIOSTFloat32LSB:
253       fmt = GST_AUDIO_FORMAT_F32LE;
254       break;
255     case ASIOSTFloat64LSB:
256       fmt = GST_AUDIO_FORMAT_F64LE;
257       break;
258       /* All these are aligned to a different boundary than the packing, not sure
259        * how to handle it, let's try the normal S32LE format */
260     case ASIOSTInt32LSB16:
261     case ASIOSTInt32LSB18:
262     case ASIOSTInt32LSB20:
263     case ASIOSTInt32LSB24:
264       GST_WARNING ("weird alignment %ld, trying S32LE", type);
265       fmt = GST_AUDIO_FORMAT_S32LE;
266       break;
267 
268       /*~~ ASIO DSD formats are don't have gstreamer mappings ~~ */
269     case ASIOSTDSDInt8LSB1:
270     case ASIOSTDSDInt8MSB1:
271     case ASIOSTDSDInt8NER8:
272       GST_ERROR ("ASIO DSD formats are not supported");
273       fmt = GST_AUDIO_FORMAT_UNKNOWN;
274       break;
275     default:
276       GST_ERROR ("Unknown asio sample type %ld", type);
277       fmt = GST_AUDIO_FORMAT_UNKNOWN;
278       break;
279   }
280 
281   return fmt;
282 }
283