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 use std::mem::MaybeUninit;
6 use std::sync::atomic::AtomicUsize;
7 use std::sync::atomic::Ordering;
8
9 use async_trait::async_trait;
10 use audio_streams::capture::AsyncCaptureBuffer;
11 use audio_streams::capture::AsyncCaptureBufferStream;
12 use audio_streams::AsyncBufferCommit;
13 use audio_streams::AsyncPlaybackBufferStream;
14 use audio_streams::AudioStreamsExecutor;
15 use audio_streams::BoxError;
16 use audio_streams::NoopStream;
17 use audio_streams::NoopStreamControl;
18 use audio_streams::SampleFormat;
19 use audio_streams::StreamControl;
20 use audio_streams::StreamSource;
21 use audio_streams::StreamSourceGenerator;
22 use base::error;
23 use base::warn;
24 use metrics::MetricEventType;
25
26 use super::NoopBufferCommit;
27 use crate::intermediate_resampler_buffer::CaptureResamplerBuffer;
28 use crate::intermediate_resampler_buffer::PlaybackResamplerBuffer;
29 use crate::CaptureError;
30 use crate::CapturerStream;
31 use crate::DeviceCapturerWrapper;
32 use crate::DeviceRenderer;
33 use crate::DeviceRendererWrapper;
34 use crate::RenderError;
35 use crate::RendererStream;
36 use crate::WinAudio;
37 use crate::WinAudioCapturer;
38 use crate::WinAudioError;
39 use crate::WinAudioRenderer;
40
41 // These global values are used to prevent metrics upload spam.
42 const ERROR_METRICS_LOG_LIMIT: usize = 5;
43 static INIT_ERRORS_LOGGED_COUNT: AtomicUsize = AtomicUsize::new(0);
44 static PLAYBACK_ERRORS_LOGGED_COUNT: AtomicUsize = AtomicUsize::new(0);
45
46 pub struct WinAudioStreamSourceGenerator {}
47
48 impl StreamSourceGenerator for WinAudioStreamSourceGenerator {
generate(&self) -> std::result::Result<Box<dyn StreamSource>, BoxError>49 fn generate(&self) -> std::result::Result<Box<dyn StreamSource>, BoxError> {
50 Ok(Box::new(WinAudio::new()?))
51 }
52 }
53
54 impl WinAudio {
new_async_playback_stream_helper( num_channels: usize, format: SampleFormat, frame_rate: u32, buffer_size: usize, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result< ( Box<dyn StreamControl>, Box<dyn audio_streams::AsyncPlaybackBufferStream>, ), BoxError, >55 pub(super) fn new_async_playback_stream_helper(
56 num_channels: usize,
57 format: SampleFormat,
58 frame_rate: u32,
59 buffer_size: usize,
60 ex: &dyn audio_streams::AudioStreamsExecutor,
61 ) -> Result<
62 (
63 Box<dyn StreamControl>,
64 Box<dyn audio_streams::AsyncPlaybackBufferStream>,
65 ),
66 BoxError,
67 > {
68 let hr = WinAudio::co_init_once_per_thread();
69 let _ = check_hresult!(hr, WinAudioError::from(hr), "Co Initialized failed");
70
71 let playback_buffer_stream: Box<dyn AsyncPlaybackBufferStream> =
72 match WinAudioRenderer::new_async(
73 num_channels,
74 format,
75 frame_rate,
76 buffer_size,
77 ex,
78 None,
79 ) {
80 Ok(renderer) => Box::new(renderer),
81 Err(e) => {
82 warn!(
83 "Failed to create WinAudioRenderer. Fallback to NoopStream with error: {}",
84 e
85 );
86 Box::new(NoopStream::new(
87 num_channels,
88 SampleFormat::S16LE,
89 frame_rate,
90 buffer_size,
91 ))
92 }
93 };
94
95 Ok((Box::new(NoopStreamControl::new()), playback_buffer_stream))
96 }
97 }
98
99 impl WinAudioRenderer {
100 /// Constructor to allow for async audio backend.
new_async( num_channels: usize, guest_bit_depth: SampleFormat, frame_rate: u32, incoming_buffer_size_in_frames: usize, ex: &dyn audio_streams::AudioStreamsExecutor, audio_client_guid: Option<String>, ) -> Result<Self, RenderError>101 pub fn new_async(
102 num_channels: usize,
103 guest_bit_depth: SampleFormat,
104 frame_rate: u32,
105 incoming_buffer_size_in_frames: usize,
106 ex: &dyn audio_streams::AudioStreamsExecutor,
107 audio_client_guid: Option<String>,
108 ) -> Result<Self, RenderError> {
109 let device = DeviceRendererWrapper::new(
110 num_channels,
111 guest_bit_depth,
112 frame_rate,
113 incoming_buffer_size_in_frames,
114 Some(ex),
115 audio_client_guid.clone(),
116 )
117 .map_err(|e| {
118 match &e {
119 RenderError::WinAudioError(win_audio_error) => {
120 log_init_error_with_limit(win_audio_error.into());
121 }
122 _ => {
123 log_init_error_with_limit((&WinAudioError::Unknown).into());
124 error!(
125 "Unhandled NoopStream forced error. These errors should not have been \
126 returned: {}",
127 e
128 );
129 }
130 }
131 e
132 })?;
133
134 Ok(Self {
135 device,
136 audio_client_guid,
137 })
138 }
139
unregister_notification_client_and_make_new_device_renderer( &mut self, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<(), BoxError>140 fn unregister_notification_client_and_make_new_device_renderer(
141 &mut self,
142 ex: &dyn audio_streams::AudioStreamsExecutor,
143 ) -> Result<(), BoxError> {
144 base::info!("Device found. Will attempt to make a DeviceRenderer");
145 let device_renderer = DeviceRendererWrapper::create_device_renderer_and_log_time(
146 self.device.num_channels,
147 self.device.guest_frame_rate,
148 self.device.incoming_buffer_size_in_frames,
149 Some(ex),
150 self.audio_client_guid.clone(),
151 )
152 .map_err(|e| {
153 match &e {
154 RenderError::WinAudioError(win_audio_error) => {
155 log_playback_error_with_limit(win_audio_error.into())
156 }
157 _ => log_playback_error_with_limit((&WinAudioError::Unknown).into()),
158 }
159 Box::new(e)
160 })?;
161
162 let audio_shared_format = device_renderer.audio_shared_format;
163
164 let playback_resampler_buffer = PlaybackResamplerBuffer::new(
165 self.device.guest_frame_rate as usize,
166 audio_shared_format.frame_rate,
167 self.device.incoming_buffer_size_in_frames,
168 audio_shared_format.shared_audio_engine_period_in_frames,
169 audio_shared_format.channels,
170 audio_shared_format.channel_mask,
171 )
172 .expect("Failed to create PlaybackResamplerBuffer");
173
174 self.device.renderer_stream =
175 RendererStream::Device((device_renderer, playback_resampler_buffer));
176
177 Ok(())
178 }
179 }
180
181 /// Attach `descriptor` to the event code `AudioNoopStreamForced` and upload to clearcut.
182 ///
183 /// This method will stop uploading after `ERRO_METRICS_LOG_LIMIT` uploads in order to prevent
184 /// metrics upload spam.
log_init_error_with_limit(descriptor: i64)185 pub(crate) fn log_init_error_with_limit(descriptor: i64) {
186 if INIT_ERRORS_LOGGED_COUNT.load(Ordering::SeqCst) <= ERROR_METRICS_LOG_LIMIT {
187 metrics::log_descriptor(MetricEventType::AudioNoopStreamForced, descriptor);
188 INIT_ERRORS_LOGGED_COUNT.fetch_add(1, Ordering::SeqCst);
189 }
190 }
191
192 #[async_trait(?Send)]
193 impl AsyncPlaybackBufferStream for WinAudioRenderer {
next_playback_buffer<'a>( &'a mut self, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<audio_streams::AsyncPlaybackBuffer<'a>, BoxError>194 async fn next_playback_buffer<'a>(
195 &'a mut self,
196 ex: &dyn audio_streams::AudioStreamsExecutor,
197 ) -> Result<audio_streams::AsyncPlaybackBuffer<'a>, BoxError> {
198 // Check to see if a new device is available, if so, create a new `DeviceRenderer`.
199 if let RendererStream::Noop(noop_renderer) = &self.device.renderer_stream {
200 if noop_renderer
201 .is_device_available
202 .fetch_and(false, Ordering::SeqCst)
203 {
204 match self.unregister_notification_client_and_make_new_device_renderer(ex) {
205 Ok(()) => {}
206 Err(e) => warn!(
207 "Making a new DeviceRenderer failed in the middle of playback. \
208 Will continue using NoopStream and listening for new devices: {}",
209 e
210 ),
211 };
212 }
213 }
214
215 if let RendererStream::Device((device_renderer, _)) = &mut self.device.renderer_stream {
216 if device_renderer.should_get_next_win_buffer {
217 if let Err(e) = device_renderer.async_next_win_buffer().await {
218 Self::handle_playback_logging_on_error(&e);
219 // At this point, the `DeviceRenderer` doesn't exist, so we assume that
220 // there were no available audio devices.
221 base::info!(
222 "async_next_win_buffer failed. Starting NoopStream and start \
223 listening for a new default device"
224 );
225 self.device.renderer_stream =
226 DeviceRendererWrapper::create_noop_stream_with_device_notification(
227 self.device.num_channels,
228 self.device.guest_frame_rate,
229 self.device.incoming_buffer_size_in_frames,
230 )
231 .inspect_err(|e| match &e {
232 RenderError::WinAudioError(win_audio_error) => {
233 log_playback_error_with_limit(win_audio_error.into())
234 }
235 _ => log_playback_error_with_limit((&WinAudioError::Unknown).into()),
236 })?;
237 }
238 }
239 }
240
241 if let RendererStream::Noop(noop_renderer) = &mut self.device.renderer_stream {
242 // This will trigger the sleep so that virtio sound doesn't write to win_audio too
243 // quickly, which will cause underruns. No audio samples will actually be written to
244 // this buffer, but it doesn't matter becuase those samples are meant to be dropped
245 // anyways.
246 AsyncPlaybackBufferStream::next_playback_buffer(&mut noop_renderer.noop_stream, ex)
247 .await?;
248 }
249
250 self.device
251 .get_intermediate_async_buffer()
252 .map_err(|e| Box::new(e) as _)
253 }
254 }
255
256 #[async_trait(?Send)]
257 impl AsyncBufferCommit for DeviceRendererWrapper {
commit(&mut self, nframes: usize)258 async fn commit(&mut self, nframes: usize) {
259 if nframes != self.incoming_buffer_size_in_frames {
260 warn!(
261 "AsyncBufferCommit commited {} frames, instead of a full period of {}",
262 nframes, self.incoming_buffer_size_in_frames
263 );
264 }
265
266 match &mut self.renderer_stream {
267 RendererStream::Device((device_renderer, playback_resampler_buffer)) => {
268 // `intermediate_buffer` will contain audio samples from CrosVm's emulated audio
269 // device (ie. Virtio Sound). First, we will add the audio samples to the resampler
270 // buffer.
271 playback_resampler_buffer.convert_and_add(self.intermediate_buffer.as_slice());
272
273 if playback_resampler_buffer.is_priming {
274 if device_renderer.win_buffer.is_null() {
275 error!("AsyncBufferCommit: win_buffer is null");
276 return;
277 }
278
279 let format = device_renderer.audio_shared_format;
280 let shared_audio_engine_period_bytes =
281 format.get_shared_audio_engine_period_in_bytes();
282 Self::write_slice_to_wasapi_buffer_and_release_buffer(
283 device_renderer,
284 &vec![0; shared_audio_engine_period_bytes],
285 );
286
287 // WASAPI's `GetBuffer` should be called next because we either wrote to the
288 // Windows endpoint buffer or the audio samples were dropped.
289 device_renderer.should_get_next_win_buffer = true;
290 return;
291 }
292
293 if let Some(next_period) = playback_resampler_buffer.get_next_period() {
294 if device_renderer.win_buffer.is_null() {
295 error!("AsyncBufferCommit: win_buffer is null");
296 return;
297 }
298 Self::write_slice_to_wasapi_buffer_and_release_buffer(
299 device_renderer,
300 next_period,
301 );
302 device_renderer.should_get_next_win_buffer = true;
303 } else {
304 // Don't call WASAPI's `GetBuffer` because the resampler didn't have enough
305 // audio samples write a full period in the Windows endpoint buffer.
306 device_renderer.should_get_next_win_buffer = false;
307 }
308 }
309 // For the `Noop` case, we can just drop the incoming audio samples.
310 RendererStream::Noop(_) => {}
311 }
312 }
313 }
314
315 impl DeviceRendererWrapper {
write_slice_to_wasapi_buffer_and_release_buffer( device_renderer: &DeviceRenderer, slice_to_write: &[u8], )316 fn write_slice_to_wasapi_buffer_and_release_buffer(
317 device_renderer: &DeviceRenderer,
318 slice_to_write: &[u8],
319 ) {
320 let format = device_renderer.audio_shared_format;
321 let shared_audio_engine_period_bytes = format.get_shared_audio_engine_period_in_bytes();
322
323 // SAFETY: win_buffer is a valid pointer to shared_audio_engine_period_bytes of data
324 let win_buffer_slice = unsafe {
325 std::slice::from_raw_parts_mut(
326 device_renderer.win_buffer,
327 shared_audio_engine_period_bytes,
328 )
329 };
330
331 win_buffer_slice.copy_from_slice(slice_to_write);
332 // SAFETY: We own the buffer
333 unsafe {
334 let hr = device_renderer
335 .audio_render_client
336 .ReleaseBuffer(format.shared_audio_engine_period_in_frames as u32, 0);
337 if let Err(e) = check_hresult!(
338 hr,
339 WinAudioError::ReleaseBufferError(hr),
340 "Audio Render Client ReleaseBuffer() failed"
341 ) {
342 log_playback_error_with_limit((&e).into());
343 }
344 }
345 }
346 }
347
log_playback_error_with_limit(descriptor: i64)348 pub(crate) fn log_playback_error_with_limit(descriptor: i64) {
349 if PLAYBACK_ERRORS_LOGGED_COUNT.load(Ordering::SeqCst) <= ERROR_METRICS_LOG_LIMIT {
350 metrics::log_descriptor(MetricEventType::AudioPlaybackError, descriptor);
351 PLAYBACK_ERRORS_LOGGED_COUNT.fetch_add(1, Ordering::SeqCst);
352 }
353 }
354
355 impl DeviceRenderer {
356 /// Similiar to `next_win_buffer`, this is the async version that will return a wrapper
357 /// the WASAPI buffer.
358 ///
359 /// Unlike `next_win_buffer`, there is no timeout if `async_ready_to_read_event` doesn't fire.
360 /// This should be fine, since the end result with or without the timeout will be no audio.
async_next_win_buffer(&mut self) -> Result<(), RenderError>361 async fn async_next_win_buffer(&mut self) -> Result<(), RenderError> {
362 self.win_buffer = MaybeUninit::uninit().as_mut_ptr();
363
364 // We will wait for windows to tell us when it is ready to take in the next set of
365 // audio samples from the guest
366 loop {
367 let async_ready_to_read_event = self
368 .async_ready_to_read_event
369 .as_ref()
370 .ok_or(RenderError::WinAudioError(WinAudioError::MissingEventAsync))?;
371 async_ready_to_read_event.wait().await.map_err(|e| {
372 RenderError::WinAudioError(WinAudioError::AsyncError(
373 e,
374 "Failed to wait for async event to get next playback buffer.".to_string(),
375 ))
376 })?;
377
378 if self.enough_available_frames()? {
379 break;
380 }
381 }
382
383 self.get_buffer()?;
384
385 Ok(())
386 }
387 }
388
389 impl WinAudioCapturer {
new_async( num_channels: usize, guest_bit_depth: SampleFormat, frame_rate: u32, outgoing_buffer_size_in_frames: usize, ex: &dyn audio_streams::AudioStreamsExecutor, ) -> Result<Self, CaptureError>390 pub fn new_async(
391 num_channels: usize,
392 guest_bit_depth: SampleFormat,
393 frame_rate: u32,
394 outgoing_buffer_size_in_frames: usize,
395 ex: &dyn audio_streams::AudioStreamsExecutor,
396 ) -> Result<Self, CaptureError> {
397 let device = DeviceCapturerWrapper::new(
398 num_channels,
399 guest_bit_depth,
400 frame_rate,
401 outgoing_buffer_size_in_frames,
402 Some(ex),
403 )?;
404
405 Ok(Self { device })
406 }
407
unregister_notification_client_and_make_new_device_capturer( &mut self, ex: &dyn AudioStreamsExecutor, ) -> Result<(), BoxError>408 fn unregister_notification_client_and_make_new_device_capturer(
409 &mut self,
410 ex: &dyn AudioStreamsExecutor,
411 ) -> Result<(), BoxError> {
412 let device_capturer = DeviceCapturerWrapper::create_device_capturer_and_log_time(
413 self.device.num_channels,
414 self.device.guest_frame_rate,
415 self.device.outgoing_buffer_size_in_frames,
416 Some(ex),
417 )
418 .map_err(Box::new)?;
419
420 let audio_shared_format = device_capturer.audio_shared_format;
421
422 let capture_resampler_buffer = CaptureResamplerBuffer::new_input_resampler(
423 audio_shared_format.frame_rate,
424 self.device.guest_frame_rate as usize,
425 self.device.outgoing_buffer_size_in_frames,
426 audio_shared_format.channels,
427 audio_shared_format.channel_mask,
428 )
429 .expect("Failed to create CaptureResamplerBuffer.");
430
431 self.device.capturer_stream =
432 CapturerStream::Device((device_capturer, capture_resampler_buffer, NoopBufferCommit));
433
434 Ok(())
435 }
436 }
437
438 #[async_trait(?Send)]
439 impl AsyncCaptureBufferStream for WinAudioCapturer {
next_capture_buffer<'a>( &'a mut self, ex: &dyn AudioStreamsExecutor, ) -> Result<AsyncCaptureBuffer<'a>, BoxError>440 async fn next_capture_buffer<'a>(
441 &'a mut self,
442 ex: &dyn AudioStreamsExecutor,
443 ) -> Result<AsyncCaptureBuffer<'a>, BoxError> {
444 // In the `Noop` state, check to see if there is a new device connected. If so, create a
445 // `DeviceCapturer`.
446 if let CapturerStream::Noop(noop_capturer) = &self.device.capturer_stream {
447 if noop_capturer
448 .is_device_available
449 .fetch_and(false, Ordering::SeqCst)
450 {
451 match self.unregister_notification_client_and_make_new_device_capturer(ex) {
452 Ok(()) => {}
453 Err(e) => warn!(
454 "Making a new DeviceCapturer failed in the middle of capture \
455 Will continue using NoopCaptureStream and listening for new devices: {}",
456 e
457 ),
458 }
459 }
460 }
461
462 // Try to drain bytes from the Windows buffer into the capture resample buffer, which acts
463 // as a sink. If any part fails, the `Noop` state is set.
464 if let CapturerStream::Device((device_capturer, capture_resampler_buffer, _)) =
465 &mut self.device.capturer_stream
466 {
467 match DeviceCapturerWrapper::drain_until_bytes_avaialable(
468 device_capturer,
469 capture_resampler_buffer,
470 self.device.outgoing_buffer_size_in_frames,
471 )
472 .await
473 {
474 Ok(()) => {}
475 Err(e) => {
476 warn!(
477 "Making a new DeviceCapturer failed in the middle of capture. \
478 Will continue using NoopStream and listening for new devices: {}",
479 e
480 );
481 self.device.capturer_stream =
482 DeviceCapturerWrapper::create_noop_capture_stream_with_device_notification(
483 self.device.num_channels,
484 self.device.guest_bit_depth,
485 self.device.guest_frame_rate,
486 self.device.outgoing_buffer_size_in_frames,
487 )
488 .map_err(Box::new)?;
489 }
490 };
491 }
492
493 // Return the buffer to be written to shared memory.
494 match &mut self.device.capturer_stream {
495 CapturerStream::Device((_, capture_resampler_buffer, noop_buffer_commit)) => {
496 DeviceCapturerWrapper::get_async_capture_buffer(
497 capture_resampler_buffer,
498 noop_buffer_commit,
499 )
500 .map_err(|e| Box::new(e) as _)
501 }
502 CapturerStream::Noop(noop_capturer) => {
503 AsyncCaptureBufferStream::next_capture_buffer(
504 &mut noop_capturer.noop_capture_stream,
505 ex,
506 )
507 .await
508 }
509 }
510 }
511 }
512
513 #[cfg(test)]
514 mod tests {
515 use cros_async::Executor;
516
517 use super::*;
518 use crate::WinStreamSourceGenerator;
519
520 // This test is meant to run through the normal audio playback procedure in order to make
521 // debugging easier. The test needs to be ran with a playback device format of 48KHz,
522 // stereo, 16bit. This test might not pass on AMD, since its period is 513 instead of 480.
523 #[ignore]
524 #[test]
test_async()525 fn test_async() {
526 async fn test(ex: &Executor) {
527 let stream_source_generator: Box<dyn StreamSourceGenerator> =
528 Box::new(WinAudioStreamSourceGenerator {});
529 let mut stream_source = stream_source_generator
530 .generate()
531 .expect("Failed to create stream source.");
532
533 let (_, mut async_pb_stream) = stream_source
534 .new_async_playback_stream(2, SampleFormat::S16LE, 48000, 480, ex)
535 .expect("Failed to create async playback stream.");
536
537 let mut async_pb_buffer = async_pb_stream
538 .next_playback_buffer(ex)
539 .await
540 .expect("Failed to get next playback buffer");
541
542 // The buffer size is calculated by "period * channels * bit depth". The actual buffer
543 // from `next_playback_buffer` may vary, depending on the device format and the user's
544 // machine.
545 let buffer = [1u8; 480 * 2 * 2];
546
547 async_pb_buffer
548 .copy_cb(buffer.len(), |out| out.copy_from_slice(&buffer))
549 .unwrap();
550
551 async_pb_buffer.commit().await;
552
553 let mut async_pb_buffer = async_pb_stream
554 .next_playback_buffer(ex)
555 .await
556 .expect("Failed to get next playback buffer");
557
558 let buffer = [1u8; 480 * 2 * 2];
559
560 async_pb_buffer
561 .copy_cb(buffer.len(), |out| out.copy_from_slice(&buffer))
562 .expect("Failed to copy samples from buffer to win_buffer");
563
564 async_pb_buffer.commit().await;
565 }
566
567 let ex = Executor::new().expect("Failed to create executor.");
568
569 ex.run_until(test(&ex)).unwrap();
570 }
571
572 // This test is meant to run through the normal audio capture procedure in order to make
573 // debugging easier.
574 #[ignore]
575 #[test]
test_async_capture()576 fn test_async_capture() {
577 async fn test(ex: &Executor) {
578 let stream_source_generator: Box<dyn WinStreamSourceGenerator> =
579 Box::new(WinAudioStreamSourceGenerator {});
580 let mut stream_source = stream_source_generator
581 .generate()
582 .expect("Failed to create stream source.");
583
584 let (mut async_cp_stream, _shared_format) = stream_source
585 .new_async_capture_stream_and_get_shared_format(
586 2,
587 SampleFormat::S16LE,
588 48000,
589 480,
590 ex,
591 )
592 .expect("Failed to create async capture stream.");
593
594 let mut async_cp_buffer = async_cp_stream
595 .next_capture_buffer(ex)
596 .await
597 .expect("Failed to get next capture buffer");
598
599 // Capacity of 480 frames, where there are 2 channels and 2 bytes per sample.
600 let mut buffer_to_send_to_guest = Vec::with_capacity(480 * 2 * 2);
601
602 async_cp_buffer
603 .copy_cb(buffer_to_send_to_guest.len(), |win_buffer| {
604 buffer_to_send_to_guest.copy_from_slice(win_buffer);
605 })
606 .expect("Failed to copy samples from win_buffer to buffer");
607 }
608
609 let ex = Executor::new().expect("Failed to create executor.");
610
611 ex.run_until(test(&ex)).unwrap();
612 }
613 }
614