wasapi: Use IAudioClient3 interface when available

This allows us to request ultra-low-latency device periods even in
shared mode. However, this requires good drivers and Windows 10, so
we only enable this when we detect that we are running on Windows 10
at runtime.

You can forcibly disable this feature on Windows 10 by setting
GST_WASAPI_DISABLE_AUDIOCLIENT3=1 in the environment.
This commit is contained in:
Nirbheek Chauhan 2018-02-14 12:13:36 +05:30
parent 16af66ee95
commit 0cb11c15ed
5 changed files with 434 additions and 124 deletions

View file

@ -0,0 +1,237 @@
/*
* Structure and enum definitions are from audioclient.h in the Windows 10 SDK
*
* These should be defined by MinGW, but they aren't yet since they're very new
* so we keep a copy in our tree. All definitions are guarded, so it should be
* fine to always include this even when building with MSVC.
*/
#pragma once
#ifndef __IAudioClient3_FWD_DEFINED__
#define __IAudioClient3_FWD_DEFINED__
typedef interface IAudioClient3 IAudioClient3;
#endif /* __IAudioClient3_FWD_DEFINED__ */
#ifndef __IAudioClient3_INTERFACE_DEFINED__
#define __IAudioClient3_INTERFACE_DEFINED__
#ifndef AUDIO_STREAM_CATEGORY
typedef enum _AUDIO_STREAM_CATEGORY {
AudioCategory_Other = 0,
AudioCategory_ForegroundOnlyMedia,
AudioCategory_BackgroundCapableMedia,
AudioCategory_Communications,
AudioCategory_Alerts,
AudioCategory_SoundEffects,
AudioCategory_GameEffects,
AudioCategory_GameMedia,
AudioCategory_GameChat,
AudioCategory_Speech,
AudioCategory_Movie,
AudioCategory_Media
} AUDIO_STREAM_CATEGORY;
#endif
#ifndef AUDCLNT_STREAMOPTIONS
typedef enum AUDCLNT_STREAMOPTIONS
{
AUDCLNT_STREAMOPTIONS_NONE = 0,
AUDCLNT_STREAMOPTIONS_RAW = 0x1,
AUDCLNT_STREAMOPTIONS_MATCH_FORMAT = 0x2
} AUDCLNT_STREAMOPTIONS;
#endif
#ifndef AudioClientProperties
typedef struct AudioClientProperties
{
UINT32 cbSize;
BOOL bIsOffload;
AUDIO_STREAM_CATEGORY eCategory;
AUDCLNT_STREAMOPTIONS Options;
} AudioClientProperties;
#endif
EXTERN_C const IID IID_IAudioClient3;
typedef struct IAudioClient3Vtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IAudioClient3 * This,
REFIID riid,
void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IAudioClient3 * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IAudioClient3 * This);
HRESULT ( STDMETHODCALLTYPE *Initialize )(
IAudioClient3 * This,
AUDCLNT_SHAREMODE ShareMode,
DWORD StreamFlags,
REFERENCE_TIME hnsBufferDuration,
REFERENCE_TIME hnsPeriodicity,
const WAVEFORMATEX *pFormat,
LPCGUID AudioSessionGuid);
HRESULT ( STDMETHODCALLTYPE *GetBufferSize )(
IAudioClient3 * This,
UINT32 *pNumBufferFrames);
HRESULT ( STDMETHODCALLTYPE *GetStreamLatency )(
IAudioClient3 * This,
REFERENCE_TIME *phnsLatency);
HRESULT ( STDMETHODCALLTYPE *GetCurrentPadding )(
IAudioClient3 * This,
UINT32 *pNumPaddingFrames);
HRESULT ( STDMETHODCALLTYPE *IsFormatSupported )(
IAudioClient3 * This,
AUDCLNT_SHAREMODE ShareMode,
const WAVEFORMATEX *pFormat,
WAVEFORMATEX **ppClosestMatch);
HRESULT ( STDMETHODCALLTYPE *GetMixFormat )(
IAudioClient3 * This,
WAVEFORMATEX **ppDeviceFormat);
HRESULT ( STDMETHODCALLTYPE *GetDevicePeriod )(
IAudioClient3 * This,
REFERENCE_TIME *phnsDefaultDevicePeriod,
REFERENCE_TIME *phnsMinimumDevicePeriod);
HRESULT ( STDMETHODCALLTYPE *Start )(
IAudioClient3 * This);
HRESULT ( STDMETHODCALLTYPE *Stop )(
IAudioClient3 * This);
HRESULT ( STDMETHODCALLTYPE *Reset )(
IAudioClient3 * This);
HRESULT ( STDMETHODCALLTYPE *SetEventHandle )(
IAudioClient3 * This,
HANDLE eventHandle);
HRESULT ( STDMETHODCALLTYPE *GetService )(
IAudioClient3 * This,
REFIID riid,
void **ppv);
HRESULT ( STDMETHODCALLTYPE *IsOffloadCapable )(
IAudioClient3 * This,
AUDIO_STREAM_CATEGORY Category,
BOOL *pbOffloadCapable);
HRESULT ( STDMETHODCALLTYPE *SetClientProperties )(
IAudioClient3 * This,
const AudioClientProperties *pProperties);
HRESULT ( STDMETHODCALLTYPE *GetBufferSizeLimits )(
IAudioClient3 * This,
const WAVEFORMATEX *pFormat,
BOOL bEventDriven,
REFERENCE_TIME *phnsMinBufferDuration,
REFERENCE_TIME *phnsMaxBufferDuration);
HRESULT ( STDMETHODCALLTYPE *GetSharedModeEnginePeriod )(
IAudioClient3 * This,
const WAVEFORMATEX *pFormat,
UINT32 *pDefaultPeriodInFrames,
UINT32 *pFundamentalPeriodInFrames,
UINT32 *pMinPeriodInFrames,
UINT32 *pMaxPeriodInFrames);
HRESULT ( STDMETHODCALLTYPE *GetCurrentSharedModeEnginePeriod )(
IAudioClient3 * This,
WAVEFORMATEX **ppFormat,
UINT32 *pCurrentPeriodInFrames);
HRESULT ( STDMETHODCALLTYPE *InitializeSharedAudioStream )(
IAudioClient3 * This,
DWORD StreamFlags,
UINT32 PeriodInFrames,
const WAVEFORMATEX *pFormat,
LPCGUID AudioSessionGuid);
END_INTERFACE
} IAudioClient3Vtbl;
interface IAudioClient3
{
CONST_VTBL struct IAudioClient3Vtbl *lpVtbl;
};
#define IAudioClient3_QueryInterface(This,riid,ppvObject) \
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IAudioClient3_AddRef(This) \
( (This)->lpVtbl -> AddRef(This) )
#define IAudioClient3_Release(This) \
( (This)->lpVtbl -> Release(This) )
#define IAudioClient3_Initialize(This,ShareMode,StreamFlags,hnsBufferDuration,hnsPeriodicity,pFormat,AudioSessionGuid) \
( (This)->lpVtbl -> Initialize(This,ShareMode,StreamFlags,hnsBufferDuration,hnsPeriodicity,pFormat,AudioSessionGuid) )
#define IAudioClient3_GetBufferSize(This,pNumBufferFrames) \
( (This)->lpVtbl -> GetBufferSize(This,pNumBufferFrames) )
#define IAudioClient3_GetStreamLatency(This,phnsLatency) \
( (This)->lpVtbl -> GetStreamLatency(This,phnsLatency) )
#define IAudioClient3_GetCurrentPadding(This,pNumPaddingFrames) \
( (This)->lpVtbl -> GetCurrentPadding(This,pNumPaddingFrames) )
#define IAudioClient3_IsFormatSupported(This,ShareMode,pFormat,ppClosestMatch) \
( (This)->lpVtbl -> IsFormatSupported(This,ShareMode,pFormat,ppClosestMatch) )
#define IAudioClient3_GetMixFormat(This,ppDeviceFormat) \
( (This)->lpVtbl -> GetMixFormat(This,ppDeviceFormat) )
#define IAudioClient3_GetDevicePeriod(This,phnsDefaultDevicePeriod,phnsMinimumDevicePeriod) \
( (This)->lpVtbl -> GetDevicePeriod(This,phnsDefaultDevicePeriod,phnsMinimumDevicePeriod) )
#define IAudioClient3_Start(This) \
( (This)->lpVtbl -> Start(This) )
#define IAudioClient3_Stop(This) \
( (This)->lpVtbl -> Stop(This) )
#define IAudioClient3_Reset(This) \
( (This)->lpVtbl -> Reset(This) )
#define IAudioClient3_SetEventHandle(This,eventHandle) \
( (This)->lpVtbl -> SetEventHandle(This,eventHandle) )
#define IAudioClient3_GetService(This,riid,ppv) \
( (This)->lpVtbl -> GetService(This,riid,ppv) )
#define IAudioClient3_IsOffloadCapable(This,Category,pbOffloadCapable) \
( (This)->lpVtbl -> IsOffloadCapable(This,Category,pbOffloadCapable) )
#define IAudioClient3_SetClientProperties(This,pProperties) \
( (This)->lpVtbl -> SetClientProperties(This,pProperties) )
#define IAudioClient3_GetBufferSizeLimits(This,pFormat,bEventDriven,phnsMinBufferDuration,phnsMaxBufferDuration) \
( (This)->lpVtbl -> GetBufferSizeLimits(This,pFormat,bEventDriven,phnsMinBufferDuration,phnsMaxBufferDuration) )
#define IAudioClient3_GetSharedModeEnginePeriod(This,pFormat,pDefaultPeriodInFrames,pFundamentalPeriodInFrames,pMinPeriodInFrames,pMaxPeriodInFrames) \
( (This)->lpVtbl -> GetSharedModeEnginePeriod(This,pFormat,pDefaultPeriodInFrames,pFundamentalPeriodInFrames,pMinPeriodInFrames,pMaxPeriodInFrames) )
#define IAudioClient3_GetCurrentSharedModeEnginePeriod(This,ppFormat,pCurrentPeriodInFrames) \
( (This)->lpVtbl -> GetCurrentSharedModeEnginePeriod(This,ppFormat,pCurrentPeriodInFrames) )
#define IAudioClient3_InitializeSharedAudioStream(This,StreamFlags,PeriodInFrames,pFormat,AudioSessionGuid) \
( (This)->lpVtbl -> InitializeSharedAudioStream(This,StreamFlags,PeriodInFrames,pFormat,AudioSessionGuid) )
#endif /* __IAudioClient3_INTERFACE_DEFINED__ */

