mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
wasapi: Implement a device provider for probing
Currently only does probing and does not handle messages from endpoints/devices. In the future we want to do proper monitoring which is well-supported in WASAPI. https://bugzilla.gnome.org/show_bug.cgi?id=792897
This commit is contained in:
parent
d6d31064b4
commit
ec6a10ed06
6 changed files with 451 additions and 17 deletions
|
@ -3,7 +3,8 @@ plugin_LTLIBRARIES = libgstwasapi.la
|
||||||
libgstwasapi_la_SOURCES = gstwasapi.c \
|
libgstwasapi_la_SOURCES = gstwasapi.c \
|
||||||
gstwasapisrc.c \
|
gstwasapisrc.c \
|
||||||
gstwasapisink.c \
|
gstwasapisink.c \
|
||||||
gstwasapiutil.c
|
gstwasapiutil.c \
|
||||||
|
gstwasapidevice.c
|
||||||
|
|
||||||
libgstwasapi_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) -DCOBJMACROS=1
|
libgstwasapi_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) -DCOBJMACROS=1
|
||||||
libgstwasapi_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \
|
libgstwasapi_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \
|
||||||
|
@ -13,5 +14,5 @@ libgstwasapi_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
|
||||||
noinst_HEADERS = gstwasapisrc.h \
|
noinst_HEADERS = gstwasapisrc.h \
|
||||||
gstwasapisink.h \
|
gstwasapisink.h \
|
||||||
gstwasapiutil.h
|
gstwasapiutil.h \
|
||||||
|
gstwasapidevice.h
|
||||||
|
|
|
@ -23,15 +23,22 @@
|
||||||
|
|
||||||
#include "gstwasapisink.h"
|
#include "gstwasapisink.h"
|
||||||
#include "gstwasapisrc.h"
|
#include "gstwasapisrc.h"
|
||||||
|
#include "gstwasapidevice.h"
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_init (GstPlugin * plugin)
|
plugin_init (GstPlugin * plugin)
|
||||||
{
|
{
|
||||||
gst_element_register (plugin, "wasapisink", GST_RANK_NONE,
|
if (!gst_element_register (plugin, "wasapisink", GST_RANK_NONE,
|
||||||
GST_TYPE_WASAPI_SINK);
|
GST_TYPE_WASAPI_SINK))
|
||||||
gst_element_register (plugin, "wasapisrc", GST_RANK_NONE,
|
return FALSE;
|
||||||
GST_TYPE_WASAPI_SRC);
|
|
||||||
|
|
||||||
|
if (!gst_element_register (plugin, "wasapisrc", GST_RANK_NONE,
|
||||||
|
GST_TYPE_WASAPI_SRC))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!gst_device_provider_register (plugin, "wasapideviceprovider",
|
||||||
|
GST_RANK_PRIMARY, GST_TYPE_WASAPI_DEVICE_PROVIDER))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
167
sys/wasapi/gstwasapidevice.c
Normal file
167
sys/wasapi/gstwasapidevice.c
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2018 Nirbheek Chauhan <nirbheek@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., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gstwasapidevice.h"
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GstWasapiDeviceProvider, gst_wasapi_device_provider,
|
||||||
|
GST_TYPE_DEVICE_PROVIDER);
|
||||||
|
|
||||||
|
static void gst_wasapi_device_provider_finalize (GObject * object);
|
||||||
|
static GList *gst_wasapi_device_provider_probe (GstDeviceProvider *
|
||||||
|
provider);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_provider_class_init (GstWasapiDeviceProviderClass *
|
||||||
|
klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = gst_wasapi_device_provider_finalize;
|
||||||
|
|
||||||
|
dm_class->probe = gst_wasapi_device_provider_probe;
|
||||||
|
|
||||||
|
gst_device_provider_class_set_static_metadata (dm_class,
|
||||||
|
"WASAPI (Windows Audio Session API) Device Provider",
|
||||||
|
"Source/Sink/Audio", "List WASAPI source and sink devices",
|
||||||
|
"Nirbheek Chauhan <nirbheek@centricular.com>");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_provider_init (GstWasapiDeviceProvider * provider)
|
||||||
|
{
|
||||||
|
CoInitialize (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_provider_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
CoUninitialize ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList *
|
||||||
|
gst_wasapi_device_provider_probe (GstDeviceProvider * provider)
|
||||||
|
{
|
||||||
|
GstWasapiDeviceProvider *self = GST_WASAPI_DEVICE_PROVIDER (provider);
|
||||||
|
GList *devices = NULL;
|
||||||
|
|
||||||
|
if (!gst_wasapi_util_get_devices (GST_ELEMENT (self), TRUE, &devices))
|
||||||
|
GST_ERROR_OBJECT (self, "Failed to enumerate devices");
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GstWasapiDevice begins */
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_DEVICE_STRID = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GstWasapiDevice, gst_wasapi_device, GST_TYPE_DEVICE);
|
||||||
|
|
||||||
|
static void gst_wasapi_device_get_property (GObject * object,
|
||||||
|
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_wasapi_device_set_property (GObject * object,
|
||||||
|
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||||
|
static void gst_wasapi_device_finalize (GObject * object);
|
||||||
|
static GstElement *gst_wasapi_device_create_element (GstDevice * device,
|
||||||
|
const gchar * name);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_class_init (GstWasapiDeviceClass * klass)
|
||||||
|
{
|
||||||
|
GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
dev_class->create_element = gst_wasapi_device_create_element;
|
||||||
|
|
||||||
|
object_class->get_property = gst_wasapi_device_get_property;
|
||||||
|
object_class->set_property = gst_wasapi_device_set_property;
|
||||||
|
object_class->finalize = gst_wasapi_device_finalize;
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class, PROP_DEVICE_STRID,
|
||||||
|
g_param_spec_string ("device", "Device string ID",
|
||||||
|
"Device strId", NULL,
|
||||||
|
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_init (GstWasapiDevice * device)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstWasapiDevice *device = GST_WASAPI_DEVICE (object);
|
||||||
|
|
||||||
|
g_free (device->strid);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (gst_wasapi_device_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstElement *
|
||||||
|
gst_wasapi_device_create_element (GstDevice * device, const gchar * name)
|
||||||
|
{
|
||||||
|
GstWasapiDevice *wasapi_dev = GST_WASAPI_DEVICE (device);
|
||||||
|
GstElement *elem;
|
||||||
|
|
||||||
|
elem = gst_element_factory_make (wasapi_dev->element, name);
|
||||||
|
|
||||||
|
g_object_set (elem, "device", wasapi_dev->strid, NULL);
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_get_property (GObject * object, guint prop_id,
|
||||||
|
GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstWasapiDevice *device = GST_WASAPI_DEVICE_CAST (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_DEVICE_STRID:
|
||||||
|
g_value_set_string (value, device->strid);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_wasapi_device_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstWasapiDevice *device = GST_WASAPI_DEVICE_CAST (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_DEVICE_STRID:
|
||||||
|
device->strid = g_value_dup_string (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
75
sys/wasapi/gstwasapidevice.h
Normal file
75
sys/wasapi/gstwasapidevice.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2018 Nirbheek Chauhan <nirbheek@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., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_WASAPI_DEVICE_H__
|
||||||
|
#define __GST_WASAPI_DEVICE_H__
|
||||||
|
|
||||||
|
#include "gstwasapiutil.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _GstWasapiDeviceProvider GstWasapiDeviceProvider;
|
||||||
|
typedef struct _GstWasapiDeviceProviderClass GstWasapiDeviceProviderClass;
|
||||||
|
|
||||||
|
#define GST_TYPE_WASAPI_DEVICE_PROVIDER (gst_wasapi_device_provider_get_type())
|
||||||
|
#define GST_IS_WASAPI_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WASAPI_DEVICE_PROVIDER))
|
||||||
|
#define GST_IS_WASAPI_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WASAPI_DEVICE_PROVIDER))
|
||||||
|
#define GST_WASAPI_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WASAPI_DEVICE_PROVIDER, GstWasapiDeviceProviderClass))
|
||||||
|
#define GST_WASAPI_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WASAPI_DEVICE_PROVIDER, GstWasapiDeviceProvider))
|
||||||
|
#define GST_WASAPI_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstWasapiDeviceProviderClass))
|
||||||
|
#define GST_WASAPI_DEVICE_PROVIDER_CAST(obj) ((GstWasapiDeviceProvider *)(obj))
|
||||||
|
|
||||||
|
struct _GstWasapiDeviceProvider {
|
||||||
|
GstDeviceProvider parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstWasapiDeviceProviderClass {
|
||||||
|
GstDeviceProviderClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_wasapi_device_provider_get_type (void);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _GstWasapiDevice GstWasapiDevice;
|
||||||
|
typedef struct _GstWasapiDeviceClass GstWasapiDeviceClass;
|
||||||
|
|
||||||
|
#define GST_TYPE_WASAPI_DEVICE (gst_wasapi_device_get_type())
|
||||||
|
#define GST_IS_WASAPI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WASAPI_DEVICE))
|
||||||
|
#define GST_IS_WASAPI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WASAPI_DEVICE))
|
||||||
|
#define GST_WASAPI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WASAPI_DEVICE, GstWasapiDeviceClass))
|
||||||
|
#define GST_WASAPI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WASAPI_DEVICE, GstWasapiDevice))
|
||||||
|
#define GST_WASAPI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstWasapiDeviceClass))
|
||||||
|
#define GST_WASAPI_DEVICE_CAST(obj) ((GstWasapiDevice *)(obj))
|
||||||
|
|
||||||
|
struct _GstWasapiDevice {
|
||||||
|
GstDevice parent;
|
||||||
|
|
||||||
|
gchar *strid;
|
||||||
|
const gchar *element;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstWasapiDeviceClass {
|
||||||
|
GstDeviceClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_wasapi_device_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_WASAPI_DEVICE_H__ */
|
|
@ -23,12 +23,25 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gstwasapiutil.h"
|
#include "gstwasapiutil.h"
|
||||||
|
#include "gstwasapidevice.h"
|
||||||
|
|
||||||
#include <mmdeviceapi.h>
|
#include <mmdeviceapi.h>
|
||||||
|
|
||||||
|
/* This was only added to MinGW in ~2015 and our Cerbero toolchain is too old */
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <functiondiscoverykeys_devpkey.h>
|
||||||
|
#elif !defined(PKEY_Device_FriendlyName)
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <propkey.h>
|
||||||
|
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
|
||||||
|
DEFINE_PROPERTYKEY(PKEY_AudioEngine_DeviceFormat, 0xf19f064d, 0x82c, 0x4e27, 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef __uuidof
|
#ifdef __uuidof
|
||||||
const CLSID CLSID_MMDeviceEnumerator = __uuidof (MMDeviceEnumerator);
|
const CLSID CLSID_MMDeviceEnumerator = __uuidof (MMDeviceEnumerator);
|
||||||
const IID IID_IMMDeviceEnumerator = __uuidof (IMMDeviceEnumerator);
|
const IID IID_IMMDeviceEnumerator = __uuidof (IMMDeviceEnumerator);
|
||||||
|
const IID IID_IMMEndpoint = __uuidof (IMMEndpoint);
|
||||||
const IID IID_IAudioClient = __uuidof (IAudioClient);
|
const IID IID_IAudioClient = __uuidof (IAudioClient);
|
||||||
const IID IID_IAudioRenderClient = __uuidof (IAudioRenderClient);
|
const IID IID_IAudioRenderClient = __uuidof (IAudioRenderClient);
|
||||||
const IID IID_IAudioCaptureClient = __uuidof (IAudioCaptureClient);
|
const IID IID_IAudioCaptureClient = __uuidof (IAudioCaptureClient);
|
||||||
|
@ -44,6 +57,10 @@ const IID IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,
|
||||||
{0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6}
|
{0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IID IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,
|
||||||
|
{0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5}
|
||||||
|
};
|
||||||
|
|
||||||
const IID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,
|
const IID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,
|
||||||
{0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2}
|
{0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2}
|
||||||
};
|
};
|
||||||
|
@ -235,6 +252,176 @@ gst_wasapi_util_hresult_to_string (HRESULT hr)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IMMDeviceEnumerator*
|
||||||
|
gst_wasapi_util_get_device_enumerator (GstElement * element)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
IMMDeviceEnumerator *enumerator = NULL;
|
||||||
|
|
||||||
|
hr = CoCreateInstance (&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
||||||
|
&IID_IMMDeviceEnumerator, (void **) &enumerator);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
GST_ERROR_OBJECT (element, "CoCreateInstance (MMDeviceEnumerator) failed"
|
||||||
|
": %s", gst_wasapi_util_hresult_to_string (hr));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_wasapi_util_get_devices (GstElement * element, gboolean active,
|
||||||
|
GList ** devices)
|
||||||
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
static GstStaticCaps scaps = GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS);
|
||||||
|
DWORD dwStateMask = active ? DEVICE_STATE_ACTIVE : DEVICE_STATEMASK_ALL;
|
||||||
|
IMMDeviceCollection *device_collection = NULL;
|
||||||
|
IMMDeviceEnumerator *enumerator = NULL;
|
||||||
|
const gchar *device_class, *element_name;
|
||||||
|
guint ii, count;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
*devices = NULL;
|
||||||
|
|
||||||
|
enumerator = gst_wasapi_util_get_device_enumerator (element);
|
||||||
|
if (!enumerator)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
hr = IMMDeviceEnumerator_EnumAudioEndpoints (enumerator, eAll, dwStateMask,
|
||||||
|
&device_collection);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
GST_ERROR_OBJECT (element, "IMMDeviceEnumerator::EnumAudioEndpoints "
|
||||||
|
"failed: %s", gst_wasapi_util_hresult_to_string (hr));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = IMMDeviceCollection_GetCount (device_collection, &count);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
GST_ERROR_OBJECT (element, "Failed to count devices: %s",
|
||||||
|
gst_wasapi_util_hresult_to_string (hr));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a GList of GstDevices* to return */
|
||||||
|
for (ii = 0; ii < count; ii++) {
|
||||||
|
IMMDevice *item = NULL;
|
||||||
|
IMMEndpoint *endpoint = NULL;
|
||||||
|
IAudioClient *client = NULL;
|
||||||
|
IPropertyStore *prop_store = NULL;
|
||||||
|
WAVEFORMATEX *format = NULL;
|
||||||
|
gchar *description = NULL;
|
||||||
|
gchar *strid = NULL;
|
||||||
|
EDataFlow dataflow;
|
||||||
|
PROPVARIANT var;
|
||||||
|
wchar_t *wstrid;
|
||||||
|
GstDevice *device;
|
||||||
|
GstStructure *props;
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
hr = IMMDeviceCollection_Item (device_collection, ii, &item);
|
||||||
|
if (hr != S_OK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hr = IMMDevice_QueryInterface (item, &IID_IMMEndpoint, (void **) &endpoint);
|
||||||
|
if (hr != S_OK)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
hr = IMMEndpoint_GetDataFlow (endpoint, &dataflow);
|
||||||
|
if (hr != S_OK)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
if (dataflow == eRender) {
|
||||||
|
device_class = "Audio/Sink";
|
||||||
|
element_name = "wasapisink";
|
||||||
|
} else {
|
||||||
|
device_class = "Audio/Source";
|
||||||
|
element_name = "wasapisrc";
|
||||||
|
}
|
||||||
|
|
||||||
|
PropVariantInit (&var);
|
||||||
|
|
||||||
|
hr = IMMDevice_GetId (item, &wstrid);
|
||||||
|
if (hr != S_OK)
|
||||||
|
goto next;
|
||||||
|
strid = g_utf16_to_utf8 (wstrid, -1, NULL, NULL, NULL);
|
||||||
|
CoTaskMemFree (wstrid);
|
||||||
|
|
||||||
|
hr = IMMDevice_OpenPropertyStore (item, STGM_READ, &prop_store);
|
||||||
|
if (hr != S_OK)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
/* NOTE: More properties can be added as needed from here:
|
||||||
|
* https://msdn.microsoft.com/en-us/library/windows/desktop/dd370794(v=vs.85).aspx */
|
||||||
|
hr = IPropertyStore_GetValue (prop_store, &PKEY_Device_FriendlyName, &var);
|
||||||
|
if (hr != S_OK)
|
||||||
|
goto next;
|
||||||
|
description = g_utf16_to_utf8 (var.pwszVal, -1, NULL, NULL, NULL);
|
||||||
|
PropVariantClear (&var);
|
||||||
|
|
||||||
|
/* Get the audio client so we can fetch the mix format for shared mode
|
||||||
|
* to get the device format for exclusive mode (or something close to that)
|
||||||
|
* fetch PKEY_AudioEngine_DeviceFormat from the property store. */
|
||||||
|
hr = IMMDevice_Activate (item, &IID_IAudioClient, CLSCTX_ALL, NULL,
|
||||||
|
(void **) &client);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
GST_ERROR_OBJECT (element, "IMMDevice::Activate (IID_IAudioClient) failed"
|
||||||
|
"on %s: %s", strid, gst_wasapi_util_hresult_to_string (hr));
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = IAudioClient_GetMixFormat (client, &format);
|
||||||
|
if (hr != S_OK || format == NULL) {
|
||||||
|
GST_ERROR_OBJECT ("GetMixFormat failed on %s: %s", strid,
|
||||||
|
gst_wasapi_util_hresult_to_string (hr));
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gst_wasapi_util_parse_waveformatex ((WAVEFORMATEXTENSIBLE *) format,
|
||||||
|
gst_static_caps_get (&scaps), &caps, NULL))
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
/* Set some useful properties */
|
||||||
|
props = gst_structure_new ("wasapi-proplist",
|
||||||
|
"device.api", G_TYPE_STRING, "wasapi",
|
||||||
|
"device.strid", G_TYPE_STRING, GST_STR_NULL (strid),
|
||||||
|
"wasapi.device.description", G_TYPE_STRING, description, NULL);
|
||||||
|
|
||||||
|
device = g_object_new (GST_TYPE_WASAPI_DEVICE, "device", strid,
|
||||||
|
"display-name", description, "caps", caps,
|
||||||
|
"device-class", device_class, "properties", props, NULL);
|
||||||
|
GST_WASAPI_DEVICE(device)->element = element_name;
|
||||||
|
|
||||||
|
gst_structure_free (props);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
*devices = g_list_prepend (*devices, device);
|
||||||
|
|
||||||
|
next:
|
||||||
|
PropVariantClear (&var);
|
||||||
|
if (prop_store)
|
||||||
|
IUnknown_Release (prop_store);
|
||||||
|
if (endpoint)
|
||||||
|
IUnknown_Release (endpoint);
|
||||||
|
if (client)
|
||||||
|
IUnknown_Release (client);
|
||||||
|
if (item)
|
||||||
|
IUnknown_Release (item);
|
||||||
|
if (description)
|
||||||
|
g_free (description);
|
||||||
|
if (strid)
|
||||||
|
g_free (strid);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (enumerator)
|
||||||
|
IUnknown_Release (enumerator);
|
||||||
|
if (device_collection)
|
||||||
|
IUnknown_Release (device_collection);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_wasapi_util_get_device_client (GstElement * element,
|
gst_wasapi_util_get_device_client (GstElement * element,
|
||||||
gboolean capture, gint role, const wchar_t * device_strid,
|
gboolean capture, gint role, const wchar_t * device_strid,
|
||||||
|
@ -246,13 +433,8 @@ gst_wasapi_util_get_device_client (GstElement * element,
|
||||||
IMMDevice *device = NULL;
|
IMMDevice *device = NULL;
|
||||||
IAudioClient *client = NULL;
|
IAudioClient *client = NULL;
|
||||||
|
|
||||||
hr = CoCreateInstance (&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
|
if (!(enumerator = gst_wasapi_util_get_device_enumerator (element)))
|
||||||
&IID_IMMDeviceEnumerator, (void **) &enumerator);
|
|
||||||
if (hr != S_OK) {
|
|
||||||
GST_ERROR_OBJECT (element, "CoCreateInstance (MMDeviceEnumerator) failed"
|
|
||||||
": %s", gst_wasapi_util_hresult_to_string (hr));
|
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
|
||||||
|
|
||||||
if (!device_strid) {
|
if (!device_strid) {
|
||||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator,
|
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator,
|
||||||
|
|
|
@ -52,10 +52,12 @@ gint gst_wasapi_erole_to_device_role (gint erole);
|
||||||
|
|
||||||
const gchar *gst_wasapi_util_hresult_to_string (HRESULT hr);
|
const gchar *gst_wasapi_util_hresult_to_string (HRESULT hr);
|
||||||
|
|
||||||
gboolean
|
gboolean gst_wasapi_util_get_devices (GstElement * element, gboolean active,
|
||||||
gst_wasapi_util_get_device_client (GstElement * element,
|
GList ** devices);
|
||||||
gboolean capture,
|
|
||||||
gint role, const wchar_t * device_name, IAudioClient ** ret_client);
|
gboolean gst_wasapi_util_get_device_client (GstElement * element,
|
||||||
|
gboolean capture, gint role, const wchar_t * device_strid,
|
||||||
|
IAudioClient ** ret_client);
|
||||||
|
|
||||||
gboolean gst_wasapi_util_get_render_client (GstElement * element,
|
gboolean gst_wasapi_util_get_render_client (GstElement * element,
|
||||||
IAudioClient * client, IAudioRenderClient ** ret_render_client);
|
IAudioClient * client, IAudioRenderClient ** ret_render_client);
|
||||||
|
|
Loading…
Reference in a new issue