如何使用 WASAPI 共享模式获得低于 10 毫秒的延迟?
How to get below 10ms latency using WASAPI shared mode?
根据 Microsoft 的说法,从 Windows10 开始,使用共享模式 WASAPI 的应用程序可以请求小于 10 毫秒的缓冲区大小(参见 https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187%28v=vs.85%29.aspx)。
根据这篇文章,实现如此低的延迟需要一些驱动程序更新,我也这样做了。使用独占模式渲染和捕获流,我测量了大约 13 毫秒的总往返延迟(使用硬件环回电缆)。这向我表明,至少有一个端点成功实现了 < 10 毫秒的延迟。 (这个假设正确吗?)
文章提到应用程序可以使用新的 IAudioClient3
接口来查询使用 IAudioClient3::GetSharedModeEnginePeriod()
的 Windows 音频引擎支持的最小缓冲区大小。但是,此函数在我的系统上总是 returns 10 毫秒,并且任何使用 IAudioClient::Initialize()
或 IAudioClient3::InitializeSharedAudioStream()
初始化音频流且周期低于 10 毫秒的尝试总是导致 AUDCLNT_E_INVALID_DEVICE_PERIOD
.
为了确定,我还禁用了音频驱动程序中的任何效果处理。
我错过了什么?甚至有可能从共享模式获得低延迟吗?
请参阅下面的示例代码。
#include <windows.h>
#include <atlbase.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <iostream>
#define VERIFY(hr) do { \
auto temp = (hr); \
if(FAILED(temp)) { \
std::cout << "Error: " << #hr << ": " << temp << "\n"; \
goto error; \
} \
} while(0)
int main(int argc, char** argv) {
HRESULT hr;
CComPtr<IMMDevice> device;
AudioClientProperties props;
CComPtr<IAudioClient> client;
CComPtr<IAudioClient2> client2;
CComPtr<IAudioClient3> client3;
CComHeapPtr<WAVEFORMATEX> format;
CComPtr<IMMDeviceEnumerator> enumerator;
REFERENCE_TIME minTime, maxTime, engineTime;
UINT32 min, max, fundamental, default_, current;
VERIFY(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
VERIFY(enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)));
VERIFY(enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));
VERIFY(device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&client)));
VERIFY(client->QueryInterface(&client2));
VERIFY(client->QueryInterface(&client3));
VERIFY(client3->GetCurrentSharedModeEnginePeriod(&format, ¤t));
// Always fails with AUDCLNT_E_OFFLOAD_MODE_ONLY.
hr = client2->GetBufferSizeLimits(format, TRUE, &minTime, &maxTime);
if(hr == AUDCLNT_E_OFFLOAD_MODE_ONLY)
std::cout << "GetBufferSizeLimits returned AUDCLNT_E_OFFLOAD_MODE_ONLY.\n";
else if(SUCCEEDED(hr))
std::cout << "hw min = " << (minTime / 10000.0) << " hw max = " << (maxTime / 10000.0) << "\n";
else
VERIFY(hr);
// Correctly? reports a minimum hardware period of 3ms and audio engine period of 10ms.
VERIFY(client->GetDevicePeriod(&engineTime, &minTime));
std::cout << "hw min = " << (minTime / 10000.0) << " engine = " << (engineTime / 10000.0) << "\n";
// All values are set to a number of frames corresponding to 10ms.
// This does not change if i change the device's sampling rate in the control panel.
VERIFY(client3->GetSharedModeEnginePeriod(format, &default_, &fundamental, &min, &max));
std::cout << "default = " << default_
<< " fundamental = " << fundamental
<< " min = " << min
<< " max = " << max
<< " current = " << current << "\n";
props.bIsOffload = FALSE;
props.cbSize = sizeof(props);
props.eCategory = AudioCategory_ForegroundOnlyMedia;
props.Options = AUDCLNT_STREAMOPTIONS_RAW | AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
// Doesn't seem to have any effect regardless of category/options values.
VERIFY(client2->SetClientProperties(&props));
format.Free();
VERIFY(client3->GetCurrentSharedModeEnginePeriod(&format, ¤t));
VERIFY(client3->GetSharedModeEnginePeriod(format, &default_, &fundamental, &min, &max));
std::cout << "default = " << default_
<< " fundamental = " << fundamental
<< " min = " << min
<< " max = " << max
<< " current = " << current << "\n";
error:
CoUninitialize();
return 0;
}
Per Hans 在上面的评论中,仔细检查您是否遵循了低延迟音频的说明 here。
为了确定,我会重启机器; Windows 对这种事情可能有点挑剔。
根据 Microsoft 的说法,从 Windows10 开始,使用共享模式 WASAPI 的应用程序可以请求小于 10 毫秒的缓冲区大小(参见 https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187%28v=vs.85%29.aspx)。
根据这篇文章,实现如此低的延迟需要一些驱动程序更新,我也这样做了。使用独占模式渲染和捕获流,我测量了大约 13 毫秒的总往返延迟(使用硬件环回电缆)。这向我表明,至少有一个端点成功实现了 < 10 毫秒的延迟。 (这个假设正确吗?)
文章提到应用程序可以使用新的 IAudioClient3
接口来查询使用 IAudioClient3::GetSharedModeEnginePeriod()
的 Windows 音频引擎支持的最小缓冲区大小。但是,此函数在我的系统上总是 returns 10 毫秒,并且任何使用 IAudioClient::Initialize()
或 IAudioClient3::InitializeSharedAudioStream()
初始化音频流且周期低于 10 毫秒的尝试总是导致 AUDCLNT_E_INVALID_DEVICE_PERIOD
.
为了确定,我还禁用了音频驱动程序中的任何效果处理。 我错过了什么?甚至有可能从共享模式获得低延迟吗? 请参阅下面的示例代码。
#include <windows.h>
#include <atlbase.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <iostream>
#define VERIFY(hr) do { \
auto temp = (hr); \
if(FAILED(temp)) { \
std::cout << "Error: " << #hr << ": " << temp << "\n"; \
goto error; \
} \
} while(0)
int main(int argc, char** argv) {
HRESULT hr;
CComPtr<IMMDevice> device;
AudioClientProperties props;
CComPtr<IAudioClient> client;
CComPtr<IAudioClient2> client2;
CComPtr<IAudioClient3> client3;
CComHeapPtr<WAVEFORMATEX> format;
CComPtr<IMMDeviceEnumerator> enumerator;
REFERENCE_TIME minTime, maxTime, engineTime;
UINT32 min, max, fundamental, default_, current;
VERIFY(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
VERIFY(enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)));
VERIFY(enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));
VERIFY(device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&client)));
VERIFY(client->QueryInterface(&client2));
VERIFY(client->QueryInterface(&client3));
VERIFY(client3->GetCurrentSharedModeEnginePeriod(&format, ¤t));
// Always fails with AUDCLNT_E_OFFLOAD_MODE_ONLY.
hr = client2->GetBufferSizeLimits(format, TRUE, &minTime, &maxTime);
if(hr == AUDCLNT_E_OFFLOAD_MODE_ONLY)
std::cout << "GetBufferSizeLimits returned AUDCLNT_E_OFFLOAD_MODE_ONLY.\n";
else if(SUCCEEDED(hr))
std::cout << "hw min = " << (minTime / 10000.0) << " hw max = " << (maxTime / 10000.0) << "\n";
else
VERIFY(hr);
// Correctly? reports a minimum hardware period of 3ms and audio engine period of 10ms.
VERIFY(client->GetDevicePeriod(&engineTime, &minTime));
std::cout << "hw min = " << (minTime / 10000.0) << " engine = " << (engineTime / 10000.0) << "\n";
// All values are set to a number of frames corresponding to 10ms.
// This does not change if i change the device's sampling rate in the control panel.
VERIFY(client3->GetSharedModeEnginePeriod(format, &default_, &fundamental, &min, &max));
std::cout << "default = " << default_
<< " fundamental = " << fundamental
<< " min = " << min
<< " max = " << max
<< " current = " << current << "\n";
props.bIsOffload = FALSE;
props.cbSize = sizeof(props);
props.eCategory = AudioCategory_ForegroundOnlyMedia;
props.Options = AUDCLNT_STREAMOPTIONS_RAW | AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
// Doesn't seem to have any effect regardless of category/options values.
VERIFY(client2->SetClientProperties(&props));
format.Free();
VERIFY(client3->GetCurrentSharedModeEnginePeriod(&format, ¤t));
VERIFY(client3->GetSharedModeEnginePeriod(format, &default_, &fundamental, &min, &max));
std::cout << "default = " << default_
<< " fundamental = " << fundamental
<< " min = " << min
<< " max = " << max
<< " current = " << current << "\n";
error:
CoUninitialize();
return 0;
}
Per Hans 在上面的评论中,仔细检查您是否遵循了低延迟音频的说明 here。
为了确定,我会重启机器; Windows 对这种事情可能有点挑剔。