Chromium Code Reviews| 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/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| (...skipping 432 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 443 wait_array, | 443 wait_array, |
| 444 FALSE, | 444 FALSE, |
| 445 INFINITE); | 445 INFINITE); |
| 446 | 446 |
| 447 switch (wait_result) { | 447 switch (wait_result) { |
| 448 case WAIT_OBJECT_0 + 0: | 448 case WAIT_OBJECT_0 + 0: |
| 449 // |stop_render_event_| has been set. | 449 // |stop_render_event_| has been set. |
| 450 playing = false; | 450 playing = false; |
| 451 break; | 451 break; |
| 452 case WAIT_OBJECT_0 + 1: | 452 case WAIT_OBJECT_0 + 1: |
| 453 { | 453 // |audio_samples_render_event_| has been set. |
| 454 TRACE_EVENT0("audio", "WASAPIAudioOutputStream::Run"); | 454 RenderAudioFromSource(audio_clock, device_frequency); |
| 455 | |
| 456 // |audio_samples_render_event_| has been set. | |
| 457 UINT32 num_queued_frames = 0; | |
| 458 uint8* audio_data = NULL; | |
| 459 | |
| 460 // Contains how much new data we can write to the buffer without | |
| 461 // the risk of overwriting previously written data that the audio | |
| 462 // engine has not yet read from the buffer. | |
| 463 size_t num_available_frames = 0; | |
| 464 | |
| 465 if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { | |
| 466 // Get the padding value which represents the amount of rendering | |
| 467 // data that is queued up to play in the endpoint buffer. | |
| 468 hr = audio_client_->GetCurrentPadding(&num_queued_frames); | |
| 469 num_available_frames = | |
| 470 endpoint_buffer_size_frames_ - num_queued_frames; | |
| 471 } else { | |
| 472 // While the stream is running, the system alternately sends one | |
| 473 // buffer or the other to the client. This form of double buffering | |
| 474 // is referred to as "ping-ponging". Each time the client receives | |
| 475 // a buffer from the system (triggers this event) the client must | |
| 476 // process the entire buffer. Calls to the GetCurrentPadding method | |
| 477 // are unnecessary because the packet size must always equal the | |
| 478 // buffer size. In contrast to the shared mode buffering scheme, | |
| 479 // the latency for an event-driven, exclusive-mode stream depends | |
| 480 // directly on the buffer size. | |
| 481 num_available_frames = endpoint_buffer_size_frames_; | |
| 482 } | |
| 483 if (FAILED(hr)) { | |
| 484 DLOG(ERROR) << "Failed to retrieve amount of available space: " | |
| 485 << std::hex << hr; | |
| 486 continue; | |
| 487 } | |
| 488 | |
| 489 // It can happen that we were not able to find a a perfect match | |
| 490 // between the native device rate and the endpoint buffer size. | |
| 491 // In this case, we are using a packet size which equals the enpoint | |
| 492 // buffer size (does not lead to lowest possible delay and is rare | |
| 493 // case) and must therefore wait for yet another callback until we | |
| 494 // are able to provide data. | |
| 495 if ((num_available_frames > 0) && | |
| 496 (num_available_frames != packet_size_frames_)) { | |
| 497 continue; | |
| 498 } | |
| 499 | |
| 500 // Grab all available space in the rendering endpoint buffer | |
| 501 // into which the client can write a data packet. | |
| 502 hr = audio_render_client_->GetBuffer(packet_size_frames_, | |
| 503 &audio_data); | |
| 504 if (FAILED(hr)) { | |
| 505 DLOG(ERROR) << "Failed to use rendering audio buffer: " | |
| 506 << std::hex << hr; | |
| 507 continue; | |
| 508 } | |
| 509 | |
| 510 // Derive the audio delay which corresponds to the delay between | |
| 511 // a render event and the time when the first audio sample in a | |
| 512 // packet is played out through the speaker. This delay value | |
| 513 // can typically be utilized by an acoustic echo-control (AEC) | |
| 514 // unit at the render side. | |
| 515 UINT64 position = 0; | |
| 516 int audio_delay_bytes = 0; | |
| 517 hr = audio_clock->GetPosition(&position, NULL); | |
| 518 if (SUCCEEDED(hr)) { | |
| 519 // Stream position of the sample that is currently playing | |
| 520 // through the speaker. | |
| 521 double pos_sample_playing_frames = format_.Format.nSamplesPerSec * | |
| 522 (static_cast<double>(position) / device_frequency); | |
| 523 | |
| 524 // Stream position of the last sample written to the endpoint | |
| 525 // buffer. Note that, the packet we are about to receive in | |
| 526 // the upcoming callback is also included. | |
| 527 size_t pos_last_sample_written_frames = | |
| 528 num_written_frames_ + packet_size_frames_; | |
| 529 | |
| 530 // Derive the actual delay value which will be fed to the | |
| 531 // render client using the OnMoreData() callback. | |
| 532 audio_delay_bytes = (pos_last_sample_written_frames - | |
| 533 pos_sample_playing_frames) * format_.Format.nBlockAlign; | |
| 534 } | |
| 535 | |
| 536 // Read a data packet from the registered client source and | |
| 537 // deliver a delay estimate in the same callback to the client. | |
| 538 // A time stamp is also stored in the AudioBuffersState. This | |
| 539 // time stamp can be used at the client side to compensate for | |
| 540 // the delay between the usage of the delay value and the time | |
| 541 // of generation. | |
| 542 | |
| 543 uint32 num_filled_bytes = 0; | |
| 544 const int bytes_per_sample = format_.Format.wBitsPerSample >> 3; | |
| 545 | |
| 546 int frames_filled = source_->OnMoreData( | |
| 547 audio_bus_.get(), AudioBuffersState(0, audio_delay_bytes)); | |
| 548 num_filled_bytes = frames_filled * format_.Format.nBlockAlign; | |
| 549 DCHECK_LE(num_filled_bytes, packet_size_bytes_); | |
| 550 | |
| 551 // Note: If this ever changes to output raw float the data must be | |
| 552 // clipped and sanitized since it may come from an untrusted | |
| 553 // source such as NaCl. | |
| 554 audio_bus_->ToInterleaved( | |
| 555 frames_filled, bytes_per_sample, audio_data); | |
| 556 | |
| 557 // Perform in-place, software-volume adjustments. | |
| 558 media::AdjustVolume(audio_data, | |
| 559 num_filled_bytes, | |
| 560 audio_bus_->channels(), | |
| 561 bytes_per_sample, | |
| 562 volume_); | |
| 563 | |
| 564 // Zero out the part of the packet which has not been filled by | |
| 565 // the client. Using silence is the least bad option in this | |
| 566 // situation. | |
| 567 if (num_filled_bytes < packet_size_bytes_) { | |
| 568 memset(&audio_data[num_filled_bytes], 0, | |
| 569 (packet_size_bytes_ - num_filled_bytes)); | |
| 570 } | |
| 571 | |
| 572 // Release the buffer space acquired in the GetBuffer() call. | |
| 573 DWORD flags = 0; | |
| 574 audio_render_client_->ReleaseBuffer(packet_size_frames_, | |
| 575 flags); | |
| 576 | |
| 577 num_written_frames_ += packet_size_frames_; | |
| 578 } | |
| 579 break; | 455 break; |
| 580 default: | 456 default: |
| 581 error = true; | 457 error = true; |
| 582 break; | 458 break; |
| 583 } | 459 } |
| 584 } | 460 } |
| 585 | 461 |
| 586 if (playing && error) { | 462 if (playing && error) { |
| 587 // Stop audio rendering since something has gone wrong in our main thread | 463 // Stop audio rendering since something has gone wrong in our main thread |
| 588 // loop. Note that, we are still in a "started" state, hence a Stop() call | 464 // loop. Note that, we are still in a "started" state, hence a Stop() call |
| 589 // is required to join the thread properly. | 465 // is required to join the thread properly. |
| 590 audio_client_->Stop(); | 466 audio_client_->Stop(); |
| 591 PLOG(ERROR) << "WASAPI rendering failed."; | 467 PLOG(ERROR) << "WASAPI rendering failed."; |
| 592 } | 468 } |
| 593 | 469 |
| 594 // Disable MMCSS. | 470 // Disable MMCSS. |
| 595 if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) { | 471 if (mm_task && !avrt::AvRevertMmThreadCharacteristics(mm_task)) { |
| 596 PLOG(WARNING) << "Failed to disable MMCSS"; | 472 PLOG(WARNING) << "Failed to disable MMCSS"; |
| 597 } | 473 } |
| 598 } | 474 } |
| 599 | 475 |
| 476 void WASAPIAudioOutputStream::RenderAudioFromSource( | |
| 477 IAudioClock* audio_clock, UINT64 device_frequency) { | |
| 478 TRACE_EVENT0("audio", "WASAPIAudioOutputStream::Run"); | |
|
tommi (sloooow) - chröme
2013/02/08 15:37:54
change to WASAPIAudioOutputStream::RenderAudioFrom
henrika (OOO until Aug 14)
2013/02/08 15:49:37
Done.
| |
| 479 | |
| 480 HRESULT hr = S_FALSE; | |
| 481 UINT32 num_queued_frames = 0; | |
| 482 uint8* audio_data = NULL; | |
| 483 | |
| 484 // Contains how much new data we can write to the buffer without | |
| 485 // the risk of overwriting previously written data that the audio | |
| 486 // engine has not yet read from the buffer. | |
| 487 size_t num_available_frames = 0; | |
| 488 | |
| 489 if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { | |
| 490 // Get the padding value which represents the amount of rendering | |
| 491 // data that is queued up to play in the endpoint buffer. | |
| 492 hr = audio_client_->GetCurrentPadding(&num_queued_frames); | |
| 493 num_available_frames = | |
| 494 endpoint_buffer_size_frames_ - num_queued_frames; | |
| 495 if (FAILED(hr)) { | |
| 496 DLOG(ERROR) << "Failed to retrieve amount of available space: " | |
| 497 << std::hex << hr; | |
| 498 return; | |
| 499 } | |
| 500 } else { | |
| 501 // While the stream is running, the system alternately sends one | |
| 502 // buffer or the other to the client. This form of double buffering | |
| 503 // is referred to as "ping-ponging". Each time the client receives | |
| 504 // a buffer from the system (triggers this event) the client must | |
| 505 // process the entire buffer. Calls to the GetCurrentPadding method | |
| 506 // are unnecessary because the packet size must always equal the | |
| 507 // buffer size. In contrast to the shared mode buffering scheme, | |
| 508 // the latency for an event-driven, exclusive-mode stream depends | |
| 509 // directly on the buffer size. | |
| 510 num_available_frames = endpoint_buffer_size_frames_; | |
| 511 } | |
| 512 | |
| 513 // Check if there is enough available space to fit the packet size | |
| 514 // specified by the client. | |
| 515 if (num_available_frames < packet_size_frames_) | |
| 516 return; | |
| 517 | |
| 518 DLOG_IF(ERROR, num_available_frames % packet_size_frames_ != 0) | |
| 519 << "Non-perfect timing detected (num_available_frames=" | |
| 520 << num_available_frames << ", packet_size_frames=" | |
| 521 << packet_size_frames_ << ")"; | |
| 522 | |
| 523 // Derive the number of packets we need to get from the client to | |
| 524 // fill up the available area in the endpoint buffer. | |
| 525 // |num_packets| will always be one for exclusive-mode streams and | |
| 526 // will be one in most cases for shared mode streams as well. | |
| 527 // However, we have found that two packets can sometimes be | |
| 528 // required. | |
| 529 size_t num_packets = (num_available_frames / packet_size_frames_); | |
| 530 | |
| 531 for (size_t n = 0; n < num_packets; ++n) { | |
| 532 // Grab all available space in the rendering endpoint buffer | |
| 533 // into which the client can write a data packet. | |
| 534 hr = audio_render_client_->GetBuffer(packet_size_frames_, | |
| 535 &audio_data); | |
| 536 if (FAILED(hr)) { | |
| 537 DLOG(ERROR) << "Failed to use rendering audio buffer: " | |
| 538 << std::hex << hr; | |
| 539 return; | |
| 540 } | |
| 541 | |
| 542 // Derive the audio delay which corresponds to the delay between | |
| 543 // a render event and the time when the first audio sample in a | |
| 544 // packet is played out through the speaker. This delay value | |
| 545 // can typically be utilized by an acoustic echo-control (AEC) | |
| 546 // unit at the render side. | |
| 547 UINT64 position = 0; | |
| 548 int audio_delay_bytes = 0; | |
| 549 hr = audio_clock->GetPosition(&position, NULL); | |
| 550 if (SUCCEEDED(hr)) { | |
| 551 // Stream position of the sample that is currently playing | |
| 552 // through the speaker. | |
| 553 double pos_sample_playing_frames = format_.Format.nSamplesPerSec * | |
| 554 (static_cast<double>(position) / device_frequency); | |
| 555 | |
| 556 // Stream position of the last sample written to the endpoint | |
| 557 // buffer. Note that, the packet we are about to receive in | |
| 558 // the upcoming callback is also included. | |
| 559 size_t pos_last_sample_written_frames = | |
| 560 num_written_frames_ + packet_size_frames_; | |
| 561 | |
| 562 // Derive the actual delay value which will be fed to the | |
| 563 // render client using the OnMoreData() callback. | |
| 564 audio_delay_bytes = (pos_last_sample_written_frames - | |
| 565 pos_sample_playing_frames) * format_.Format.nBlockAlign; | |
| 566 } | |
| 567 | |
| 568 // Read a data packet from the registered client source and | |
| 569 // deliver a delay estimate in the same callback to the client. | |
| 570 // A time stamp is also stored in the AudioBuffersState. This | |
| 571 // time stamp can be used at the client side to compensate for | |
| 572 // the delay between the usage of the delay value and the time | |
| 573 // of generation. | |
| 574 | |
| 575 uint32 num_filled_bytes = 0; | |
| 576 const int bytes_per_sample = format_.Format.wBitsPerSample >> 3; | |
|
tommi (sloooow) - chröme
2013/02/08 15:37:54
nit: move this variable down to where it's used.
henrika (OOO until Aug 14)
2013/02/08 15:49:37
Done.
| |
| 577 | |
| 578 int frames_filled = source_->OnMoreData( | |
| 579 audio_bus_.get(), AudioBuffersState(0, audio_delay_bytes)); | |
| 580 num_filled_bytes = frames_filled * format_.Format.nBlockAlign; | |
|
tommi (sloooow) - chröme
2013/02/08 15:37:54
nit: declare num_filled_bytes here
henrika (OOO until Aug 14)
2013/02/08 15:49:37
Done.
| |
| 581 DCHECK_LE(num_filled_bytes, packet_size_bytes_); | |
| 582 | |
| 583 // Note: If this ever changes to output raw float the data must be | |
| 584 // clipped and sanitized since it may come from an untrusted | |
| 585 // source such as NaCl. | |
| 586 audio_bus_->ToInterleaved( | |
| 587 frames_filled, bytes_per_sample, audio_data); | |
| 588 | |
| 589 // Perform in-place, software-volume adjustments. | |
| 590 media::AdjustVolume(audio_data, | |
| 591 num_filled_bytes, | |
| 592 audio_bus_->channels(), | |
| 593 bytes_per_sample, | |
| 594 volume_); | |
| 595 | |
| 596 // Release the buffer space acquired in the GetBuffer() call. | |
| 597 // Render silence if we were not able to fill up the buffer totally. | |
| 598 DWORD flags = (num_filled_bytes < packet_size_bytes_) ? | |
| 599 AUDCLNT_BUFFERFLAGS_SILENT : 0; | |
| 600 audio_render_client_->ReleaseBuffer(packet_size_frames_, | |
| 601 flags); | |
|
tommi (sloooow) - chröme
2013/02/08 15:37:54
no need to wrap?
henrika (OOO until Aug 14)
2013/02/08 15:49:37
Done.
| |
| 602 | |
| 603 num_written_frames_ += packet_size_frames_; | |
| 604 } | |
| 605 } | |
| 606 | |
| 600 void WASAPIAudioOutputStream::HandleError(HRESULT err) { | 607 void WASAPIAudioOutputStream::HandleError(HRESULT err) { |
| 601 CHECK((started() && GetCurrentThreadId() == render_thread_->tid()) || | 608 CHECK((started() && GetCurrentThreadId() == render_thread_->tid()) || |
| 602 (!started() && GetCurrentThreadId() == creating_thread_id_)); | 609 (!started() && GetCurrentThreadId() == creating_thread_id_)); |
| 603 NOTREACHED() << "Error code: " << std::hex << err; | 610 NOTREACHED() << "Error code: " << std::hex << err; |
| 604 if (source_) | 611 if (source_) |
| 605 source_->OnError(this, static_cast<int>(err)); | 612 source_->OnError(this, static_cast<int>(err)); |
| 606 } | 613 } |
| 607 | 614 |
| 608 HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization( | 615 HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization( |
| 609 IAudioClient* client, HANDLE event_handle, uint32* endpoint_buffer_size) { | 616 IAudioClient* client, HANDLE event_handle, uint32* endpoint_buffer_size) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 678 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr; | 685 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr; |
| 679 return hr; | 686 return hr; |
| 680 } | 687 } |
| 681 | 688 |
| 682 *endpoint_buffer_size = buffer_size_in_frames; | 689 *endpoint_buffer_size = buffer_size_in_frames; |
| 683 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames; | 690 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames; |
| 684 return hr; | 691 return hr; |
| 685 } | 692 } |
| 686 | 693 |
| 687 } // namespace media | 694 } // namespace media |
| OLD | NEW |