mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-03 17:22:29 +00:00
wasapisrc: Implement loopback recording
Now, when you set loopback=true on wasapisrc, the `device` property should refer to a sink (render) device for loopback recording. If the `device` property is not set, the default sink device is used.
This commit is contained in:
parent
8649cef462
commit
affb0182c6
5 changed files with 50 additions and 23 deletions
|
@ -378,7 +378,7 @@ gst_wasapi_sink_open (GstAudioSink * asink)
|
||||||
* even if the old device was unplugged. We need to handle this somehow.
|
* even if the old device was unplugged. We need to handle this somehow.
|
||||||
* For example, perhaps we should automatically switch to the new device if
|
* For example, perhaps we should automatically switch to the new device if
|
||||||
* the default device is changed and a device isn't explicitly selected. */
|
* the default device is changed and a device isn't explicitly selected. */
|
||||||
if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), FALSE,
|
if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), eRender,
|
||||||
self->role, self->device_strid, &device, &client)) {
|
self->role, self->device_strid, &device, &client)) {
|
||||||
if (!self->device_strid)
|
if (!self->device_strid)
|
||||||
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL),
|
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL),
|
||||||
|
@ -452,12 +452,12 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
|
||||||
if (gst_wasapi_sink_can_audioclient3 (self)) {
|
if (gst_wasapi_sink_can_audioclient3 (self)) {
|
||||||
if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
|
if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
|
||||||
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
|
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
|
||||||
&devicep_frames))
|
FALSE, &devicep_frames))
|
||||||
goto beach;
|
goto beach;
|
||||||
} else {
|
} else {
|
||||||
if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
|
if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
|
||||||
self->client, self->mix_format, self->sharemode, self->low_latency,
|
self->client, self->mix_format, self->sharemode, self->low_latency,
|
||||||
&devicep_frames))
|
FALSE, &devicep_frames))
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS));
|
GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS));
|
||||||
|
|
||||||
#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE
|
#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE
|
||||||
|
#define DEFAULT_LOOPBACK FALSE
|
||||||
#define DEFAULT_EXCLUSIVE FALSE
|
#define DEFAULT_EXCLUSIVE FALSE
|
||||||
#define DEFAULT_LOW_LATENCY FALSE
|
#define DEFAULT_LOW_LATENCY FALSE
|
||||||
#define DEFAULT_AUDIOCLIENT3 FALSE
|
#define DEFAULT_AUDIOCLIENT3 FALSE
|
||||||
|
@ -62,6 +63,7 @@ enum
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_ROLE,
|
PROP_ROLE,
|
||||||
PROP_DEVICE,
|
PROP_DEVICE,
|
||||||
|
PROP_LOOPBACK,
|
||||||
PROP_EXCLUSIVE,
|
PROP_EXCLUSIVE,
|
||||||
PROP_LOW_LATENCY,
|
PROP_LOW_LATENCY,
|
||||||
PROP_AUDIOCLIENT3
|
PROP_AUDIOCLIENT3
|
||||||
|
@ -118,6 +120,12 @@ gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
|
||||||
"WASAPI playback device as a GUID string",
|
"WASAPI playback device as a GUID string",
|
||||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
NULL, 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, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class,
|
g_object_class_install_property (gobject_class,
|
||||||
PROP_EXCLUSIVE,
|
PROP_EXCLUSIVE,
|
||||||
g_param_spec_boolean ("exclusive", "Exclusive mode",
|
g_param_spec_boolean ("exclusive", "Exclusive mode",
|
||||||
|
@ -170,6 +178,7 @@ gst_wasapi_src_init (GstWasapiSrc * self)
|
||||||
|
|
||||||
self->role = DEFAULT_ROLE;
|
self->role = DEFAULT_ROLE;
|
||||||
self->sharemode = AUDCLNT_SHAREMODE_SHARED;
|
self->sharemode = AUDCLNT_SHAREMODE_SHARED;
|
||||||
|
self->loopback = DEFAULT_LOOPBACK;
|
||||||
self->low_latency = DEFAULT_LOW_LATENCY;
|
self->low_latency = DEFAULT_LOW_LATENCY;
|
||||||
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
|
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
|
||||||
self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
|
self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||||||
|
@ -239,6 +248,9 @@ gst_wasapi_src_set_property (GObject * object, guint prop_id,
|
||||||
device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL;
|
device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PROP_LOOPBACK:
|
||||||
|
self->loopback = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
case PROP_EXCLUSIVE:
|
case PROP_EXCLUSIVE:
|
||||||
self->sharemode = g_value_get_boolean (value)
|
self->sharemode = g_value_get_boolean (value)
|
||||||
? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
|
? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
|
||||||
|
@ -269,6 +281,9 @@ gst_wasapi_src_get_property (GObject * object, guint prop_id,
|
||||||
g_value_take_string (value, self->device_strid ?
|
g_value_take_string (value, self->device_strid ?
|
||||||
g_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL);
|
g_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL);
|
||||||
break;
|
break;
|
||||||
|
case PROP_LOOPBACK:
|
||||||
|
g_value_set_boolean (value, self->loopback);
|
||||||
|
break;
|
||||||
case PROP_EXCLUSIVE:
|
case PROP_EXCLUSIVE:
|
||||||
g_value_set_boolean (value,
|
g_value_set_boolean (value,
|
||||||
self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE);
|
self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE);
|
||||||
|
@ -369,8 +384,9 @@ gst_wasapi_src_open (GstAudioSrc * asrc)
|
||||||
* even if the old device was unplugged. We need to handle this somehow.
|
* even if the old device was unplugged. We need to handle this somehow.
|
||||||
* For example, perhaps we should automatically switch to the new device if
|
* For example, perhaps we should automatically switch to the new device if
|
||||||
* the default device is changed and a device isn't explicitly selected. */
|
* the default device is changed and a device isn't explicitly selected. */
|
||||||
if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), TRUE,
|
if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self),
|
||||||
self->role, self->device_strid, &device, &client)) {
|
self->loopback ? eRender : eCapture, self->role, self->device_strid,
|
||||||
|
&device, &client)) {
|
||||||
if (!self->device_strid)
|
if (!self->device_strid)
|
||||||
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
||||||
("Failed to get default device"));
|
("Failed to get default device"));
|
||||||
|
@ -419,12 +435,12 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
|
||||||
if (gst_wasapi_src_can_audioclient3 (self)) {
|
if (gst_wasapi_src_can_audioclient3 (self)) {
|
||||||
if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
|
if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
|
||||||
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
|
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
|
||||||
&devicep_frames))
|
self->loopback, &devicep_frames))
|
||||||
goto beach;
|
goto beach;
|
||||||
} else {
|
} else {
|
||||||
if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
|
if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
|
||||||
self->client, self->mix_format, self->sharemode, self->low_latency,
|
self->client, self->mix_format, self->sharemode, self->low_latency,
|
||||||
&devicep_frames))
|
self->loopback, &devicep_frames))
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct _GstWasapiSrc
|
||||||
/* properties */
|
/* properties */
|
||||||
gint role;
|
gint role;
|
||||||
gint sharemode;
|
gint sharemode;
|
||||||
|
gboolean loopback;
|
||||||
gboolean low_latency;
|
gboolean low_latency;
|
||||||
gboolean try_audioclient3;
|
gboolean try_audioclient3;
|
||||||
wchar_t *device_strid;
|
wchar_t *device_strid;
|
||||||
|
|
|
@ -543,7 +543,7 @@ out:
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
gst_wasapi_util_get_device_client (GstElement * self,
|
gst_wasapi_util_get_device_client (GstElement * self,
|
||||||
gboolean capture, gint role, const wchar_t * device_strid,
|
gint data_flow, gint role, const wchar_t * device_strid,
|
||||||
IMMDevice ** ret_device, IAudioClient ** ret_client)
|
IMMDevice ** ret_device, IAudioClient ** ret_client)
|
||||||
{
|
{
|
||||||
gboolean res = FALSE;
|
gboolean res = FALSE;
|
||||||
|
@ -556,8 +556,8 @@ gst_wasapi_util_get_device_client (GstElement * self,
|
||||||
goto beach;
|
goto beach;
|
||||||
|
|
||||||
if (!device_strid) {
|
if (!device_strid) {
|
||||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator,
|
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator, data_flow,
|
||||||
capture ? eCapture : eRender, role, &device);
|
role, &device);
|
||||||
HR_FAILED_GOTO (hr, IMMDeviceEnumerator::GetDefaultAudioEndpoint, beach);
|
HR_FAILED_GOTO (hr, IMMDeviceEnumerator::GetDefaultAudioEndpoint, beach);
|
||||||
} else {
|
} else {
|
||||||
hr = IMMDeviceEnumerator_GetDevice (enumerator, device_strid, &device);
|
hr = IMMDeviceEnumerator_GetDevice (enumerator, device_strid, &device);
|
||||||
|
@ -840,11 +840,11 @@ gboolean
|
||||||
gst_wasapi_util_initialize_audioclient (GstElement * self,
|
gst_wasapi_util_initialize_audioclient (GstElement * self,
|
||||||
GstAudioRingBufferSpec * spec, IAudioClient * client,
|
GstAudioRingBufferSpec * spec, IAudioClient * client,
|
||||||
WAVEFORMATEX * format, guint sharemode, gboolean low_latency,
|
WAVEFORMATEX * format, guint sharemode, gboolean low_latency,
|
||||||
guint * ret_devicep_frames)
|
gboolean loopback, guint * ret_devicep_frames)
|
||||||
{
|
{
|
||||||
REFERENCE_TIME default_period, min_period;
|
REFERENCE_TIME default_period, min_period;
|
||||||
REFERENCE_TIME device_period, device_buffer_duration;
|
REFERENCE_TIME device_period, device_buffer_duration;
|
||||||
guint rate;
|
guint rate, stream_flags;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
hr = IAudioClient_GetDevicePeriod (client, &default_period, &min_period);
|
hr = IAudioClient_GetDevicePeriod (client, &default_period, &min_period);
|
||||||
|
@ -874,8 +874,12 @@ gst_wasapi_util_initialize_audioclient (GstElement * self,
|
||||||
if (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
|
if (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
|
||||||
CoInitialize (NULL);
|
CoInitialize (NULL);
|
||||||
|
|
||||||
hr = IAudioClient_Initialize (client, sharemode,
|
stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_buffer_duration,
|
if (loopback)
|
||||||
|
stream_flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
|
||||||
|
|
||||||
|
hr = IAudioClient_Initialize (client, sharemode, stream_flags,
|
||||||
|
device_buffer_duration,
|
||||||
/* This must always be 0 in shared mode */
|
/* This must always be 0 in shared mode */
|
||||||
sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : device_period, format, NULL);
|
sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : device_period, format, NULL);
|
||||||
|
|
||||||
|
@ -895,9 +899,8 @@ gst_wasapi_util_initialize_audioclient (GstElement * self,
|
||||||
GST_WARNING_OBJECT (self, "trying to re-initialize with period %i "
|
GST_WARNING_OBJECT (self, "trying to re-initialize with period %i "
|
||||||
"(%i frames, %i rate)", (int) device_period, n_frames, rate);
|
"(%i frames, %i rate)", (int) device_period, n_frames, rate);
|
||||||
|
|
||||||
hr = IAudioClient_Initialize (client, sharemode,
|
hr = IAudioClient_Initialize (client, sharemode, stream_flags,
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_period,
|
device_period, device_period, format, NULL);
|
||||||
device_period, format, NULL);
|
|
||||||
}
|
}
|
||||||
HR_FAILED_RET (hr, IAudioClient::Initialize, FALSE);
|
HR_FAILED_RET (hr, IAudioClient::Initialize, FALSE);
|
||||||
|
|
||||||
|
@ -909,9 +912,11 @@ gst_wasapi_util_initialize_audioclient (GstElement * self,
|
||||||
gboolean
|
gboolean
|
||||||
gst_wasapi_util_initialize_audioclient3 (GstElement * self,
|
gst_wasapi_util_initialize_audioclient3 (GstElement * self,
|
||||||
GstAudioRingBufferSpec * spec, IAudioClient3 * client,
|
GstAudioRingBufferSpec * spec, IAudioClient3 * client,
|
||||||
WAVEFORMATEX * format, gboolean low_latency, guint * ret_devicep_frames)
|
WAVEFORMATEX * format, gboolean low_latency, gboolean loopback,
|
||||||
|
guint * ret_devicep_frames)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
gint stream_flags;
|
||||||
guint rate, devicep_frames;
|
guint rate, devicep_frames;
|
||||||
guint defaultp_frames, fundp_frames, minp_frames, maxp_frames;
|
guint defaultp_frames, fundp_frames, minp_frames, maxp_frames;
|
||||||
WAVEFORMATEX *tmpf;
|
WAVEFORMATEX *tmpf;
|
||||||
|
@ -937,8 +942,12 @@ gst_wasapi_util_initialize_audioclient3 (GstElement * self,
|
||||||
devicep_frames = tmp * fundp_frames;
|
devicep_frames = tmp * fundp_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = IAudioClient3_InitializeSharedAudioStream (client,
|
stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, devicep_frames, format, NULL);
|
if (loopback)
|
||||||
|
stream_flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
|
||||||
|
|
||||||
|
hr = IAudioClient3_InitializeSharedAudioStream (client, stream_flags,
|
||||||
|
devicep_frames, format, NULL);
|
||||||
HR_FAILED_RET (hr, IAudioClient3::InitializeSharedAudioStream, FALSE);
|
HR_FAILED_RET (hr, IAudioClient3::InitializeSharedAudioStream, FALSE);
|
||||||
|
|
||||||
hr = IAudioClient3_GetCurrentSharedModeEnginePeriod (client, &tmpf,
|
hr = IAudioClient3_GetCurrentSharedModeEnginePeriod (client, &tmpf,
|
||||||
|
|
|
@ -79,7 +79,7 @@ gboolean gst_wasapi_util_get_devices (GstElement * element, gboolean active,
|
||||||
GList ** devices);
|
GList ** devices);
|
||||||
|
|
||||||
gboolean gst_wasapi_util_get_device_client (GstElement * element,
|
gboolean gst_wasapi_util_get_device_client (GstElement * element,
|
||||||
gboolean capture, gint role, const wchar_t * device_strid,
|
gint data_flow, gint role, const wchar_t * device_strid,
|
||||||
IMMDevice ** ret_device, IAudioClient ** ret_client);
|
IMMDevice ** ret_device, IAudioClient ** ret_client);
|
||||||
|
|
||||||
gboolean gst_wasapi_util_get_device_format (GstElement * element,
|
gboolean gst_wasapi_util_get_device_format (GstElement * element,
|
||||||
|
@ -107,11 +107,12 @@ void gst_wasapi_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec,
|
||||||
gboolean gst_wasapi_util_initialize_audioclient (GstElement * element,
|
gboolean gst_wasapi_util_initialize_audioclient (GstElement * element,
|
||||||
GstAudioRingBufferSpec * spec, IAudioClient * client,
|
GstAudioRingBufferSpec * spec, IAudioClient * client,
|
||||||
WAVEFORMATEX * format, guint sharemode, gboolean low_latency,
|
WAVEFORMATEX * format, guint sharemode, gboolean low_latency,
|
||||||
guint * ret_devicep_frames);
|
gboolean loopback, guint * ret_devicep_frames);
|
||||||
|
|
||||||
gboolean gst_wasapi_util_initialize_audioclient3 (GstElement * element,
|
gboolean gst_wasapi_util_initialize_audioclient3 (GstElement * element,
|
||||||
GstAudioRingBufferSpec * spec, IAudioClient3 * client,
|
GstAudioRingBufferSpec * spec, IAudioClient3 * client,
|
||||||
WAVEFORMATEX * format, gboolean low_latency, guint * ret_devicep_frames);
|
WAVEFORMATEX * format, gboolean low_latency, gboolean loopback,
|
||||||
|
guint * ret_devicep_frames);
|
||||||
|
|
||||||
HANDLE gst_wasapi_util_set_thread_characteristics (void);
|
HANDLE gst_wasapi_util_set_thread_characteristics (void);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue