1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 pub mod async_stream;
6 mod completion_handler;
7 mod wave_format;
8
9 use std::convert::From;
10 use std::fmt::Debug;
11 use std::os::raw::c_void;
12 use std::ptr::null_mut;
13 use std::sync::Arc;
14 use std::sync::Once;
15 use std::thread_local;
16 use std::time::Duration;
17
18 use audio_streams::async_api::EventAsyncWrapper;
19 use audio_streams::AsyncPlaybackBufferStream;
20 use audio_streams::BoxError;
21 use audio_streams::BufferCommit;
22 use audio_streams::NoopStream;
23 use audio_streams::NoopStreamControl;
24 use audio_streams::PlaybackBuffer;
25 use audio_streams::PlaybackBufferError;
26 use audio_streams::PlaybackBufferStream;
27 use audio_streams::SampleFormat;
28 use audio_streams::StreamControl;
29 use audio_streams::StreamSource;
30 use base::error;
31 use base::info;
32 use base::warn;
33 use base::AsRawDescriptor;
34 use base::Error;
35 use base::Event;
36 use base::EventExt;
37 use base::EventWaitResult;
38 use completion_handler::WinAudioActivateAudioInterfaceCompletionHandler;
39 use sync::Mutex;
40 use thiserror::Error as ThisError;
41 use wave_format::*;
42 use winapi::shared::guiddef::GUID;
43 use winapi::shared::guiddef::REFCLSID;
44 use winapi::um::audioclient::*;
45 use winapi::um::audiosessiontypes::AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED;
46 use winapi::um::audiosessiontypes::AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED;
47 use winapi::um::audiosessiontypes::AUDCLNT_SHAREMODE_SHARED;
48 use winapi::um::audiosessiontypes::AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
49 use winapi::um::combaseapi::*;
50 use winapi::um::coml2api::STGM_READ;
51 use winapi::um::functiondiscoverykeys_devpkey::PKEY_Device_FriendlyName;
52 use winapi::um::mmdeviceapi::*;
53 use winapi::um::objbase::COINIT_APARTMENTTHREADED;
54 use winapi::um::propidl::PropVariantClear;
55 use winapi::um::propidl::PROPVARIANT;
56 use winapi::um::propsys::IPropertyStore;
57 use winapi::um::synchapi::WaitForSingleObject;
58 use winapi::um::unknwnbase::IUnknown;
59 use winapi::um::winbase::WAIT_OBJECT_0;
60 use winapi::Interface;
61 use wio::com::ComPtr;
62
63 use crate::AudioSharedFormat;
64
65 const READY_TO_READ_TIMEOUT_MS: u32 = 2000;
66 pub const STEREO_CHANNEL_COUNT: u16 = 2;
67 pub const MONO_CHANNEL_COUNT: u16 = 1;
68
69 // from msdn: https://docs.microsoft.com/en-us/windows/win32/coreaudio/audclnt-streamflags-xxx-constants
70 // these don't currently exist in winapi
71 const AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM: u32 = 0x80000000;
72 const AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY: u32 = 0x08000000;
73
74 thread_local!(static THREAD_ONCE_INIT: Once = Once::new());
75
76 // Used to differentiate between S_FALSE and S_OK. This means `CoInitializeEx` did not get called. Mainly used for testing.
77 const S_SKIPPED_COINIT: i32 = 2;
78
79 const ACTIVATE_AUDIO_EVENT_TIMEOUT: Duration = Duration::from_secs(5);
80
81 pub struct WinAudio {
82 pub cached_playback_buffer_stream:
83 Option<(Arc<Mutex<Box<dyn PlaybackBufferStream>>>, AudioSharedFormat)>,
84 }
85 impl WinAudio {
new() -> Result<Self, BoxError>86 pub fn new() -> Result<Self, BoxError> {
87 Ok(WinAudio {
88 cached_playback_buffer_stream: None,
89 })
90 }
91
co_init_once_per_thread() -> i3292 pub(crate) fn co_init_once_per_thread() -> i32 {
93 let mut hr = S_SKIPPED_COINIT;
94 THREAD_ONCE_INIT.with(|once| {
95 once.call_once(|| {
96 // Safe because all variables passed into `CoInitializeEx` are hardcoded
97 unsafe {
98 // Initializes the COM library for use by the calling thread. Needed so that `CoCreateInstance`
99 // can be called to create a device enumerator object.
100 //
101 // TODO(b/217413370): `CoUninitialize` is never called at any point in KiwiVm.
102 // It might make sense for all VCPU threads to call `CoInitializeEx` when
103 // starting and `CoUninitialize` when the thread ends. However when switching to
104 // virtio-snd, we need to make sure cros_async threads get `Co(Un)Initialize`
105 // support if needed.
106 hr = CoInitializeEx(null_mut(), COINIT_APARTMENTTHREADED);
107 };
108 })
109 });
110
111 hr
112 }
113 }
114
115 impl StreamSource for WinAudio {
116 /// Returns a stream control and a buffer generator object. The stream control object is not
117 /// used. The buffer generator object is a wrapper around WASAPI's objects that will create a
118 /// buffer for crosvm to copy audio bytes into.
new_playback_stream( &mut self, num_channels: usize, _format: SampleFormat, frame_rate: u32, buffer_size: usize, ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>119 fn new_playback_stream(
120 &mut self,
121 num_channels: usize,
122 _format: SampleFormat,
123 frame_rate: u32,
124 buffer_size: usize, //number of frames;
125 ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {
126 let hr = WinAudio::co_init_once_per_thread();
127 let _ = check_hresult!(hr, RenderError::from(hr), "Co Initialized failed");
128
129 let playback_buffer_stream: Box<dyn PlaybackBufferStream> =
130 match WinAudioRenderer::new(num_channels, frame_rate, buffer_size) {
131 Ok(renderer) => Box::new(renderer),
132 Err(e) => {
133 warn!(
134 "Failed to create WinAudioRenderer. Fallback to NoopStream with error: {}",
135 e
136 );
137 Box::new(NoopStream::new(
138 num_channels,
139 SampleFormat::S16LE,
140 frame_rate,
141 buffer_size,
142 ))
143 }
144 };
145
146 Ok((Box::new(NoopStreamControl::new()), playback_buffer_stream))
147 }
148
149 /// Similar to `new_playback_stream, but will return an `AsyncPlaybackBufferStream` that can
150 /// run async operations.
new_async_playback_stream( &mut self, num_channels: usize, format: SampleFormat, frame_rate: u32, buffer_size: usize, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError>151 fn new_async_playback_stream(
152 &mut self,
153 num_channels: usize,
154 format: SampleFormat,
155 frame_rate: u32,
156 buffer_size: usize,
157 ex: &dyn audio_streams::AudioStreamsExecutor,
158 ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> {
159 WinAudio::new_async_playback_stream_helper(
160 num_channels,
161 format,
162 frame_rate,
163 buffer_size,
164 ex,
165 )
166 }
167 }
168
169 /// Proxy for a `DeviceRenderer` that handles device invalidated errors by switching to a new
170 /// `DeviceRenderer` on a new device.
171 pub(crate) struct WinAudioRenderer {
172 pub device: DeviceRenderer,
173 num_channels: usize,
174 frame_rate: u32,
175 incoming_buffer_size_in_frames: usize,
176 }
177
178 impl WinAudioRenderer {
179 const MAX_REATTACH_TRIES: usize = 50;
180
181 // Initializes WASAPI objects needed for audio.
new( num_channels: usize, frame_rate: u32, incoming_buffer_size_in_frames: usize, ) -> Result<Self, RenderError>182 pub fn new(
183 num_channels: usize,
184 frame_rate: u32,
185 incoming_buffer_size_in_frames: usize,
186 ) -> Result<Self, RenderError> {
187 let device = Self::create_device_renderer_and_log_time(
188 num_channels,
189 frame_rate,
190 incoming_buffer_size_in_frames,
191 None,
192 )?;
193
194 Ok(Self {
195 device,
196 num_channels,
197 frame_rate, // guest frame rate
198 incoming_buffer_size_in_frames, // from the guest`
199 })
200 }
201
create_device_renderer_and_log_time( num_channels: usize, frame_rate: u32, incoming_buffer_size_in_frames: usize, ex: Option<&dyn audio_streams::AudioStreamsExecutor>, ) -> Result<DeviceRenderer, RenderError>202 fn create_device_renderer_and_log_time(
203 num_channels: usize,
204 frame_rate: u32,
205 incoming_buffer_size_in_frames: usize,
206 ex: Option<&dyn audio_streams::AudioStreamsExecutor>,
207 ) -> Result<DeviceRenderer, RenderError> {
208 let start = std::time::Instant::now();
209 let device =
210 DeviceRenderer::new(num_channels, frame_rate, incoming_buffer_size_in_frames, ex)?;
211 // This can give us insights to how other long other machines take to initialize audio.
212 // Eventually this should be a histogram metric.
213 info!(
214 "DeviceRenderer took {}ms to initialize audio.",
215 start.elapsed().as_millis()
216 );
217 Ok(device)
218 }
219
220 // Drops the existing DeviceRenderer and initializes a new DeviceRenderer for the default
221 // device.
reattach_device(&mut self) -> Result<(), RenderError>222 fn reattach_device(&mut self) -> Result<(), RenderError> {
223 self.device = DeviceRenderer::new(
224 self.num_channels,
225 self.frame_rate,
226 self.incoming_buffer_size_in_frames,
227 None,
228 )?;
229 Ok(())
230 }
231 }
232
233 impl PlaybackBufferStream for WinAudioRenderer {
234 /// Returns a wrapper around the WASAPI buffer.
next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError>235 fn next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError> {
236 for _ in 0..Self::MAX_REATTACH_TRIES {
237 match self.device.next_win_buffer() {
238 Ok(_) => return self.device.playback_buffer().map_err(|e| Box::new(e) as _),
239 // If the audio device was disconnected, set up whatever is now the default device
240 // and loop to try again.
241 Err(RenderError::DeviceInvalidated) => {
242 warn!("Audio device disconnected, switching to new default device");
243 self.reattach_device()?;
244 }
245 Err(e) => return Err(Box::new(e)),
246 }
247 }
248 error!("Unable to attach to a working audio device, giving up");
249 Err(Box::new(RenderError::DeviceInvalidated))
250 }
251 }
252
253 // Implementation of buffer generator object. Used to get a buffer from WASAPI for crosvm to copy audio
254 // bytes from the guest memory into.
255 pub(crate) struct DeviceRenderer {
256 audio_render_client: ComPtr<IAudioRenderClient>,
257 audio_client: ComPtr<IAudioClient>,
258 win_buffer: *mut u8,
259 pub audio_shared_format: AudioSharedFormat,
260 audio_render_client_buffer_frame_count: u32,
261 ready_to_read_event: Event,
262 async_ready_to_read_event: Option<Box<dyn EventAsyncWrapper>>,
263 }
264
265 impl DeviceRenderer {
266 // Initializes WASAPI objects needed for audio
new( num_channels: usize, _guest_frame_rate: u32, incoming_buffer_size_in_frames: usize, ex: Option<&dyn audio_streams::AudioStreamsExecutor>, ) -> Result<Self, RenderError>267 fn new(
268 num_channels: usize,
269 _guest_frame_rate: u32,
270 incoming_buffer_size_in_frames: usize,
271 ex: Option<&dyn audio_streams::AudioStreamsExecutor>,
272 ) -> Result<Self, RenderError> {
273 if num_channels > 2 {
274 return Err(RenderError::InvalidChannelCount(num_channels));
275 }
276
277 let audio_client = create_audio_client(eRender)?;
278
279 let format = get_valid_mix_format(&audio_client)?;
280
281 // Safe because `audio_client` is initialized
282 let hr = unsafe {
283 // Intializes the audio client by setting the buffer size in 100-nanoseconds and
284 // specifying the format the audio bytes will be passed in as.
285 // Setting `hnsBufferDuration` (in miilisecond units) to 0 will let the audio engine to
286 // pick the size that will minimize latency.
287 // `hnsPeriodicity` sets the device period and should always be 0 for shared mode.
288 audio_client.Initialize(
289 AUDCLNT_SHAREMODE_SHARED,
290 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
291 | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
292 | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
293 | AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
294 | AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED,
295 0, /* hnsBufferDuration */
296 0, /* hnsPeriodicity */
297 format.as_ptr(),
298 null_mut(),
299 )
300 };
301 check_hresult!(
302 hr,
303 RenderError::from(hr),
304 "Audio Client Initialize() failed."
305 )?;
306
307 let (ready_to_read_event, async_ready_to_read_event) =
308 create_and_set_audio_client_event(&audio_client, &ex)?;
309
310 let audio_render_client = DeviceRenderer::create_audio_render_client(&audio_client)?;
311
312 let shared_audio_engine_period_in_frames =
313 get_device_period_in_frames(&audio_client, &format);
314
315 let audio_render_client_buffer_frame_count =
316 check_endpoint_buffer_size(&audio_client, shared_audio_engine_period_in_frames)?;
317 if incoming_buffer_size_in_frames % shared_audio_engine_period_in_frames != 0 {
318 warn!(
319 "Guest period size: `{}` not divisible by shared audio engine period size: `{}`. \
320 Audio glitches may occur if sample rate conversion isn't on.",
321 incoming_buffer_size_in_frames, shared_audio_engine_period_in_frames
322 );
323 }
324
325 // Safe because `audio_client` is initialized
326 let hr = unsafe {
327 // Starts the audio stream for playback
328 audio_client.Start()
329 };
330 check_hresult!(
331 hr,
332 RenderError::from(hr),
333 "Audio Render Client Start() failed."
334 )?;
335
336 Ok(Self {
337 audio_render_client,
338 audio_client,
339 win_buffer: std::ptr::null_mut(),
340 audio_shared_format: format
341 .create_audio_shared_format(shared_audio_engine_period_in_frames),
342 audio_render_client_buffer_frame_count,
343 ready_to_read_event,
344 async_ready_to_read_event,
345 })
346 }
347
create_audio_render_client( audio_client: &IAudioClient, ) -> Result<ComPtr<IAudioRenderClient>, RenderError>348 fn create_audio_render_client(
349 audio_client: &IAudioClient,
350 ) -> Result<ComPtr<IAudioRenderClient>, RenderError> {
351 let mut audio_render_client: *mut c_void = null_mut();
352
353 // Safe because `audio_client` is initialized
354 let hr = unsafe {
355 audio_client.GetService(
356 &IID_IAudioRenderClient as *const GUID,
357 &mut audio_render_client,
358 )
359 };
360 check_hresult!(
361 hr,
362 RenderError::from(hr),
363 "Audio Client GetService() failed."
364 )?;
365
366 // Safe because `audio_render_client` is guaranteed to be initialized
367 unsafe {
368 Ok(ComPtr::from_raw(
369 audio_render_client as *mut IAudioRenderClient,
370 ))
371 }
372 }
373
374 // Returns a wraper around the WASAPI buffer
next_win_buffer(&mut self) -> Result<(), RenderError>375 fn next_win_buffer(&mut self) -> Result<(), RenderError> {
376 // We will wait for windows to tell us when it is ready to take in the next set of
377 // audio samples from the guest
378 loop {
379 // Safe because `ready_to_read_event` is guarenteed to be properly initialized
380 // and `num_frames_padding` is property initliazed as an empty pointer.
381 unsafe {
382 let res = WaitForSingleObject(
383 self.ready_to_read_event.as_raw_descriptor(),
384 READY_TO_READ_TIMEOUT_MS,
385 );
386 if res != WAIT_OBJECT_0 {
387 warn!(
388 "Waiting for ready_to_read_event timed out after {} ms",
389 READY_TO_READ_TIMEOUT_MS
390 );
391 break;
392 }
393 if self.enough_available_frames()? {
394 break;
395 }
396 }
397 }
398
399 self.get_buffer()?;
400
401 Ok(())
402 }
403
404 /// Returns true if the number of frames avaialble in the Windows playback buffer is at least
405 /// the size of one full period worth of audio samples.
enough_available_frames(&mut self) -> Result<bool, RenderError>406 fn enough_available_frames(&mut self) -> Result<bool, RenderError> {
407 // Safe because `num_frames_padding` is an u32 and `GetCurrentPadding` is a simple
408 // Windows function that shouldn't fail.
409 let mut num_frames_padding = 0u32;
410 let hr = unsafe { self.audio_client.GetCurrentPadding(&mut num_frames_padding) };
411 check_hresult!(
412 hr,
413 RenderError::from(hr),
414 "Audio Client GetCurrentPadding() failed."
415 )?;
416
417 // If the available free frames is less than the frames that are being sent over from the
418 // guest, then we want to only grab the number of frames available.
419 let num_frames_available =
420 (self.audio_render_client_buffer_frame_count - num_frames_padding) as usize;
421
422 Ok(num_frames_available
423 >= self
424 .audio_shared_format
425 .shared_audio_engine_period_in_frames)
426 }
427
get_buffer(&mut self) -> Result<(), RenderError>428 fn get_buffer(&mut self) -> Result<(), RenderError> {
429 self.win_buffer = std::ptr::null_mut();
430
431 // This unsafe block will get the playback buffer and return the size of the buffer
432 //
433 // This is safe because the contents of `win_buffer` will be
434 // released when `ReleaseBuffer` is called in the `BufferCommit` implementation.
435 unsafe {
436 let hr = self.audio_render_client.GetBuffer(
437 self.audio_shared_format
438 .shared_audio_engine_period_in_frames as u32,
439 &mut self.win_buffer,
440 );
441 check_hresult!(
442 hr,
443 RenderError::from(hr),
444 "Audio Render Client GetBuffer failed."
445 )?;
446 }
447
448 Ok(())
449 }
450
playback_buffer(&mut self) -> Result<PlaybackBuffer, RenderError>451 fn playback_buffer(&mut self) -> Result<PlaybackBuffer, RenderError> {
452 // Safe because `win_buffer` is allocated and retrieved from WASAPI. The size requested,
453 // which we specified in `next_win_buffer` is exactly
454 // `shared_audio_engine_period_in_frames`, so the size parameter should be valid.
455 let (frame_size_bytes, buffer_slice) = unsafe {
456 Self::get_frame_size_and_buffer_slice(
457 self.audio_shared_format.bit_depth as usize,
458 self.audio_shared_format.channels as usize,
459 self.win_buffer,
460 self.audio_shared_format
461 .shared_audio_engine_period_in_frames,
462 )?
463 };
464
465 PlaybackBuffer::new(frame_size_bytes, buffer_slice, self)
466 .map_err(RenderError::PlaybackBuffer)
467 }
468
469 /// # Safety
470 ///
471 /// Safe only if:
472 /// 1. `win_buffer` is pointing to a valid buffer used for holding audio bytes.
473 /// 2. `bit_depth`, `channels`, and `shared_audio_engine_period_in_frames` are accurate with
474 /// respect to `win_buffer`, so that a valid slice can be made.
475 /// 3. The variables mentioned in reason "2." must calculate a size no greater than the size
476 /// of the buffer pointed to by `win_buffer`.
get_frame_size_and_buffer_slice<'a>( bit_depth: usize, channels: usize, win_buffer: *mut u8, shared_audio_engine_period_in_frames: usize, ) -> Result<(usize, &'a mut [u8]), RenderError>477 unsafe fn get_frame_size_and_buffer_slice<'a>(
478 bit_depth: usize,
479 channels: usize,
480 win_buffer: *mut u8,
481 shared_audio_engine_period_in_frames: usize,
482 ) -> Result<(usize, &'a mut [u8]), RenderError> {
483 if win_buffer.is_null() {
484 return Err(RenderError::InvalidBuffer);
485 }
486
487 let frame_size_bytes = bit_depth * channels / 8;
488
489 Ok((
490 frame_size_bytes,
491 std::slice::from_raw_parts_mut(
492 win_buffer,
493 shared_audio_engine_period_in_frames * frame_size_bytes,
494 ),
495 ))
496 }
497 }
498
499 impl BufferCommit for DeviceRenderer {
500 // Called after buffer from WASAPI is filled. This will allow the audio bytes to be played as sound.
commit(&mut self, nframes: usize)501 fn commit(&mut self, nframes: usize) {
502 // Safe because `audio_render_client` is initialized and parameters passed
503 // into `ReleaseBuffer()` are valid
504 unsafe {
505 let hr = self.audio_render_client.ReleaseBuffer(nframes as u32, 0);
506 let _ = check_hresult!(
507 hr,
508 RenderError::from(hr),
509 "Audio Render Client ReleaseBuffer() failed"
510 );
511 }
512 }
513 }
514
515 impl Drop for DeviceRenderer {
drop(&mut self)516 fn drop(&mut self) {
517 unsafe {
518 let hr = self.audio_client.Stop();
519 let _ = check_hresult!(
520 hr,
521 RenderError::from(hr),
522 "Audio Render Client Stop() failed."
523 );
524 // audio_client and audio_render_client will be released by ComPtr when dropped. Most
525 // likely safe to Release() if audio_client fails to stop. The MSDN doc does not mention
526 // that it will crash and this should be done anyways to prevent memory leaks
527 }
528 }
529 }
530
531 unsafe impl Send for DeviceRenderer {}
532
533 // Create the `IAudioClient` which is used to create `IAudioRenderClient`, which is used for
534 // audio playback, or used to create `IAudioCaptureClient`, which is used for audio capture.
create_audio_client(dataflow: EDataFlow) -> Result<ComPtr<IAudioClient>, RenderError>535 fn create_audio_client(dataflow: EDataFlow) -> Result<ComPtr<IAudioClient>, RenderError> {
536 let mut device_enumerator: *mut c_void = null_mut();
537
538 // Creates a device enumerator in order to select our default audio device.
539 //
540 // Safe because only `device_enumerator` is being modified and we own it.
541 let hr = unsafe {
542 CoCreateInstance(
543 &CLSID_MMDeviceEnumerator as REFCLSID,
544 null_mut(),
545 CLSCTX_ALL,
546 &IMMDeviceEnumerator::uuidof(),
547 &mut device_enumerator,
548 )
549 };
550 check_hresult!(
551 hr,
552 RenderError::from(hr),
553 "Win audio create client CoCreateInstance() failed."
554 )?;
555
556 // Safe because `device_enumerator` is guaranteed to be initialized
557 let device_enumerator =
558 unsafe { ComPtr::from_raw(device_enumerator as *mut IMMDeviceEnumerator) };
559
560 let mut device: *mut IMMDevice = null_mut();
561 // Safe because `device_enumerator` is guaranteed to be initialized otherwise this method would've
562 // exited
563 let hr = unsafe { device_enumerator.GetDefaultAudioEndpoint(dataflow, eConsole, &mut device) };
564 check_hresult!(
565 hr,
566 RenderError::from(hr),
567 "Device Enumerator GetDefaultAudioEndpoint() failed."
568 )?;
569
570 // Safe because `device` is guaranteed to be initialized
571 let device = unsafe { ComPtr::from_raw(device) };
572 print_device_info(&device)?;
573
574 let is_render = if dataflow == eRender { true } else { false };
575
576 // Call Windows API functions to get the `async_op` which will be used to retrieve the
577 // AudioClient. More details above function definition.
578 let async_op = enable_auto_stream_routing_and_wait(is_render)?;
579
580 let mut factory: *mut IUnknown = null_mut();
581
582 // Safe because `async_op` should be initialized at this point.
583 let activate_result_hr = unsafe {
584 let mut activate_result_hr = 0;
585 let hr = (*async_op).GetActivateResult(&mut activate_result_hr, &mut factory);
586
587 check_hresult!(
588 hr,
589 RenderError::from(hr),
590 "GetActivateResult failed. Cannot retrieve factory to create the Audio Client."
591 )?;
592
593 activate_result_hr
594 };
595 check_hresult!(
596 activate_result_hr,
597 RenderError::from(activate_result_hr),
598 "activateResult is an error. Cannot retrieve factory to create the Audio Client."
599 )?;
600
601 // Safe because `factory` is guaranteed to be initialized.
602 let factory = unsafe { ComPtr::from_raw(factory) };
603
604 factory.cast().map_err(RenderError::from)
605 }
606
607 // Enables automatic audio device routing (only will work for Windows 10, version 1607+).
608 // This will return IActivateAudioInterfaceAsyncOperation that can be used to retrive the
609 // AudioClient.
610 //
611 // This function will pretty much works as follows:
612 // 1. Create the parameters to pass into `ActivateAudioInterfaceAsync`
613 // 2. Call `ActivateAudioInterfaceAsync` which will run asynchrnously and will call
614 // a callback when completed.
615 // 3. Wait on an event that will be notified when that callback is triggered.
616 // 4. Return an IActivateAudioInterfaceAsyncOperation which can be used to retrived the
617 // AudioClient.
enable_auto_stream_routing_and_wait( is_render: bool, ) -> Result<ComPtr<IActivateAudioInterfaceAsyncOperation>, RenderError>618 fn enable_auto_stream_routing_and_wait(
619 is_render: bool,
620 ) -> Result<ComPtr<IActivateAudioInterfaceAsyncOperation>, RenderError> {
621 // Event that fires when callback is called.
622 let activate_audio_interface_complete_event =
623 Event::new_auto_reset().map_err(RenderError::CreateActivateAudioEventError)?;
624
625 let cloned_activate_event = activate_audio_interface_complete_event
626 .try_clone()
627 .map_err(RenderError::CloneEvent)?;
628
629 // Create the callback that is called when `ActivateAudioInterfaceAsync` is finished.
630 // The field `parent` is irrelevant and is only there to fill in the struct so that
631 // this code will run. `ActivateCompleted` is the callback.
632 let completion_handler =
633 WinAudioActivateAudioInterfaceCompletionHandler::create_com_ptr(cloned_activate_event);
634
635 // Retrieve GUID that represents the default audio device.
636 let mut audio_direction_guid_string: *mut u16 = std::ptr::null_mut();
637
638 // This will get the GUID that represents the device we want `ActivateAudioInterfaceAsync`
639 // to activate. `DEVINTERFACE_AUDIO_RENDER` represents the users default audio render device, so
640 // as a result Windows will always route sound to the default device. Likewise,
641 // `DEVINTERFACE_AUDIO_CAPTURE` represents the default audio capture device.
642 //
643 // Safe because we own `audio_direction_guid_string`.
644 let hr = unsafe {
645 if is_render {
646 StringFromIID(
647 &DEVINTERFACE_AUDIO_RENDER as *const winapi::shared::guiddef::GUID,
648 &mut audio_direction_guid_string,
649 )
650 } else {
651 StringFromIID(
652 &DEVINTERFACE_AUDIO_CAPTURE as *const winapi::shared::guiddef::GUID,
653 &mut audio_direction_guid_string,
654 )
655 }
656 };
657 check_hresult!(
658 hr,
659 RenderError::from(hr),
660 format!(
661 "Failed to retrive DEVINTERFACE_AUDIO GUID for {}",
662 if is_render { "rendering" } else { "capturing" }
663 )
664 )?;
665
666 let mut async_op: *mut IActivateAudioInterfaceAsyncOperation = std::ptr::null_mut();
667
668 // This will asynchronously run and when completed, it will trigger the
669 // `IActivateINterfaceCompletetionHandler` callback.
670 // The callback is where the AudioClient can be retrived. This would be easier in C/C++,
671 // but since in rust the callback is an extern function, it would be difficult to get the
672 // `IAudioClient` from the callback to the scope here, so we use an
673 // event to wait for the callback.
674 //
675 // Safe because we own async_op and the completion handler.
676 let hr = unsafe {
677 ActivateAudioInterfaceAsync(
678 audio_direction_guid_string,
679 &IAudioClient::uuidof(),
680 /* activateParams= */ std::ptr::null_mut(),
681 completion_handler.as_raw(),
682 &mut async_op,
683 )
684 };
685
686 // We want to free memory before error checking for `ActivateAudioInterfaceAsync` to prevent
687 // a memory leak.
688 //
689 // Safe because `audio_direction_guid_string` should have valid memory
690 // and we are freeing up memory here.
691 unsafe {
692 CoTaskMemFree(audio_direction_guid_string as *mut std::ffi::c_void);
693 }
694
695 check_hresult!(
696 hr,
697 RenderError::from(hr),
698 "`Activate AudioInterfaceAsync failed."
699 )?;
700
701 // Wait for `ActivateAudioInterfaceAsync` to finish. `ActivateAudioInterfaceAsync` should
702 // never hang, but added a long timeout just incase.
703 match activate_audio_interface_complete_event.wait_timeout(ACTIVATE_AUDIO_EVENT_TIMEOUT) {
704 Ok(event_result) => match event_result {
705 EventWaitResult::Signaled => {}
706 EventWaitResult::TimedOut => {
707 return Err(RenderError::ActivateAudioEventTimeoutError);
708 }
709 },
710 Err(e) => {
711 return Err(RenderError::ActivateAudioEventError(e));
712 }
713 }
714
715 // Safe because we own `async_op` and it shouldn't be null if the activate audio event
716 // fired.
717 unsafe { Ok(ComPtr::from_raw(async_op)) }
718 }
719
720 // Prints the friendly name for audio `device` to the log.
721 // Safe when `device` is guaranteed to be successfully initialized.
print_device_info(device: &IMMDevice) -> Result<(), RenderError>722 fn print_device_info(device: &IMMDevice) -> Result<(), RenderError> {
723 let mut props: *mut IPropertyStore = null_mut();
724 // Safe because `device` is guaranteed to be initialized
725 let hr = unsafe { device.OpenPropertyStore(STGM_READ, &mut props) };
726 check_hresult!(
727 hr,
728 RenderError::from(hr),
729 "Win audio OpenPropertyStore failed."
730 )?;
731
732 // Safe because `props` is guaranteed to be initialized
733 let props = unsafe { ComPtr::from_raw(props) };
734
735 let mut val: PROPVARIANT = Default::default();
736 // Safe because `props` is guaranteed to be initialized
737 let hr = unsafe { props.GetValue(&PKEY_Device_FriendlyName, &mut val) };
738 check_hresult!(
739 hr,
740 RenderError::from(hr),
741 "Win audio property store GetValue failed."
742 )?;
743
744 // Safe because `val` was populated by a successful GetValue call that returns a pwszVal
745 if unsafe { val.data.pwszVal().is_null() } {
746 warn!("Win audio property store GetValue returned a null string");
747 return Err(RenderError::GenericError);
748 }
749 // Safe because `val` was populated by a successful GetValue call that returned a non-null
750 // null-terminated pwszVal
751 let device_name = unsafe { win_util::from_ptr_win32_wide_string(*val.data.pwszVal()) };
752 info!("Creating audio client: {}", device_name);
753 // Safe because `val` was populated by a successful GetValue call
754 unsafe {
755 // TODO(b/256244007): `PropVariantClear` doesn't get called if this function errors or
756 // returns early.
757 PropVariantClear(&mut val);
758 }
759
760 Ok(())
761 }
762
create_and_set_audio_client_event( audio_client: &IAudioClient, ex: &Option<&dyn audio_streams::AudioStreamsExecutor>, ) -> Result<(Event, Option<Box<dyn EventAsyncWrapper>>), RenderError>763 fn create_and_set_audio_client_event(
764 audio_client: &IAudioClient,
765 ex: &Option<&dyn audio_streams::AudioStreamsExecutor>,
766 ) -> Result<(Event, Option<Box<dyn EventAsyncWrapper>>), RenderError> {
767 let ready_event = Event::new_auto_reset().unwrap();
768 // Safe because `ready_event` will be initialized and also it will have the same
769 // lifetime as `audio_client` because they are owned by DeviceRenderer or DeviceCapturer on
770 // return.
771 let hr = unsafe { audio_client.SetEventHandle(ready_event.as_raw_descriptor()) };
772 check_hresult!(hr, RenderError::from(hr), "SetEventHandle() failed.")?;
773
774 let async_ready_event = if let Some(ex) = ex {
775 // Unsafe if `ready_event` and `async_ready_event` have different
776 // lifetimes because both can close the underlying `RawDescriptor`. However, both
777 // will be stored in the `DeviceRenderer` or `DeviceCapturer` fields, so this should be
778 // safe.
779 Some(unsafe {
780 ex.async_event(ready_event.as_raw_descriptor())
781 .map_err(|e| {
782 RenderError::AsyncError(e, "Failed to create async event".to_string())
783 })?
784 })
785 } else {
786 None
787 };
788 Ok((ready_event, async_ready_event))
789 }
790
get_device_period_in_frames(audio_client: &IAudioClient, format: &WaveAudioFormat) -> usize791 fn get_device_period_in_frames(audio_client: &IAudioClient, format: &WaveAudioFormat) -> usize {
792 let mut shared_default_size_in_100nanoseconds: i64 = 0;
793 let mut exclusive_min: i64 = 0;
794 // Safe because `GetDevicePeriod` are taking in intialized valid i64's on the stack created above.
795 unsafe {
796 audio_client.GetDevicePeriod(
797 &mut shared_default_size_in_100nanoseconds,
798 &mut exclusive_min,
799 );
800 };
801
802 format.get_shared_audio_engine_period_in_frames(shared_default_size_in_100nanoseconds as f64)
803 }
804
check_endpoint_buffer_size( audio_client: &IAudioClient, shared_audio_engine_period_in_frames: usize, ) -> Result<u32, RenderError>805 fn check_endpoint_buffer_size(
806 audio_client: &IAudioClient,
807 shared_audio_engine_period_in_frames: usize,
808 ) -> Result<u32, RenderError> {
809 let mut audio_client_buffer_frame_count: u32 = 0;
810 // Safe because audio_client_buffer_frame_count is created above.
811 let hr = unsafe { audio_client.GetBufferSize(&mut audio_client_buffer_frame_count) };
812 check_hresult!(
813 hr,
814 RenderError::from(hr),
815 "Audio Client GetBufferSize() failed."
816 )?;
817
818 if audio_client_buffer_frame_count < shared_audio_engine_period_in_frames as u32 {
819 warn!(
820 "The Windows audio engine period size in frames: {} /
821 is bigger than the Audio Client's buffer size in frames: {}",
822 shared_audio_engine_period_in_frames, audio_client_buffer_frame_count
823 );
824 return Err(RenderError::InvalidIncomingBufferSize);
825 }
826 Ok(audio_client_buffer_frame_count)
827 }
828
829 // TODO(b/253509368): Rename error so it is more generic for rendering and capturing.
830 #[derive(Debug, ThisError)]
831 pub enum RenderError {
832 /// The audio device was unplugged or became unavailable.
833 #[error("win audio device invalidated")]
834 DeviceInvalidated,
835 /// A Windows API error occurred.
836 /// "unknown win audio error HResult: {}, error code: {}"
837 #[error("unknown win audio error HResult: {0}, error code: {1}")]
838 WindowsError(i32, Error),
839 #[error("buffer pointer is null")]
840 InvalidBuffer,
841 #[error("playback buffer error: {0}")]
842 PlaybackBuffer(PlaybackBufferError),
843 #[error("Incoming buffer size invalid")]
844 InvalidIncomingBufferSize,
845 #[error("Failed to wait for Activate Audio Event callback: {0}")]
846 ActivateAudioEventError(Error),
847 #[error("Failed to create Activate Audio Event: {0}")]
848 CreateActivateAudioEventError(Error),
849 #[error("Timed out waiting for Activate Audio Event callback.")]
850 ActivateAudioEventTimeoutError,
851 #[error("Something went wrong in windows audio.")]
852 GenericError,
853 #[error("Invalid guest channel count {0} is > than 2")]
854 InvalidChannelCount(usize),
855 #[error("Async related error: {0}: {1}")]
856 AsyncError(std::io::Error, String),
857 #[error("Ready to read async event was not set during win_audio initialization.")]
858 MissingEventAsync,
859 #[error("Failed to clone an event: {0}")]
860 CloneEvent(Error),
861 }
862
863 impl From<i32> for RenderError {
from(winapi_error_code: i32) -> Self864 fn from(winapi_error_code: i32) -> Self {
865 match winapi_error_code {
866 AUDCLNT_E_DEVICE_INVALIDATED => Self::DeviceInvalidated,
867 _ => Self::WindowsError(winapi_error_code, Error::last()),
868 }
869 }
870 }
871
872 // Unfortunately, Kokoro's VM tests will fail on `GetDefaultAudioEndpoint` most likely because there
873 // are no audio endpoints on the VMs running the test. These tests can be ran on a windows machine
874 // with an audio device though.
875 //
876 // Thus these test are ignored, but are good for local testing. To run, just use the command:
877 //
878 // $: cargo test -p win_audio win_audio_impl::tests:: -- --ignored
879 //
880 // Also, if a STATUS_DLL_NOT_FOUND exception happens, this is because the r8brain.dll can't be
881 // be found. Just put it in the appropriate spot in the `target` directory.
882 #[cfg(test)]
883 mod tests {
884 use std::thread;
885
886 use metrics::MetricEventType;
887 use once_cell::sync::Lazy;
888 use winapi::shared::ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
889 use winapi::shared::mmreg::WAVEFORMATEX;
890 use winapi::shared::mmreg::WAVEFORMATEXTENSIBLE;
891 use winapi::shared::mmreg::WAVE_FORMAT_EXTENSIBLE;
892 use winapi::shared::winerror::S_OK;
893
894 use super::*;
895 // These tests needs to be ran serially because there is a chance that two different tests
896 // running on different threads could open the same event named
897 // ACTIVATE_AUDIO_INTERFACE_COMPLETION_EVENT.
898 // So the first test thread could trigger it correctly, but the second test thread could open
899 // the same triggered event even though the `ActivateAudioInterfaceAsync` operation hasn't
900 // completed, thus causing an error.
901 //
902 // TODO(b/217768491): Randomizing events should resolve the need for serialized tests.
903 static SERIALIZE_LOCK: Lazy<Mutex<()>> = Lazy::new(Mutex::default);
904
905 struct SafeCoInit;
906 impl SafeCoInit {
new_coinitialize() -> Self907 fn new_coinitialize() -> Self {
908 unsafe {
909 CoInitializeEx(null_mut(), COINIT_APARTMENTTHREADED);
910 }
911 SafeCoInit {}
912 }
913 }
914
915 impl Drop for SafeCoInit {
drop(&mut self)916 fn drop(&mut self) {
917 unsafe {
918 CoUninitialize();
919 }
920 }
921 }
922
923 #[ignore]
924 #[test]
test_create_win_audio_renderer_no_co_initliazed()925 fn test_create_win_audio_renderer_no_co_initliazed() {
926 let _shared = SERIALIZE_LOCK.lock();
927 let win_audio_renderer = DeviceRenderer::new(2, 48000, 720, None);
928 assert!(win_audio_renderer.is_err());
929 }
930
931 #[ignore]
932 #[test]
test_create_win_audio_renderer()933 fn test_create_win_audio_renderer() {
934 let _shared = SERIALIZE_LOCK.lock();
935 let _co_init = SafeCoInit::new_coinitialize();
936 let win_audio_renderer_result = DeviceRenderer::new(2, 48000, 480, None);
937 assert!(win_audio_renderer_result.is_ok());
938 let win_audio_renderer = win_audio_renderer_result.unwrap();
939 assert_eq!(
940 win_audio_renderer
941 .audio_shared_format
942 .shared_audio_engine_period_in_frames,
943 480
944 );
945 }
946
947 #[ignore]
948 #[test]
test_create_playback_stream()949 fn test_create_playback_stream() {
950 let _shared = SERIALIZE_LOCK.lock();
951 let mut win_audio: WinAudio = WinAudio::new().unwrap();
952 let (_, mut stream_source) = win_audio
953 .new_playback_stream(2, SampleFormat::S16LE, 48000, 480)
954 .unwrap();
955 let playback_buffer = stream_source.next_playback_buffer().unwrap();
956
957 assert_eq!(playback_buffer.frame_capacity(), 480);
958 }
959
960 #[ignore]
961 #[test]
962 // If the guest buffer is too big, then
963 // there is no way to copy audio samples over succiently.
test_guest_buffer_size_bigger_than_audio_render_client_buffer_size()964 fn test_guest_buffer_size_bigger_than_audio_render_client_buffer_size() {
965 let _shared = SERIALIZE_LOCK.lock();
966 let win_audio_renderer = DeviceRenderer::new(2, 48000, 100000, None);
967
968 assert!(win_audio_renderer.is_err());
969 }
970
971 #[ignore]
972 #[test]
test_co_init_called_once_per_thread()973 fn test_co_init_called_once_per_thread() {
974 let _shared = SERIALIZE_LOCK.lock();
975 // Call co init in a background thread
976 let join_handle = thread::spawn(move || {
977 assert_eq!(WinAudio::co_init_once_per_thread(), S_OK);
978 });
979
980 // Wait for thread to finish
981 join_handle
982 .join()
983 .expect("Thread calling co_init_once_per_thread panicked");
984
985 // Call co init twice on the main thread.
986 assert_eq!(WinAudio::co_init_once_per_thread(), S_OK);
987 // Without thread local once_only this should fail
988 assert_eq!(WinAudio::co_init_once_per_thread(), S_SKIPPED_COINIT);
989 unsafe {
990 CoUninitialize();
991 }
992 }
993
994 // Test may be flakey because other tests will be creating an AudioClient. Putting all tests
995 // in one so we can run this individually to prevent the flakiness. This test may fail
996 // depending on your selected default audio device.
997 #[ignore]
998 #[test]
test_check_format_get_mix_format_success()999 fn test_check_format_get_mix_format_success() {
1000 let _shared = SERIALIZE_LOCK.lock();
1001
1002 let _co_init = SafeCoInit::new_coinitialize();
1003 let audio_client = create_audio_client(eRender).unwrap();
1004 let mut format_ptr: *mut WAVEFORMATEX = std::ptr::null_mut();
1005 let _hr = unsafe { audio_client.GetMixFormat(&mut format_ptr) };
1006
1007 // Safe because `format_ptr` is not a null pointer, since it is set by `GetMixFormat`.
1008 let format = unsafe { WaveAudioFormat::new(format_ptr) };
1009
1010 // Test format from `GetMixFormat`. This should ALWAYS be valid.
1011 assert!(check_format(
1012 &*audio_client,
1013 &format,
1014 WaveFormatDetailsProto::new(),
1015 MetricEventType::AudioFormatRequestOk,
1016 )
1017 .is_ok());
1018
1019 let format = WAVEFORMATEXTENSIBLE {
1020 Format: WAVEFORMATEX {
1021 wFormatTag: WAVE_FORMAT_EXTENSIBLE,
1022 nChannels: 2,
1023 nSamplesPerSec: 48000,
1024 nAvgBytesPerSec: 8 * 48000,
1025 nBlockAlign: 8,
1026 wBitsPerSample: 32,
1027 cbSize: 22,
1028 },
1029 Samples: 32,
1030 dwChannelMask: 3,
1031 SubFormat: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
1032 };
1033
1034 // Safe because `GetMixFormat` casts `WAVEFORMATEXTENSIBLE` into a `WAVEFORMATEX` like so.
1035 // Also this is casting from a bigger to a smaller struct, so it shouldn't be possible for
1036 // this contructor to access memory it shouldn't.
1037 let format = unsafe { WaveAudioFormat::new((&format) as *const _ as *mut WAVEFORMATEX) };
1038
1039 // Test valid custom format.
1040 assert!(check_format(
1041 &*audio_client,
1042 &format,
1043 WaveFormatDetailsProto::new(),
1044 MetricEventType::AudioFormatRequestOk,
1045 )
1046 .is_ok());
1047
1048 let format = WAVEFORMATEXTENSIBLE {
1049 Format: WAVEFORMATEX {
1050 wFormatTag: WAVE_FORMAT_EXTENSIBLE,
1051 nChannels: 2,
1052 nSamplesPerSec: 48000,
1053 nAvgBytesPerSec: 8 * 48000,
1054 nBlockAlign: 8,
1055 wBitsPerSample: 3, // This value will cause failure, since bitdepth of 3
1056 // doesn't make sense
1057 cbSize: 22,
1058 },
1059 Samples: 32,
1060 dwChannelMask: 3,
1061 SubFormat: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
1062 };
1063
1064 // Safe because `GetMixFormat` casts `WAVEFORMATEXTENSIBLE` into a `WAVEFORMATEX` like so.
1065 // Also this is casting from a bigger to a smaller struct, so it shouldn't be possible for
1066 // this contructor to access memory it shouldn't.
1067 let format = unsafe { WaveAudioFormat::new((&format) as *const _ as *mut WAVEFORMATEX) };
1068
1069 // Test invalid format
1070 assert!(check_format(
1071 &*audio_client,
1072 &format,
1073 WaveFormatDetailsProto::new(),
1074 MetricEventType::AudioFormatRequestOk,
1075 )
1076 .is_err());
1077 }
1078 }
1079