View file

@ -413,78 +413,35 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
GstWasapiSink *self = GST_WASAPI_SINK (asink); GstWasapiSink *self = GST_WASAPI_SINK (asink);
gboolean res = FALSE; gboolean res = FALSE;
REFERENCE_TIME latency_rt; REFERENCE_TIME latency_rt;
REFERENCE_TIME default_period, min_period; guint bpf, rate, devicep_frames;
REFERENCE_TIME device_period, device_buffer_duration;
guint bpf, rate;
HRESULT hr; HRESULT hr;
hr = IAudioClient_GetDevicePeriod (self->client, &default_period, if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
&min_period); gst_wasapi_util_have_audioclient3 ()) {
HR_FAILED_RET (hr, IAudioClient::GetDevicePeriod, FALSE); if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT &devicep_frames))
", min period: %" G_GINT64_FORMAT, default_period, min_period); 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))
goto beach;
}
bpf = GST_AUDIO_INFO_BPF (&spec->info); bpf = GST_AUDIO_INFO_BPF (&spec->info);
rate = GST_AUDIO_INFO_RATE (&spec->info); rate = GST_AUDIO_INFO_RATE (&spec->info);
if (self->low_latency) {
if (self->sharemode == AUDCLNT_SHAREMODE_SHARED) {
device_period = default_period;
device_buffer_duration = 0;
} else {
device_period = min_period;
device_buffer_duration = min_period;
}
} else {
/* Clamp values to integral multiples of an appropriate period */
gst_wasapi_util_get_best_buffer_sizes (spec,
self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE, default_period,
min_period, &device_period, &device_buffer_duration);
}
/* For some reason, we need to call this a second time for exclusive mode */
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
CoInitialize (NULL);
hr = IAudioClient_Initialize (self->client, self->sharemode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_buffer_duration,
/* This must always be 0 in shared mode */
self->sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : device_period,
self->mix_format, NULL);
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED &&
self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
guint32 n_frames;
GST_WARNING_OBJECT (self, "initialize failed due to unaligned period %i",
(int) device_period);
/* Calculate a new aligned period. First get the aligned buffer size. */
hr = IAudioClient_GetBufferSize (self->client, &n_frames);
HR_FAILED_RET (hr, IAudioClient::GetBufferSize, FALSE);
device_period = (GST_SECOND / 100) * n_frames / rate;
GST_WARNING_OBJECT (self, "trying to re-initialize with period %i "
"(%i frames, %i rate)", (int) device_period, n_frames, rate);
hr = IAudioClient_Initialize (self->client, self->sharemode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_period,
device_period, self->mix_format, NULL);
}
HR_FAILED_GOTO (hr, IAudioClient::Initialize, beach);
/* Total size of the allocated buffer that we will write to */ /* Total size of the allocated buffer that we will write to */
hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count); hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count);
HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach); HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
GST_INFO_OBJECT (self, "buffer size is %i frames, bpf is %i bytes, " GST_INFO_OBJECT (self, "buffer size is %i frames, device period is %i "
"rate is %i Hz", self->buffer_frame_count, bpf, rate); "frames, bpf is %i bytes, rate is %i Hz", self->buffer_frame_count,
devicep_frames, bpf, rate);
/* Actual latency-time/buffer-time are different now */ /* Actual latency-time/buffer-time will be different now */
spec->segsize = gst_util_uint64_scale_int_round (rate * bpf, spec->segsize = devicep_frames * bpf;
device_period * 100, GST_SECOND);
/* We need a minimum of 2 segments to ensure glitch-free playback */ /* We need a minimum of 2 segments to ensure glitch-free playback */
spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2); spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2);
@ -569,7 +526,8 @@ gst_wasapi_sink_unprepare (GstAudioSink * asink)
{ {
GstWasapiSink *self = GST_WASAPI_SINK (asink); GstWasapiSink *self = GST_WASAPI_SINK (asink);
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
!gst_wasapi_util_have_audioclient3 ())
CoUninitialize (); CoUninitialize ();
#if defined(_MSC_VER) || defined(GST_FORCE_WIN_AVRT) #if defined(_MSC_VER) || defined(GST_FORCE_WIN_AVRT)

View file

@ -380,77 +380,36 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
{ {
GstWasapiSrc *self = GST_WASAPI_SRC (asrc); GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
gboolean res = FALSE; gboolean res = FALSE;
REFERENCE_TIME latency_rt, default_period, min_period; REFERENCE_TIME latency_rt;
REFERENCE_TIME device_period, device_buffer_duration; guint bpf, rate, devicep_frames, buffer_frames;
guint bpf, rate, buffer_frames;
HRESULT hr; HRESULT hr;
hr = IAudioClient_GetDevicePeriod (self->client, &default_period, if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
&min_period); gst_wasapi_util_have_audioclient3 ()) {
HR_FAILED_RET (hr, IAudioClient::GetDevicePeriod, FALSE); if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
(IAudioClient3 *) self->client, self->mix_format, self->low_latency,
GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT &devicep_frames))
", min period: %" G_GINT64_FORMAT, default_period, min_period); 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))
goto beach;
}
bpf = GST_AUDIO_INFO_BPF (&spec->info); bpf = GST_AUDIO_INFO_BPF (&spec->info);
rate = GST_AUDIO_INFO_RATE (&spec->info); rate = GST_AUDIO_INFO_RATE (&spec->info);
if (self->low_latency) {
if (self->sharemode == AUDCLNT_SHAREMODE_SHARED) {
device_period = default_period;
device_buffer_duration = 0;
} else {
device_period = min_period;
device_buffer_duration = min_period;
}
} else {
/* Clamp values to integral multiples of an appropriate period */
gst_wasapi_util_get_best_buffer_sizes (spec,
self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE, default_period,
min_period, &device_period, &device_buffer_duration);
}
/* For some reason, we need to call this a second time for exclusive mode */
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
CoInitialize (NULL);
hr = IAudioClient_Initialize (self->client, self->sharemode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_buffer_duration,
/* This must always be 0 in shared mode */
self->sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : device_period,
self->mix_format, NULL);
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED &&
self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
guint32 n_frames;
GST_WARNING_OBJECT (self, "initialize failed due to unaligned period %i",
(int) device_period);
/* Calculate a new aligned period. First get the aligned buffer size. */
hr = IAudioClient_GetBufferSize (self->client, &n_frames);
HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
device_period = (GST_SECOND / 100) * n_frames / rate;
GST_WARNING_OBJECT (self, "trying to re-initialize with period %i "
"(%i frames, %i rate)", (int) device_period, n_frames, rate);
hr = IAudioClient_Initialize (self->client, self->sharemode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_period,
device_period, self->mix_format, NULL);
}
HR_FAILED_GOTO (hr, IAudioClient::Initialize, beach);
/* Total size in frames of the allocated buffer that we will read from */ /* Total size in frames of the allocated buffer that we will read from */
hr = IAudioClient_GetBufferSize (self->client, &buffer_frames); hr = IAudioClient_GetBufferSize (self->client, &buffer_frames);
HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach); HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
GST_INFO_OBJECT (self, "buffer size is %i frames, bpf is %i bytes, " GST_INFO_OBJECT (self, "buffer size is %i frames, device period is %i "
"rate is %i Hz", buffer_frames, bpf, rate); "frames, bpf is %i bytes, rate is %i Hz", buffer_frames,
devicep_frames, bpf, rate);
spec->segsize = gst_util_uint64_scale_int_round (rate * bpf, /* Actual latency-time/buffer-time will be different now */
device_period * 100, GST_SECOND); spec->segsize = devicep_frames * bpf;
/* We need a minimum of 2 segments to ensure glitch-free playback */ /* We need a minimum of 2 segments to ensure glitch-free playback */
spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2); spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2);

