summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNirbheek Chauhan <nirbheek@centricular.com>2018-02-14 12:13:36 +0530
committerNirbheek Chauhan <nirbheek@centricular.com>2018-02-26 16:23:11 +0530
commit0cb11c15ed02896c698a3c512ca436ab432055f6 (patch)
tree1a568b1105778fcfb459e839b0049307cf54d01f
parent16af66ee952804c2ddfb6ed719ea48f3092f6d9f (diff)
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.
-rw-r--r--sys/wasapi/gstaudioclient3.h237
-rw-r--r--sys/wasapi/gstwasapisink.c82
-rw-r--r--sys/wasapi/gstwasapisrc.c79
-rw-r--r--sys/wasapi/gstwasapiutil.c147
-rw-r--r--sys/wasapi/gstwasapiutil.h13
5 files changed, 434 insertions, 124 deletions
diff --git a/sys/wasapi/gstaudioclient3.h b/sys/wasapi/gstaudioclient3.h
new file mode 100644
index 000000000..e03033b04
--- /dev/null
+++ b/sys/wasapi/gstaudioclient3.h
@@ -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__ */
diff --git a/sys/wasapi/gstwasapisink.c b/sys/wasapi/gstwasapisink.c
index d047849d8..a1cb31b1f 100644
--- a/sys/wasapi/gstwasapisink.c
+++ b/sys/wasapi/gstwasapisink.c
@@ -413,78 +413,35 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
GstWasapiSink *self = GST_WASAPI_SINK (asink);
gboolean res = FALSE;
REFERENCE_TIME latency_rt;
- REFERENCE_TIME default_period, min_period;
- REFERENCE_TIME device_period, device_buffer_duration;
- guint bpf, rate;
+ guint bpf, rate, devicep_frames;
HRESULT hr;
- hr = IAudioClient_GetDevicePeriod (self->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);
-
- bpf = GST_AUDIO_INFO_BPF (&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;
- }
+ if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
+ gst_wasapi_util_have_audioclient3 ()) {
+ if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
+ (IAudioClient3 *) self->client, self->mix_format, self->low_latency,
+ &devicep_frames))
+ goto beach;
} 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);
+ if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
+ self->client, self->mix_format, self->sharemode, self->low_latency,
+ &devicep_frames))
+ goto beach;
}
- /* 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);
+ bpf = GST_AUDIO_INFO_BPF (&spec->info);
+ rate = GST_AUDIO_INFO_RATE (&spec->info);
/* Total size of the allocated buffer that we will write to */
hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count);
HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
- GST_INFO_OBJECT (self, "buffer size is %i frames, bpf is %i bytes, "
- "rate is %i Hz", self->buffer_frame_count, bpf, rate);
+ GST_INFO_OBJECT (self, "buffer size is %i frames, device period is %i "
+ "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 */
- spec->segsize = gst_util_uint64_scale_int_round (rate * bpf,
- device_period * 100, GST_SECOND);
+ /* Actual latency-time/buffer-time will be different now */
+ spec->segsize = devicep_frames * bpf;
/* We need a minimum of 2 segments to ensure glitch-free playback */
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);
- if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
+ !gst_wasapi_util_have_audioclient3 ())
CoUninitialize ();
#if defined(_MSC_VER) || defined(GST_FORCE_WIN_AVRT)
diff --git a/sys/wasapi/gstwasapisrc.c b/sys/wasapi/gstwasapisrc.c
index 5d0821323..aa555c900 100644
--- a/sys/wasapi/gstwasapisrc.c
+++ b/sys/wasapi/gstwasapisrc.c
@@ -380,77 +380,36 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
{
GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
gboolean res = FALSE;
- REFERENCE_TIME latency_rt, default_period, min_period;
- REFERENCE_TIME device_period, device_buffer_duration;
- guint bpf, rate, buffer_frames;
+ REFERENCE_TIME latency_rt;
+ guint bpf, rate, devicep_frames, buffer_frames;
HRESULT hr;
- hr = IAudioClient_GetDevicePeriod (self->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);
-
- bpf = GST_AUDIO_INFO_BPF (&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;
- }
+ if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
+ gst_wasapi_util_have_audioclient3 ()) {
+ if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
+ (IAudioClient3 *) self->client, self->mix_format, self->low_latency,
+ &devicep_frames))
+ goto beach;
} 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);
+ if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
+ self->client, self->mix_format, self->sharemode, self->low_latency,
+ &devicep_frames))
+ goto beach;
}
- /* 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);
+ bpf = GST_AUDIO_INFO_BPF (&spec->info);
+ rate = GST_AUDIO_INFO_RATE (&spec->info);
/* Total size in frames of the allocated buffer that we will read from */
hr = IAudioClient_GetBufferSize (self->client, &buffer_frames);
HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
- GST_INFO_OBJECT (self, "buffer size is %i frames, bpf is %i bytes, "
- "rate is %i Hz", buffer_frames, bpf, rate);
+ GST_INFO_OBJECT (self, "buffer size is %i frames, device period is %i "
+ "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,
- device_period * 100, GST_SECOND);
+ /* Actual latency-time/buffer-time will be different now */
+ spec->segsize = devicep_frames * bpf;
/* We need a minimum of 2 segments to ensure glitch-free playback */
spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2);
diff --git a/sys/wasapi/gstwasapiutil.c b/sys/wasapi/gstwasapiutil.c
index fa0f19f3b..7002d0544 100644
--- a/sys/wasapi/gstwasapiutil.c
+++ b/sys/wasapi/gstwasapiutil.c
@@ -58,6 +58,10 @@ const IID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,
{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,
{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}
};
+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
gst_wasapi_device_role_get_type (void)
{
@@ -533,8 +558,12 @@ gst_wasapi_util_get_device_client (GstElement * self,
}
}
- hr = IMMDevice_Activate (device, &IID_IAudioClient, CLSCTX_ALL, NULL,
- (void **) &client);
+ 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,
+ (void **) &client);
HR_FAILED_GOTO (hr, IMMDevice::Activate (IID_IAudioClient), beach);
IUnknown_AddRef (client);
@@ -795,3 +824,117 @@ gst_wasapi_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec,
*ret_period = use_period;
*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;
+}
diff --git a/sys/wasapi/gstwasapiutil.h b/sys/wasapi/gstwasapiutil.h
index 0d495467d..67b39b003 100644
--- a/sys/wasapi/gstwasapiutil.h
+++ b/sys/wasapi/gstwasapiutil.h
@@ -28,6 +28,8 @@
#include <mmdeviceapi.h>
#include <audioclient.h>
+#include "gstaudioclient3.h"
+
/* Static Caps shared between source, sink, and device provider */
#define GST_WASAPI_STATIC_CAPS "audio/x-raw, " \
"format = (string) " GST_AUDIO_FORMATS_ALL ", " \
@@ -62,6 +64,8 @@ GType gst_wasapi_device_role_get_type (void);
/* Utilities */
+gboolean gst_wasapi_util_have_audioclient3 (void);
+
gint gst_wasapi_device_role_to_erole (gint role);
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 * 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__ */