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

Unified Diff: media/audio/win/audio_low_latency_output_win.cc

Issue 12220076: Ensures that WASAPI audio output does not go silent in rare cases (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: nits Created 7 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/audio/win/audio_low_latency_output_win.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/audio/win/audio_low_latency_output_win.cc
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index 01be01a85ec2e146b5dd34499894b8017228132d..eb6d9ba1ff9acec84723420ab011a13dfd389f63 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -450,132 +450,8 @@ void WASAPIAudioOutputStream::Run() {
playing = false;
break;
case WAIT_OBJECT_0 + 1:
- {
- TRACE_EVENT0("audio", "WASAPIAudioOutputStream::Run");
-
- // |audio_samples_render_event_| has been set.
- UINT32 num_queued_frames = 0;
- uint8* audio_data = NULL;
-
- // Contains how much new data we can write to the buffer without
- // the risk of overwriting previously written data that the audio
- // engine has not yet read from the buffer.
- size_t num_available_frames = 0;
-
- if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) {
- // Get the padding value which represents the amount of rendering
- // data that is queued up to play in the endpoint buffer.
- hr = audio_client_->GetCurrentPadding(&num_queued_frames);
- num_available_frames =
- endpoint_buffer_size_frames_ - num_queued_frames;
- } else {
- // While the stream is running, the system alternately sends one
- // buffer or the other to the client. This form of double buffering
- // is referred to as "ping-ponging". Each time the client receives
- // a buffer from the system (triggers this event) the client must
- // process the entire buffer. Calls to the GetCurrentPadding method
- // are unnecessary because the packet size must always equal the
- // buffer size. In contrast to the shared mode buffering scheme,
- // the latency for an event-driven, exclusive-mode stream depends
- // directly on the buffer size.
- num_available_frames = endpoint_buffer_size_frames_;
- }
- if (FAILED(hr)) {
- DLOG(ERROR) << "Failed to retrieve amount of available space: "
- << std::hex << hr;
- continue;
- }
-
- // It can happen that we were not able to find a a perfect match
- // between the native device rate and the endpoint buffer size.
- // In this case, we are using a packet size which equals the enpoint
- // buffer size (does not lead to lowest possible delay and is rare
- // case) and must therefore wait for yet another callback until we
- // are able to provide data.
- if ((num_available_frames > 0) &&
- (num_available_frames != packet_size_frames_)) {
- continue;
- }
-
- // Grab all available space in the rendering endpoint buffer
- // into which the client can write a data packet.
- hr = audio_render_client_->GetBuffer(packet_size_frames_,
- &audio_data);
- if (FAILED(hr)) {
- DLOG(ERROR) << "Failed to use rendering audio buffer: "
- << std::hex << hr;
- continue;
- }
-
- // Derive the audio delay which corresponds to the delay between
- // a render event and the time when the first audio sample in a
- // packet is played out through the speaker. This delay value
- // can typically be utilized by an acoustic echo-control (AEC)
- // unit at the render side.
- UINT64 position = 0;
- int audio_delay_bytes = 0;
- hr = audio_clock->GetPosition(&position, NULL);
- if (SUCCEEDED(hr)) {
- // Stream position of the sample that is currently playing
- // through the speaker.
- double pos_sample_playing_frames = format_.Format.nSamplesPerSec *
- (static_cast<double>(position) / device_frequency);
-
- // Stream position of the last sample written to the endpoint
- // buffer. Note that, the packet we are about to receive in
- // the upcoming callback is also included.
- size_t pos_last_sample_written_frames =
- num_written_frames_ + packet_size_frames_;
-
- // Derive the actual delay value which will be fed to the
- // render client using the OnMoreData() callback.
- audio_delay_bytes = (pos_last_sample_written_frames -
- pos_sample_playing_frames) * format_.Format.nBlockAlign;
- }
-
- // Read a data packet from the registered client source and
- // deliver a delay estimate in the same callback to the client.
- // A time stamp is also stored in the AudioBuffersState. This
- // time stamp can be used at the client side to compensate for
- // the delay between the usage of the delay value and the time
- // of generation.
-
- uint32 num_filled_bytes = 0;
- const int bytes_per_sample = format_.Format.wBitsPerSample >> 3;
-
- int frames_filled = source_->OnMoreData(
- audio_bus_.get(), AudioBuffersState(0, audio_delay_bytes));
- num_filled_bytes = frames_filled * format_.Format.nBlockAlign;
- DCHECK_LE(num_filled_bytes, packet_size_bytes_);
-
- // Note: If this ever changes to output raw float the data must be
- // clipped and sanitized since it may come from an untrusted
- // source such as NaCl.
- audio_bus_->ToInterleaved(
- frames_filled, bytes_per_sample, audio_data);
-
- // Perform in-place, software-volume adjustments.
- media::AdjustVolume(audio_data,
- num_filled_bytes,
- audio_bus_->channels(),
- bytes_per_sample,
- volume_);
-
- // Zero out the part of the packet which has not been filled by
- // the client. Using silence is the least bad option in this
- // situation.
- if (num_filled_bytes < packet_size_bytes_) {
- memset(&audio_data[num_filled_bytes], 0,
- (packet_size_bytes_ - num_filled_bytes));
- }
-
- // Release the buffer space acquired in the GetBuffer() call.
- DWORD flags = 0;
- audio_render_client_->ReleaseBuffer(packet_size_frames_,
- flags);
-
- num_written_frames_ += packet_size_frames_;
- }
+ // |audio_samples_render_event_| has been set.
+ RenderAudioFromSource(audio_clock, device_frequency);
break;
default:
error = true;
@@ -597,6 +473,134 @@ void WASAPIAudioOutputStream::Run() {
}
}
+void WASAPIAudioOutputStream::RenderAudioFromSource(
+ IAudioClock* audio_clock, UINT64 device_frequency) {
+ TRACE_EVENT0("audio", "RenderAudioFromSource");
+
+ HRESULT hr = S_FALSE;
+ UINT32 num_queued_frames = 0;
+ uint8* audio_data = NULL;
+
+ // Contains how much new data we can write to the buffer without
+ // the risk of overwriting previously written data that the audio
+ // engine has not yet read from the buffer.
+ size_t num_available_frames = 0;
+
+ if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) {
+ // Get the padding value which represents the amount of rendering
+ // data that is queued up to play in the endpoint buffer.
+ hr = audio_client_->GetCurrentPadding(&num_queued_frames);
+ num_available_frames =
+ endpoint_buffer_size_frames_ - num_queued_frames;
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to retrieve amount of available space: "
+ << std::hex << hr;
+ return;
+ }
+ } else {
+ // While the stream is running, the system alternately sends one
+ // buffer or the other to the client. This form of double buffering
+ // is referred to as "ping-ponging". Each time the client receives
+ // a buffer from the system (triggers this event) the client must
+ // process the entire buffer. Calls to the GetCurrentPadding method
+ // are unnecessary because the packet size must always equal the
+ // buffer size. In contrast to the shared mode buffering scheme,
+ // the latency for an event-driven, exclusive-mode stream depends
+ // directly on the buffer size.
+ num_available_frames = endpoint_buffer_size_frames_;
+ }
+
+ // Check if there is enough available space to fit the packet size
+ // specified by the client.
+ if (num_available_frames < packet_size_frames_)
+ return;
+
+ DLOG_IF(ERROR, num_available_frames % packet_size_frames_ != 0)
+ << "Non-perfect timing detected (num_available_frames="
+ << num_available_frames << ", packet_size_frames="
+ << packet_size_frames_ << ")";
+
+ // Derive the number of packets we need to get from the client to
+ // fill up the available area in the endpoint buffer.
+ // |num_packets| will always be one for exclusive-mode streams and
+ // will be one in most cases for shared mode streams as well.
+ // However, we have found that two packets can sometimes be
+ // required.
+ size_t num_packets = (num_available_frames / packet_size_frames_);
+
+ for (size_t n = 0; n < num_packets; ++n) {
+ // Grab all available space in the rendering endpoint buffer
+ // into which the client can write a data packet.
+ hr = audio_render_client_->GetBuffer(packet_size_frames_,
+ &audio_data);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to use rendering audio buffer: "
+ << std::hex << hr;
+ return;
+ }
+
+ // Derive the audio delay which corresponds to the delay between
+ // a render event and the time when the first audio sample in a
+ // packet is played out through the speaker. This delay value
+ // can typically be utilized by an acoustic echo-control (AEC)
+ // unit at the render side.
+ UINT64 position = 0;
+ int audio_delay_bytes = 0;
+ hr = audio_clock->GetPosition(&position, NULL);
+ if (SUCCEEDED(hr)) {
+ // Stream position of the sample that is currently playing
+ // through the speaker.
+ double pos_sample_playing_frames = format_.Format.nSamplesPerSec *
+ (static_cast<double>(position) / device_frequency);
+
+ // Stream position of the last sample written to the endpoint
+ // buffer. Note that, the packet we are about to receive in
+ // the upcoming callback is also included.
+ size_t pos_last_sample_written_frames =
+ num_written_frames_ + packet_size_frames_;
+
+ // Derive the actual delay value which will be fed to the
+ // render client using the OnMoreData() callback.
+ audio_delay_bytes = (pos_last_sample_written_frames -
+ pos_sample_playing_frames) * format_.Format.nBlockAlign;
+ }
+
+ // Read a data packet from the registered client source and
+ // deliver a delay estimate in the same callback to the client.
+ // A time stamp is also stored in the AudioBuffersState. This
+ // time stamp can be used at the client side to compensate for
+ // the delay between the usage of the delay value and the time
+ // of generation.
+
+ int frames_filled = source_->OnMoreData(
+ audio_bus_.get(), AudioBuffersState(0, audio_delay_bytes));
+ uint32 num_filled_bytes = frames_filled * format_.Format.nBlockAlign;
+ DCHECK_LE(num_filled_bytes, packet_size_bytes_);
+
+ // Note: If this ever changes to output raw float the data must be
+ // clipped and sanitized since it may come from an untrusted
+ // source such as NaCl.
+ const int bytes_per_sample = format_.Format.wBitsPerSample >> 3;
+ audio_bus_->ToInterleaved(
+ frames_filled, bytes_per_sample, audio_data);
+
+ // Perform in-place, software-volume adjustments.
+ media::AdjustVolume(audio_data,
+ num_filled_bytes,
+ audio_bus_->channels(),
+ bytes_per_sample,
+ volume_);
+
+ // Release the buffer space acquired in the GetBuffer() call.
+ // Render silence if we were not able to fill up the buffer totally.
+ DWORD flags = (num_filled_bytes < packet_size_bytes_) ?
+ AUDCLNT_BUFFERFLAGS_SILENT : 0;
+ audio_render_client_->ReleaseBuffer(packet_size_frames_, flags);
+
+ num_written_frames_ += packet_size_frames_;
+ }
+}
+
void WASAPIAudioOutputStream::HandleError(HRESULT err) {
CHECK((started() && GetCurrentThreadId() == render_thread_->tid()) ||
(!started() && GetCurrentThreadId() == creating_thread_id_));
« no previous file with comments | « media/audio/win/audio_low_latency_output_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698