Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(847)

Side by Side Diff: media/audio/win/audio_low_latency_output_win.cc

Issue 1111503003: Revert of Switch to STA mode for audio thread and WASAPI I/O streams. (Closed) Base URL: http://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « media/audio/win/audio_low_latency_output_win.h ('k') | media/audio/win/audio_low_latency_output_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698