Skip to content

Commit b1bcea2

Browse files
committed
Switch WASAPI playout to timer-driven mode with AUTOCONVERTPCM
Use WAVEFORMATEXTENSIBLE and AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM to support devices whose native format differs from 48kHz stereo PCM, such as Bluetooth HFP endpoints (16kHz mono) and surround sound devices (5.1/7.1). Replace the event-driven render loop (AUDCLNT_STREAMFLAGS_EVENTCALLBACK) with a timer-driven polling loop to avoid a known issue where AUTOCONVERTPCM combined with EVENTCALLBACK causes the audio engine to stop signaling render events, resulting in premature thread termination. Also adds 192kHz to the supported sample rate list, uses the first closest-match format from IsFormatSupported as a fallback when no exact match is found, and applies the same changes to the recording path.
1 parent 6d26957 commit b1bcea2

File tree

1 file changed

+65
-15
lines changed

1 file changed

+65
-15
lines changed

build/patches/windows_add_192k.patch

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
diff --git a/modules/audio_device/win/audio_device_core_win.cc b/modules/audio_device/win/audio_device_core_win.cc
2-
index f1cc0474fc..f8e4f25bb9 100644
2+
index f1cc0474fc..2aacd1d309 100644
33
--- a/modules/audio_device/win/audio_device_core_win.cc
44
+++ b/modules/audio_device/win/audio_device_core_win.cc
55
@@ -1845,8 +1845,8 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
@@ -142,14 +142,13 @@ index f1cc0474fc..f8e4f25bb9 100644
142142
RTC_LOG(LS_VERBOSE) << "Additional settings:";
143143
RTC_LOG(LS_VERBOSE) << "_playAudioFrameSize: " << _playAudioFrameSize;
144144
RTC_LOG(LS_VERBOSE) << "_playBlockSize : " << _playBlockSize;
145-
@@ -1980,12 +2007,14 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
145+
@@ -1980,12 +2007,12 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
146146
}
147147
hr = _ptrClientOut->Initialize(
148148
AUDCLNT_SHAREMODE_SHARED, // share Audio Engine with other applications
149149
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK, // processing of the audio buffer by
150-
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | // processing of the audio buffer by
151-
// the client will be event driven
152-
+ AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM |
150+
- // the client will be event driven
151+
+ AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | // let WASAPI handle format conversion
153152
+ AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
154153
hnsBufferDuration, // requested buffer capacity as a time value (in
155154
// 100-nanosecond units)
@@ -159,7 +158,19 @@ index f1cc0474fc..f8e4f25bb9 100644
159158
NULL); // session GUID
160159