View file

@ -58,6 +58,10 @@ const IID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,
{0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2} {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2}
}; };
const IID IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4,
{0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42}
};
const IID IID_IAudioClock = { 0xcd63314f, 0x3fba, 0x4a1b, const IID IID_IAudioClock = { 0xcd63314f, 0x3fba, 0x4a1b,
{0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7} {0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7}
}; };
@ -98,6 +102,27 @@ static struct
{SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT} {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
}; };
static int windows_major_version = 0;
gboolean
gst_wasapi_util_have_audioclient3 (void)
{
if (windows_major_version > 0)
return windows_major_version == 10;
if (g_getenv ("GST_WASAPI_DISABLE_AUDIOCLIENT3") != NULL) {
windows_major_version = 6;
return FALSE;
}
/* https://msdn.microsoft.com/en-us/library/windows/desktop/ms724834(v=vs.85).aspx */
windows_major_version = 6;
if (g_win32_check_windows_version (10, 0, 0, G_WIN32_OS_ANY))
windows_major_version = 10;
return windows_major_version == 10;
}
GType GType
gst_wasapi_device_role_get_type (void) gst_wasapi_device_role_get_type (void)
{ {
@ -533,6 +558,10 @@ gst_wasapi_util_get_device_client (GstElement * self,
} }
} }
if (gst_wasapi_util_have_audioclient3 ())
hr = IMMDevice_Activate (device, &IID_IAudioClient3, CLSCTX_ALL, NULL,
(void **) &client);
else
hr = IMMDevice_Activate (device, &IID_IAudioClient, CLSCTX_ALL, NULL, hr = IMMDevice_Activate (device, &IID_IAudioClient, CLSCTX_ALL, NULL,
(void **) &client); (void **) &client);
HR_FAILED_GOTO (hr, IMMDevice::Activate (IID_IAudioClient), beach); HR_FAILED_GOTO (hr, IMMDevice::Activate (IID_IAudioClient), beach);
@ -795,3 +824,117 @@ gst_wasapi_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec,
*ret_period = use_period; *ret_period = use_period;
*ret_buffer_duration = use_buffer; *ret_buffer_duration = use_buffer;
} }
gboolean
gst_wasapi_util_initialize_audioclient (GstElement * self,
GstAudioRingBufferSpec * spec, IAudioClient * client,
WAVEFORMATEX * format, guint sharemode, gboolean low_latency,
guint * ret_devicep_frames)
{
REFERENCE_TIME default_period, min_period;
REFERENCE_TIME device_period, device_buffer_duration;
guint rate;
HRESULT hr;
hr = IAudioClient_GetDevicePeriod (client, &default_period, &min_period);
HR_FAILED_RET (hr, IAudioClient::GetDevicePeriod, FALSE);
GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT
", min period: %" G_GINT64_FORMAT, default_period, min_period);
rate = GST_AUDIO_INFO_RATE (&spec->info);
if (low_latency) {
if (sharemode == AUDCLNT_SHAREMODE_SHARED) {
device_period = default_period;
device_buffer_duration = 0;
} else {
device_period = min_period;
device_buffer_duration = min_period;
}
} else {
/* Clamp values to integral multiples of an appropriate period */
gst_wasapi_util_get_best_buffer_sizes (spec,
sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE, default_period,
min_period, &device_period, &device_buffer_duration);
}
/* For some reason, we need to call this a second time for exclusive mode */
if (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
CoInitialize (NULL);
hr = IAudioClient_Initialize (client, sharemode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_buffer_duration,
/* This must always be 0 in shared mode */
sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : device_period, format, NULL);
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED &&
sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
guint32 n_frames;
GST_WARNING_OBJECT (self, "initialize failed due to unaligned period %i",
(int) device_period);
/* Calculate a new aligned period. First get the aligned buffer size. */
hr = IAudioClient_GetBufferSize (client, &n_frames);
HR_FAILED_RET (hr, IAudioClient::GetBufferSize, FALSE);
device_period = (GST_SECOND / 100) * n_frames / rate;
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_FAILED_RET (hr, IAudioClient::Initialize, FALSE);
*ret_devicep_frames = (rate * device_period * 100) / GST_SECOND;
return TRUE;
}
gboolean
gst_wasapi_util_initialize_audioclient3 (GstElement * self,
GstAudioRingBufferSpec * spec, IAudioClient3 * client,
WAVEFORMATEX * format, gboolean low_latency, guint * ret_devicep_frames)
{
HRESULT hr;
guint rate, devicep_frames;
guint defaultp_frames, fundp_frames, minp_frames, maxp_frames;
WAVEFORMATEX *tmpf;
rate = GST_AUDIO_INFO_RATE (&spec->info);
hr = IAudioClient3_GetSharedModeEnginePeriod (client, format,
&defaultp_frames, &fundp_frames, &minp_frames, &maxp_frames);
HR_FAILED_RET (hr, IAudioClient3::GetSharedModeEnginePeriod, FALSE);
GST_INFO_OBJECT (self, "Using IAudioClient3, default period %i frames, "
"fundamental period %i frames, minimum period %i frames, maximum period "
"%i frames", defaultp_frames, fundp_frames, minp_frames, maxp_frames);
if (low_latency) {
devicep_frames = minp_frames;
} else {
/* rate is in Hz, latency_time is in usec */
int tmp = (rate * spec->latency_time * GST_USECOND) / GST_SECOND;
devicep_frames = CLAMP (tmp, minp_frames, maxp_frames);
/* Ensure it's a multiple of the fundamental period */
tmp = devicep_frames / fundp_frames;
devicep_frames = tmp * fundp_frames;
}
hr = IAudioClient3_InitializeSharedAudioStream (client,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, devicep_frames, format, NULL);
HR_FAILED_RET (hr, IAudioClient3::InitializeSharedAudioStream, FALSE);
hr = IAudioClient3_GetCurrentSharedModeEnginePeriod (client, &tmpf,
&devicep_frames);
CoTaskMemFree (tmpf);
HR_FAILED_RET (hr, IAudioClient3::GetCurrentSharedModeEnginePeriod, FALSE);
*ret_devicep_frames = devicep_frames;
return TRUE;
}

