mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 16:26:39 +00:00
282 lines
7.1 KiB
C++
282 lines
7.1 KiB
C++
/* 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;
|
|
}
|