161160
if (FAILED(hr)) {
162-
@@ -2031,7 +2060,7 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
161+
@@ -2016,11 +2043,6 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
162+
<< bufferFrameCount * _playAudioFrameSize << " bytes)";
163+
}
164+
165+
- // Set the event handle that the system signals when an audio buffer is ready
166+
- // to be processed by the client.
167+
- hr = _ptrClientOut->SetEventHandle(_hRenderSamplesReadyEvent);
168+
- EXIT_ON_ERROR(hr);
169+
-
170+
// Get an IAudioRenderClient interface.
171+
SAFE_RELEASE(_ptrRenderClient);
172+
hr = _ptrClientOut->GetService(__uuidof(IAudioRenderClient),
173+
@@ -2031,7 +2053,7 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
163174
_playIsInitialized = true;
164175

165176
CoTaskMemFree(pWfxOut);
@@ -168,7 +179,7 @@ index f1cc0474fc..f8e4f25bb9 100644
168179

169180
RTC_LOG(LS_VERBOSE) << "render side is now initialized";
170181
return 0;
171-
@@ -2039,7 +2068,7 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
182+
@@ -2039,7 +2061,7 @@ int32_t AudioDeviceWindowsCore::InitPlayout() {
172183
Exit:
173184
_TraceCOMError(hr);
174185
CoTaskMemFree(pWfxOut);
@@ -177,7 +188,7 @@ index f1cc0474fc..f8e4f25bb9 100644
177188
SAFE_RELEASE(_ptrClientOut);
178189
SAFE_RELEASE(_ptrRenderClient);
179190
return -1;
180-
@@ -2163,7 +2192,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
191+
@@ -2163,7 +2185,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
181192
HRESULT hr = S_OK;
182193
WAVEFORMATEX* pWfxIn = NULL;
183194
WAVEFORMATEXTENSIBLE Wfx = WAVEFORMATEXTENSIBLE();
@@ -186,7 +197,7 @@ index f1cc0474fc..f8e4f25bb9 100644
186197

187198
// Create COM object with IAudioClient interface.
188199
SAFE_RELEASE(_ptrClientIn);
189-
@@ -2201,7 +2230,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
200+
@@ -2201,7 +2223,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
190201
Wfx.Samples.wValidBitsPerSample = Wfx.Format.wBitsPerSample;
191202
Wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
192203

@@ -195,7 +206,7 @@ index f1cc0474fc..f8e4f25bb9 100644
195206
hr = S_FALSE;
196207

197208
// Iterate over frequencies and channels, in order of priority
198-
@@ -2218,8 +2247,10 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
209+
@@ -2218,8 +2240,10 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
199210
// If the method succeeds and the audio endpoint device supports the
200211
// specified stream format, it returns S_OK. If the method succeeds and
201212
// provides a closest match to the specified format, it returns S_FALSE.
@@ -208,7 +219,7 @@ index f1cc0474fc..f8e4f25bb9 100644
208219
if (hr == S_OK) {
209220
break;
210221
} else {
211-
@@ -2228,10 +2259,14 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
222+
@@ -2228,10 +2252,14 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
212223
<< ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec
213224
<< " is not supported. Closest match: "
214225
"nChannels="
@@ -227,7 +238,7 @@ index f1cc0474fc..f8e4f25bb9 100644
227238
} else {
228239
RTC_LOG(LS_INFO) << "nChannels=" << Wfx.Format.nChannels
229240
<< ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec
230-
@@ -2242,7 +2277,20 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
241+
@@ -2242,7 +2270,20 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
231242
if (hr == S_OK)
232243
break;
233244
}
@@ -249,7 +260,7 @@ index f1cc0474fc..f8e4f25bb9 100644
249260
if (hr == S_OK) {
250261
_recAudioFrameSize = Wfx.Format.nBlockAlign;
251262
_recSampleRate = Wfx.Format.nSamplesPerSec;
252-
@@ -2270,6 +2318,8 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
263+
@@ -2270,6 +2311,8 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
253264
AUDCLNT_SHAREMODE_SHARED, // share Audio Engine with other applications
254265
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | // processing of the audio buffer by
255266
// the client will be event driven
@@ -258,7 +269,7 @@ index f1cc0474fc..f8e4f25bb9 100644
258269
AUDCLNT_STREAMFLAGS_NOPERSIST, // volume and mute settings for an
259270
// audio session will not persist
260271
// across system restarts
261-
@@ -2321,7 +2371,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
272+
@@ -2321,7 +2364,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
262273
_recIsInitialized = true;
263274

264275
CoTaskMemFree(pWfxIn);
@@ -267,7 +278,7 @@ index f1cc0474fc..f8e4f25bb9 100644
267278

268279
RTC_LOG(LS_VERBOSE) << "capture side is now initialized";
269280
return 0;
270-
@@ -2329,7 +2379,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
281+
@@ -2329,7 +2372,7 @@ int32_t AudioDeviceWindowsCore::InitRecording() {
271282
Exit:
272283
_TraceCOMError(hr);
273284
CoTaskMemFree(pWfxIn);
@@ -276,3 +287,42 @@ index f1cc0474fc..f8e4f25bb9 100644
276287
SAFE_RELEASE(_ptrClientIn);
277288
SAFE_RELEASE(_ptrCaptureClient);
278289
return -1;
290+
@@ -2661,7 +2704,6 @@ DWORD WINAPI AudioDeviceWindowsCore::WSAPICaptureThreadPollDMO(LPVOID context) {
291+
292+
DWORD AudioDeviceWindowsCore::DoRenderThread() {
293+
bool keepPlaying = true;
294+
- HANDLE waitArray[2] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent};
295+
HRESULT hr = S_OK;
296+
HANDLE hMmTask = NULL;
297+
298+
@@ -2777,20 +2819,16 @@ DWORD AudioDeviceWindowsCore::DoRenderThread() {
299+
// >> ------------------ THREAD LOOP ------------------
300+
301+
while (keepPlaying) {
302+
- // Wait for a render notification event or a shutdown event
303+
- DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500);
304+
- switch (waitResult) {
305+
- case WAIT_OBJECT_0 + 0: // _hShutdownRenderEvent
306+
- keepPlaying = false;
307+
- break;
308+
- case WAIT_OBJECT_0 + 1: // _hRenderSamplesReadyEvent
309+
- break;
310+
- case WAIT_TIMEOUT: // timeout notification
311+
- RTC_LOG(LS_WARNING) << "render event timed out after 0.5 seconds";
312+
- goto Exit;
313+
- default: // unexpected error
314+
- RTC_LOG(LS_WARNING) << "unknown wait termination on render side";
315+
- goto Exit;
316+
+ // Sleep for half the device period, then poll for available buffer space.
317+
+ // We use the shutdown event wait as the sleep mechanism so we can still
318+
+ // wake immediately on shutdown.
319+
+ DWORD sleepMs = static_cast<DWORD>(devPeriod / 10000 / 2);
320+
+ if (sleepMs < 1)
321+
+ sleepMs = 1;
322+
+ DWORD waitResult = WaitForSingleObject(_hShutdownRenderEvent, sleepMs);
323+
+ if (waitResult == WAIT_OBJECT_0) {
324+
+ keepPlaying = false;
325+
+ break;
326+
}
327+
328+
while (keepPlaying) {

0 commit comments

Comments
 (0)