View file

@ -28,6 +28,8 @@
#include <mmdeviceapi.h> #include <mmdeviceapi.h>
#include <audioclient.h> #include <audioclient.h>
#include "gstaudioclient3.h"
/* Static Caps shared between source, sink, and device provider */ /* Static Caps shared between source, sink, and device provider */
#define GST_WASAPI_STATIC_CAPS "audio/x-raw, " \ #define GST_WASAPI_STATIC_CAPS "audio/x-raw, " \
"format = (string) " GST_AUDIO_FORMATS_ALL ", " \ "format = (string) " GST_AUDIO_FORMATS_ALL ", " \
@ -62,6 +64,8 @@ GType gst_wasapi_device_role_get_type (void);
/* Utilities */ /* Utilities */
gboolean gst_wasapi_util_have_audioclient3 (void);
gint gst_wasapi_device_role_to_erole (gint role); gint gst_wasapi_device_role_to_erole (gint role);
gint gst_wasapi_erole_to_device_role (gint erole); gint gst_wasapi_erole_to_device_role (gint erole);
@ -97,4 +101,13 @@ void gst_wasapi_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec,
REFERENCE_TIME min_period, REFERENCE_TIME * ret_period, REFERENCE_TIME min_period, REFERENCE_TIME * ret_period,
REFERENCE_TIME * ret_buffer_duration); REFERENCE_TIME * ret_buffer_duration);
gboolean gst_wasapi_util_initialize_audioclient (GstElement * element,
GstAudioRingBufferSpec * spec, IAudioClient * client,
WAVEFORMATEX * format, guint sharemode, gboolean low_latency,
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);
#endif /* __GST_WASAPI_UTIL_H__ */ #endif /* __GST_WASAPI_UTIL_H__ */