mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +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.
|
||||
* For example, perhaps we should automatically switch to the new device if
|
||||
* 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)) {
|
||||
if (!self->device_strid)
|
||||
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_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
|
||||
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
|
||||
&devicep_frames))
|
||||
FALSE, &devicep_frames))
|
||||
goto beach;
|
||||
} else {
|
||||
if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
|
||||
self->client, self->mix_format, self->sharemode, self->low_latency,
|
||||
&devicep_frames))
|
||||
FALSE, &devicep_frames))
|
||||
goto beach;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|||
GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS));
|
||||
|
||||
#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE
|
||||
#define DEFAULT_LOOPBACK FALSE
|
||||
#define DEFAULT_EXCLUSIVE FALSE
|
||||
#define DEFAULT_LOW_LATENCY FALSE
|
||||
#define DEFAULT_AUDIOCLIENT3 FALSE
|
||||
|
@ -62,6 +63,7 @@ enum
|
|||
PROP_0,
|
||||
PROP_ROLE,
|
||||
PROP_DEVICE,
|
||||
PROP_LOOPBACK,
|
||||
PROP_EXCLUSIVE,
|
||||
PROP_LOW_LATENCY,
|
||||
PROP_AUDIOCLIENT3
|
||||
|
@ -118,6 +120,12 @@ gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
|
|||
"WASAPI playback device as a GUID string",
|
||||
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,
|
||||
PROP_EXCLUSIVE,
|
||||
g_param_spec_boolean ("exclusive", "Exclusive mode",
|
||||
|
@ -170,6 +178,7 @@ gst_wasapi_src_init (GstWasapiSrc * self)
|
|||
|
||||
self->role = DEFAULT_ROLE;
|
||||
self->sharemode = AUDCLNT_SHAREMODE_SHARED;
|
||||
self->loopback = DEFAULT_LOOPBACK;
|
||||
self->low_latency = DEFAULT_LOW_LATENCY;
|
||||
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case PROP_LOOPBACK:
|
||||
self->loopback = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_EXCLUSIVE:
|
||||
self->sharemode = g_value_get_boolean (value)
|
||||
? 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_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL);
|
||||
break;
|
||||
case PROP_LOOPBACK:
|
||||
g_value_set_boolean (value, self->loopback);
|
||||
break;
|
||||
case PROP_EXCLUSIVE:
|
||||
g_value_set_boolean (value,
|
||||
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.
|
||||
* For example, perhaps we should automatically switch to the new device if
|
||||
* the default device is changed and a device isn't explicitly selected. */
|
||||
if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), TRUE,
|
||||
self->role, self->device_strid, &device, &client)) {
|
||||
if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self),
|
||||
self->loopback ? eRender : eCapture, self->role, self->device_strid,
|
||||
&device, &client)) {
|
||||
if (!self->device_strid)
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
||||
("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_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
|
||||
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
|
||||
&devicep_frames))
|
||||
self->loopback, &devicep_frames))
|
||||
goto beach;
|
||||
} else {
|
||||
if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
|
||||
self->client, self->mix_format, self->sharemode, self->low_latency,
|
||||
&devicep_frames))
|
||||
self->loopback, &devicep_frames))
|
||||
goto beach;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ struct _GstWasapiSrc
|
|||
/* properties */
|
||||
gint role;
|
||||
gint sharemode;
|
||||
gboolean loopback;
|
||||
gboolean low_latency;
|
||||
gboolean try_audioclient3;
|
||||
wchar_t *device_strid;
|
||||
|
|
|
@ -543,7 +543,7 @@ out:
|
|||
|
||||
gboolean
|
||||
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)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
|
@ -556,8 +556,8 @@ gst_wasapi_util_get_device_client (GstElement * self,
|
|||
goto beach;
|
||||
|
||||
if (!device_strid) {
|
||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator,
|
||||
capture ? eCapture : eRender, role, &device);
|
||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator, data_flow,
|
||||
role, &device);
|
||||
HR_FAILED_GOTO (hr, IMMDeviceEnumerator::GetDefaultAudioEndpoint, beach);
|
||||
} else {
|
||||
hr = IMMDeviceEnumerator_GetDevice (enumerator, device_strid, &device);
|
||||
|
@ -840,11 +840,11 @@ gboolean
|
|||
gst_wasapi_util_initialize_audioclient (GstElement * self,
|
||||
GstAudioRingBufferSpec * spec, IAudioClient * client,
|
||||
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 device_period, device_buffer_duration;
|
||||
guint rate;
|
||||
guint rate, stream_flags;
|
||||
HRESULT hr;
|
||||
|
||||
hr = IAudioClient_GetDevicePeriod (client, &default_period, &min_period);
|
||||
|
@ -874,8 +874,12 @@ gst_wasapi_util_initialize_audioclient (GstElement * self,
|
|||
if (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
|
||||
CoInitialize (NULL);
|
||||
|
||||
hr = IAudioClient_Initialize (client, sharemode,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_buffer_duration,
|
||||
stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
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 */
|
||||
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 "
|
||||
"(%i frames, %i rate)", (int) device_period, n_frames, rate);
|
||||
|
||||
hr = IAudioClient_Initialize (client, sharemode,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_period,
|
||||
device_period, format, NULL);
|
||||
hr = IAudioClient_Initialize (client, sharemode, stream_flags,
|
||||
device_period, device_period, format, NULL);
|
||||
}
|
||||
HR_FAILED_RET (hr, IAudioClient::Initialize, FALSE);
|
||||
|
||||
|
@ -909,9 +912,11 @@ gst_wasapi_util_initialize_audioclient (GstElement * self,
|
|||
gboolean
|
||||
gst_wasapi_util_initialize_audioclient3 (GstElement * self,
|
||||
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;
|
||||
gint stream_flags;
|
||||
guint rate, devicep_frames;
|
||||
guint defaultp_frames, fundp_frames, minp_frames, maxp_frames;
|
||||
WAVEFORMATEX *tmpf;
|
||||
|
@ -937,8 +942,12 @@ gst_wasapi_util_initialize_audioclient3 (GstElement * self,
|
|||
devicep_frames = tmp * fundp_frames;
|
||||
}
|
||||
|
||||
hr = IAudioClient3_InitializeSharedAudioStream (client,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, devicep_frames, format, NULL);
|
||||
stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
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 = IAudioClient3_GetCurrentSharedModeEnginePeriod (client, &tmpf,
|
||||
|
|
|
@ -79,7 +79,7 @@ gboolean gst_wasapi_util_get_devices (GstElement * element, gboolean active,
|
|||
GList ** devices);
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
GstAudioRingBufferSpec * spec, IAudioClient * client,
|
||||
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,
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue