OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/win/audio_low_latency_output_win.h" | 5 #include "media/audio/win/audio_low_latency_output_win.h" |
6 | 6 |
7 #include <Functiondiscoverykeys_devpkey.h> | 7 #include <Functiondiscoverykeys_devpkey.h> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { | 242 if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { |
243 if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence( | 243 if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence( |
244 audio_client_.get(), audio_render_client_.get())) { | 244 audio_client_.get(), audio_render_client_.get())) { |
245 LOG(ERROR) << "Failed to prepare endpoint buffers with silence."; | 245 LOG(ERROR) << "Failed to prepare endpoint buffers with silence."; |
246 callback->OnError(this); | 246 callback->OnError(this); |
247 return; | 247 return; |
248 } | 248 } |
249 } | 249 } |
250 num_written_frames_ = endpoint_buffer_size_frames_; | 250 num_written_frames_ = endpoint_buffer_size_frames_; |
251 | 251 |
252 if (!MarshalComPointers()) { | |
253 callback->OnError(this); | |
254 return; | |
255 } | |
256 | |
257 // Create and start the thread that will drive the rendering by waiting for | 252 // Create and start the thread that will drive the rendering by waiting for |
258 // render events. | 253 // render events. |
259 render_thread_.reset( | 254 render_thread_.reset( |
260 new base::DelegateSimpleThread(this, "wasapi_render_thread")); | 255 new base::DelegateSimpleThread(this, "wasapi_render_thread")); |
261 render_thread_->Start(); | 256 render_thread_->Start(); |
262 if (!render_thread_->HasBeenStarted()) { | 257 if (!render_thread_->HasBeenStarted()) { |
263 LOG(ERROR) << "Failed to start WASAPI render thread."; | 258 LOG(ERROR) << "Failed to start WASAPI render thread."; |
264 StopThread(); | 259 StopThread(); |
265 callback->OnError(this); | 260 callback->OnError(this); |
266 return; | 261 return; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 } | 326 } |
332 volume_ = volume_float; | 327 volume_ = volume_float; |
333 } | 328 } |
334 | 329 |
335 void WASAPIAudioOutputStream::GetVolume(double* volume) { | 330 void WASAPIAudioOutputStream::GetVolume(double* volume) { |
336 DVLOG(1) << "GetVolume()"; | 331 DVLOG(1) << "GetVolume()"; |
337 *volume = static_cast<double>(volume_); | 332 *volume = static_cast<double>(volume_); |
338 } | 333 } |
339 | 334 |
340 void WASAPIAudioOutputStream::Run() { | 335 void WASAPIAudioOutputStream::Run() { |
341 ScopedCOMInitializer com_init; | 336 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); |
342 | 337 |
343 // Increase the thread priority. | 338 // Increase the thread priority. |
344 render_thread_->SetThreadPriority(base::ThreadPriority::REALTIME_AUDIO); | 339 render_thread_->SetThreadPriority(base::ThreadPriority::REALTIME_AUDIO); |
345 | 340 |
346 // Enable MMCSS to ensure that this thread receives prioritized access to | 341 // Enable MMCSS to ensure that this thread receives prioritized access to |
347 // CPU resources. | 342 // CPU resources. |
348 DWORD task_index = 0; | 343 DWORD task_index = 0; |
349 HANDLE mm_task = avrt::AvSetMmThreadCharacteristics(L"Pro Audio", | 344 HANDLE mm_task = avrt::AvSetMmThreadCharacteristics(L"Pro Audio", |
350 &task_index); | 345 &task_index); |
351 bool mmcss_is_ok = | 346 bool mmcss_is_ok = |
352 (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL)); | 347 (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL)); |
353 if (!mmcss_is_ok) { | 348 if (!mmcss_is_ok) { |
354 // Failed to enable MMCSS on this thread. It is not fatal but can lead | 349 // Failed to enable MMCSS on this thread. It is not fatal but can lead |
355 // to reduced QoS at high load. | 350 // to reduced QoS at high load. |
356 DWORD err = GetLastError(); | 351 DWORD err = GetLastError(); |
357 LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; | 352 LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; |
358 } | 353 } |
359 | 354 |
360 // Retrieve COM pointers from the main thread. | |
361 ScopedComPtr<IAudioClient> audio_client; | |
362 ScopedComPtr<IAudioRenderClient> audio_render_client; | |
363 ScopedComPtr<IAudioClock> audio_clock; | |
364 UnmarshalComPointers(&audio_client, &audio_render_client, &audio_clock); | |
365 | |
366 HRESULT hr = S_FALSE; | 355 HRESULT hr = S_FALSE; |
367 | 356 |
368 bool playing = true; | 357 bool playing = true; |
369 bool error = false; | 358 bool error = false; |
370 HANDLE wait_array[] = { stop_render_event_.Get(), | 359 HANDLE wait_array[] = { stop_render_event_.Get(), |
371 audio_samples_render_event_.Get() }; | 360 audio_samples_render_event_.Get() }; |
372 UINT64 device_frequency = 0; | 361 UINT64 device_frequency = 0; |
373 | 362 |
374 // The device frequency is the frequency generated by the hardware clock in | 363 // The device frequency is the frequency generated by the hardware clock in |
375 // the audio device. The GetFrequency() method reports a constant frequency. | 364 // the audio device. The GetFrequency() method reports a constant frequency. |
376 hr = audio_clock->GetFrequency(&device_frequency); | 365 hr = audio_clock_->GetFrequency(&device_frequency); |
377 error = FAILED(hr); | 366 error = FAILED(hr); |
378 PLOG_IF(ERROR, error) << "Failed to acquire IAudioClock interface: " | 367 PLOG_IF(ERROR, error) << "Failed to acquire IAudioClock interface: " |
379 << std::hex << hr; | 368 << std::hex << hr; |
380 | 369 |
381 // Keep rendering audio until the stop event or the stream-switch event | 370 // Keep rendering audio until the stop event or the stream-switch event |
382 // is signaled. An error event can also break the main thread loop. | 371 // is signaled. An error event can also break the main thread loop. |
383 while (playing && !error) { | 372 while (playing && !error) { |
384 // Wait for a close-down event, stream-switch event or a new render event. | 373 // Wait for a close-down event, stream-switch event or a new render event. |
385 DWORD wait_result = WaitForMultipleObjects(arraysize(wait_array), | 374 DWORD wait_result = WaitForMultipleObjects(arraysize(wait_array), |
386 wait_array, | 375 wait_array, |
387 FALSE, | 376 FALSE, |
388 INFINITE); | 377 INFINITE); |
389 | 378 |
390 switch (wait_result) { | 379 switch (wait_result) { |
391 case WAIT_OBJECT_0 + 0: | 380 case WAIT_OBJECT_0 + 0: |
392 // |stop_render_event_| has been set. | 381 // |stop_render_event_| has been set. |
393 playing = false; | 382 playing = false; |
394 break; | 383 break; |
395 case WAIT_OBJECT_0 + 1: | 384 case WAIT_OBJECT_0 + 1: |
396 // |audio_samples_render_event_| has been set. | 385 // |audio_samples_render_event_| has been set. |
397 error = !RenderAudioFromSource(device_frequency, audio_client.get(), | 386 error = !RenderAudioFromSource(device_frequency); |
398 audio_render_client.get(), | |
399 audio_clock.get()); | |
400 break; | 387 break; |
401 default: | 388 default: |
402 error = true; | 389 error = true; |
403 break; | 390 break; |
404 } | 391 } |
405 } | 392 } |
406 | 393 |
407 if (playing && error && audio_client) { | 394 if (playing && error) { |
408 // Stop audio rendering since something has gone wrong in our main thread | 395 // Stop audio rendering since something has gone wrong in our main thread |
409 // loop. Note that, we are still in a "started" state, hence a Stop() call | 396 // loop. Note that, we are still in a "started" state, hence a Stop() call |
410 // is required to join the thread properly. | 397 // is required to join the thread properly. |
411 audio_client->Stop(); | 398 audio_client_->Stop(); |
412 PLOG(ERROR) << "WASAPI rendering failed."; | 399 PLOG(ERROR) << "WASAPI rendering failed."; |
413 } | 400 } |
414 | 401 |
415 // Disable MMCSS. | 402 // Disable MMCSS. |
416 if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) { | 403 if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) { |
417 PLOG(WARNING) << "Failed to disable MMCSS"; | 404 PLOG(WARNING) << "Failed to disable MMCSS"; |
418 } | 405 } |
419 } | 406 } |
420 | 407 |
421 bool WASAPIAudioOutputStream::RenderAudioFromSource( | 408 bool WASAPIAudioOutputStream::RenderAudioFromSource(UINT64 device_frequency) { |
422 UINT64 device_frequency, | |
423 IAudioClient* audio_client, | |
424 IAudioRenderClient* audio_render_client, | |
425 IAudioClock* audio_clock) { | |
426 TRACE_EVENT0("audio", "RenderAudioFromSource"); | 409 TRACE_EVENT0("audio", "RenderAudioFromSource"); |
427 | 410 |
428 HRESULT hr = S_FALSE; | 411 HRESULT hr = S_FALSE; |
429 UINT32 num_queued_frames = 0; | 412 UINT32 num_queued_frames = 0; |
430 uint8* audio_data = NULL; | 413 uint8* audio_data = NULL; |
431 | 414 |
432 // Contains how much new data we can write to the buffer without | 415 // Contains how much new data we can write to the buffer without |
433 // the risk of overwriting previously written data that the audio | 416 // the risk of overwriting previously written data that the audio |
434 // engine has not yet read from the buffer. | 417 // engine has not yet read from the buffer. |
435 size_t num_available_frames = 0; | 418 size_t num_available_frames = 0; |
436 | 419 |
437 if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { | 420 if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { |
438 // Get the padding value which represents the amount of rendering | 421 // Get the padding value which represents the amount of rendering |
439 // data that is queued up to play in the endpoint buffer. | 422 // data that is queued up to play in the endpoint buffer. |
440 hr = audio_client->GetCurrentPadding(&num_queued_frames); | 423 hr = audio_client_->GetCurrentPadding(&num_queued_frames); |
441 num_available_frames = | 424 num_available_frames = |
442 endpoint_buffer_size_frames_ - num_queued_frames; | 425 endpoint_buffer_size_frames_ - num_queued_frames; |
443 if (FAILED(hr)) { | 426 if (FAILED(hr)) { |
444 DLOG(ERROR) << "Failed to retrieve amount of available space: " | 427 DLOG(ERROR) << "Failed to retrieve amount of available space: " |
445 << std::hex << hr; | 428 << std::hex << hr; |
446 return false; | 429 return false; |
447 } | 430 } |
448 } else { | 431 } else { |
449 // While the stream is running, the system alternately sends one | 432 // While the stream is running, the system alternately sends one |
450 // buffer or the other to the client. This form of double buffering | 433 // buffer or the other to the client. This form of double buffering |
(...skipping 21 matching lines...) Expand all Loading... |
472 // fill up the available area in the endpoint buffer. | 455 // fill up the available area in the endpoint buffer. |
473 // |num_packets| will always be one for exclusive-mode streams and | 456 // |num_packets| will always be one for exclusive-mode streams and |
474 // will be one in most cases for shared mode streams as well. | 457 // will be one in most cases for shared mode streams as well. |
475 // However, we have found that two packets can sometimes be | 458 // However, we have found that two packets can sometimes be |
476 // required. | 459 // required. |
477 size_t num_packets = (num_available_frames / packet_size_frames_); | 460 size_t num_packets = (num_available_frames / packet_size_frames_); |
478 | 461 |
479 for (size_t n = 0; n < num_packets; ++n) { | 462 for (size_t n = 0; n < num_packets; ++n) { |
480 // Grab all available space in the rendering endpoint buffer | 463 // Grab all available space in the rendering endpoint buffer |
481 // into which the client can write a data packet. | 464 // into which the client can write a data packet. |
482 hr = audio_render_client->GetBuffer(packet_size_frames_, &audio_data); | 465 hr = audio_render_client_->GetBuffer(packet_size_frames_, |
| 466 &audio_data); |
483 if (FAILED(hr)) { | 467 if (FAILED(hr)) { |
484 DLOG(ERROR) << "Failed to use rendering audio buffer: " | 468 DLOG(ERROR) << "Failed to use rendering audio buffer: " |
485 << std::hex << hr; | 469 << std::hex << hr; |
486 return false; | 470 return false; |
487 } | 471 } |
488 | 472 |
489 // Derive the audio delay which corresponds to the delay between | 473 // Derive the audio delay which corresponds to the delay between |
490 // a render event and the time when the first audio sample in a | 474 // a render event and the time when the first audio sample in a |
491 // packet is played out through the speaker. This delay value | 475 // packet is played out through the speaker. This delay value |
492 // can typically be utilized by an acoustic echo-control (AEC) | 476 // can typically be utilized by an acoustic echo-control (AEC) |
493 // unit at the render side. | 477 // unit at the render side. |
494 UINT64 position = 0; | 478 UINT64 position = 0; |
495 uint32 audio_delay_bytes = 0; | 479 uint32 audio_delay_bytes = 0; |
496 hr = audio_clock->GetPosition(&position, NULL); | 480 hr = audio_clock_->GetPosition(&position, NULL); |
497 if (SUCCEEDED(hr)) { | 481 if (SUCCEEDED(hr)) { |
498 // Stream position of the sample that is currently playing | 482 // Stream position of the sample that is currently playing |
499 // through the speaker. | 483 // through the speaker. |
500 double pos_sample_playing_frames = format_.Format.nSamplesPerSec * | 484 double pos_sample_playing_frames = format_.Format.nSamplesPerSec * |
501 (static_cast<double>(position) / device_frequency); | 485 (static_cast<double>(position) / device_frequency); |
502 | 486 |
503 // Stream position of the last sample written to the endpoint | 487 // Stream position of the last sample written to the endpoint |
504 // buffer. Note that, the packet we are about to receive in | 488 // buffer. Note that, the packet we are about to receive in |
505 // the upcoming callback is also included. | 489 // the upcoming callback is also included. |
506 size_t pos_last_sample_written_frames = | 490 size_t pos_last_sample_written_frames = |
(...skipping 19 matching lines...) Expand all Loading... |
526 const int bytes_per_sample = format_.Format.wBitsPerSample >> 3; | 510 const int bytes_per_sample = format_.Format.wBitsPerSample >> 3; |
527 audio_bus_->Scale(volume_); | 511 audio_bus_->Scale(volume_); |
528 audio_bus_->ToInterleaved( | 512 audio_bus_->ToInterleaved( |
529 frames_filled, bytes_per_sample, audio_data); | 513 frames_filled, bytes_per_sample, audio_data); |
530 | 514 |
531 | 515 |
532 // Release the buffer space acquired in the GetBuffer() call. | 516 // Release the buffer space acquired in the GetBuffer() call. |
533 // Render silence if we were not able to fill up the buffer totally. | 517 // Render silence if we were not able to fill up the buffer totally. |
534 DWORD flags = (num_filled_bytes < packet_size_bytes_) ? | 518 DWORD flags = (num_filled_bytes < packet_size_bytes_) ? |
535 AUDCLNT_BUFFERFLAGS_SILENT : 0; | 519 AUDCLNT_BUFFERFLAGS_SILENT : 0; |
536 audio_render_client->ReleaseBuffer(packet_size_frames_, flags); | 520 audio_render_client_->ReleaseBuffer(packet_size_frames_, flags); |
537 | 521 |
538 num_written_frames_ += packet_size_frames_; | 522 num_written_frames_ += packet_size_frames_; |
539 } | 523 } |
540 | 524 |
541 return true; | 525 return true; |
542 } | 526 } |
543 | 527 |
544 HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization( | 528 HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization( |
545 IAudioClient* client, HANDLE event_handle, uint32* endpoint_buffer_size) { | 529 IAudioClient* client, HANDLE event_handle, uint32* endpoint_buffer_size) { |
546 DCHECK_EQ(share_mode_, AUDCLNT_SHAREMODE_EXCLUSIVE); | 530 DCHECK_EQ(share_mode_, AUDCLNT_SHAREMODE_EXCLUSIVE); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
631 render_thread_.reset(); | 615 render_thread_.reset(); |
632 | 616 |
633 // Ensure that we don't quit the main thread loop immediately next | 617 // Ensure that we don't quit the main thread loop immediately next |
634 // time Start() is called. | 618 // time Start() is called. |
635 ResetEvent(stop_render_event_.Get()); | 619 ResetEvent(stop_render_event_.Get()); |
636 } | 620 } |
637 | 621 |
638 source_ = NULL; | 622 source_ = NULL; |
639 } | 623 } |
640 | 624 |
641 bool WASAPIAudioOutputStream::MarshalComPointers() { | |
642 DCHECK_EQ(creating_thread_id_, base::PlatformThread::CurrentId()); | |
643 DCHECK(!com_stream_); | |
644 | |
645 ScopedComPtr<IStream> com_stream; | |
646 HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, com_stream.Receive()); | |
647 if (FAILED(hr)) { | |
648 DLOG(ERROR) << "Failed to create stream for marshaling COM pointers."; | |
649 return false; | |
650 } | |
651 | |
652 hr = CoMarshalInterface(com_stream.get(), __uuidof(IAudioClient), | |
653 audio_client_.get(), MSHCTX_INPROC, NULL, | |
654 MSHLFLAGS_NORMAL); | |
655 if (FAILED(hr)) { | |
656 DLOG(ERROR) << "Marshal failed for IAudioClient: " << std::hex << hr; | |
657 return false; | |
658 } | |
659 | |
660 hr = CoMarshalInterface(com_stream.get(), __uuidof(IAudioRenderClient), | |
661 audio_render_client_.get(), MSHCTX_INPROC, NULL, | |
662 MSHLFLAGS_NORMAL); | |
663 if (FAILED(hr)) { | |
664 DLOG(ERROR) << "Marshal failed for IAudioRenderClient: " << std::hex << hr; | |
665 return false; | |
666 } | |
667 | |
668 hr = CoMarshalInterface(com_stream.get(), __uuidof(IAudioClock), | |
669 audio_clock_.get(), MSHCTX_INPROC, NULL, | |
670 MSHLFLAGS_NORMAL); | |
671 if (FAILED(hr)) { | |
672 DLOG(ERROR) << "Marshal failed for IAudioClock: " << std::hex << hr; | |
673 return false; | |
674 } | |
675 | |
676 LARGE_INTEGER pos = {0}; | |
677 hr = com_stream->Seek(pos, STREAM_SEEK_SET, NULL); | |
678 if (FAILED(hr)) { | |
679 DLOG(ERROR) << "Failed to seek IStream for marshaling: " << std::hex << hr; | |
680 return false; | |
681 } | |
682 | |
683 com_stream_ = com_stream.Pass(); | |
684 return true; | |
685 } | |
686 | |
687 void WASAPIAudioOutputStream::UnmarshalComPointers( | |
688 ScopedComPtr<IAudioClient>* audio_client, | |
689 ScopedComPtr<IAudioRenderClient>* audio_render_client, | |
690 ScopedComPtr<IAudioClock>* audio_clock) { | |
691 DCHECK_EQ(render_thread_->tid(), base::PlatformThread::CurrentId()); | |
692 | |
693 DCHECK(com_stream_); | |
694 ScopedComPtr<IStream> com_stream; | |
695 com_stream = com_stream_.Pass(); | |
696 | |
697 HRESULT hr = CoUnmarshalInterface(com_stream.get(), __uuidof(IAudioClient), | |
698 audio_client->ReceiveVoid()); | |
699 CHECK(SUCCEEDED(hr)); | |
700 | |
701 hr = CoUnmarshalInterface(com_stream.get(), __uuidof(IAudioRenderClient), | |
702 audio_render_client->ReceiveVoid()); | |
703 CHECK(SUCCEEDED(hr)); | |
704 | |
705 hr = CoUnmarshalInterface(com_stream.get(), __uuidof(IAudioClock), | |
706 audio_clock->ReceiveVoid()); | |
707 CHECK(SUCCEEDED(hr)); | |
708 } | |
709 | |
710 } // namespace media | 625 } // namespace media |
OLD | NEW |