summaryrefslogtreecommitdiffstats
path: root/media/audio/linux/alsa_output.cc
diff options
context:
space:
mode:
authorxians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-14 09:47:44 +0000
committerxians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-14 09:47:44 +0000
commit954927055ce96c94deff0a0f52727d1ed8fd3497 (patch)
tree3f4dba5f11d3d8d8cfebaeb9755d4ae93a522453 /media/audio/linux/alsa_output.cc
parent42130d3f849d626cbf4f5bbd4e3189ef89cd82c6 (diff)
downloadchromium_src-954927055ce96c94deff0a0f52727d1ed8fd3497.zip
chromium_src-954927055ce96c94deff0a0f52727d1ed8fd3497.tar.gz
chromium_src-954927055ce96c94deff0a0f52727d1ed8fd3497.tar.bz2
Reland 7976047 to fix the alsa output scheduling for low latency audio.
This patch is to allow using 10ms buffersize or above, and it also optimize the WebAudio performance by using 5ms as the minimum interview between two OnMoreData() calls. Bug=None Test=media_unittests, manully tests: WebAudio: http://chromium.googlecode.com/svn/trunk/samples/audio/shiny-drum-machine.html (change audio_util.cc to return 512 as buffersize) WebRTC: Internal test app Html5: http://www.youtube.com/watch?v=siOHh0uzcuY Chromebook: http://www.youtube.com/watch?v=-9AtssRoc2Q&feature=related Review URL: http://codereview.chromium.org/8465018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@109852 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio/linux/alsa_output.cc')
-rw-r--r--media/audio/linux/alsa_output.cc50
1 files changed, 34 insertions, 16 deletions
diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc
index 91b1efd..3086853 100644
--- a/media/audio/linux/alsa_output.cc
+++ b/media/audio/linux/alsa_output.cc
@@ -58,6 +58,9 @@
// busy looping.
static const uint32 kNoDataSleepMilliseconds = 10;
+// Mininum interval between OnMoreData() calls.
+const uint32 kMinIntervalBetweenOnMoreDataCallsInMs = 5;
+
// According to the linux nanosleep manpage, nanosleep on linux can miss the
// deadline by up to 10ms because the kernel timeslice is 10ms. Give a 2x
// buffer to compensate for the timeslice, and any additional slowdowns.
@@ -347,7 +350,8 @@ void AlsaPcmOutputStream::OpenTask() {
if (error < 0) {
LOG(ERROR) << "Failed to get playback buffer size from ALSA: "
<< wrapper_->StrError(error);
- alsa_buffer_frames_ = frames_per_packet_;
+ // Buffer size is at least twice of packet size.
+ alsa_buffer_frames_ = frames_per_packet_ * 2;
} else {
alsa_buffer_frames_ = buffer_size;
}
@@ -427,8 +431,9 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
*source_exhausted = false;
- // Request more data if we have capacity.
- if (buffer_->forward_capacity() > buffer_->forward_bytes()) {
+ // Request more data only when we run out of data in the buffer, because
+ // WritePacket() comsumes only the current chunk of data.
+ if (!buffer_->forward_bytes()) {
// Before making a request to source for data we need to determine the
// delay (in bytes) for the requested data to be played.
@@ -597,25 +602,38 @@ void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) {
return;
}
- // Next write is scheduled for the moment when half of the buffer is
- // available.
uint32 frames_avail_wanted = alsa_buffer_frames_ / 2;
uint32 available_frames = GetAvailableFrames();
- uint32 next_fill_time_ms = 0;
+ uint32 frames_in_buffer = buffer_->forward_bytes() / bytes_per_output_frame_;
- // It's possible to have more frames available than what we want, in which
- // case we'll leave our |next_fill_time_ms| at 0ms.
- if (available_frames < frames_avail_wanted) {
- uint32 frames_until_empty_enough = frames_avail_wanted - available_frames;
- next_fill_time_ms =
- FramesToMillis(frames_until_empty_enough, sample_rate_);
- }
+ // Next write is initially scheduled for the moment when half of a packet
+ // has been played out.
+ uint32 next_fill_time_ms =
+ FramesToMillis(frames_per_packet_ / 2, sample_rate_);
- // Adjust for timer resolution issues.
- if (next_fill_time_ms < kSleepErrorMilliseconds) {
+ if (frames_in_buffer && (frames_in_buffer <= available_frames)) {
+ // There is data in the current buffer, consume them immediately if we have
+ // enough space in the soundcard.
next_fill_time_ms = 0;
} else {
- next_fill_time_ms -= kSleepErrorMilliseconds;
+ // Otherwise schedule the next write for the moment when half of the alsa
+ // buffer becomes available.
+ if (available_frames < frames_avail_wanted) {
+ uint32 frames_until_empty_enough = frames_avail_wanted - available_frames;
+ next_fill_time_ms =
+ FramesToMillis(frames_until_empty_enough, sample_rate_);
+
+ // Adjust for time resolution.
+ if (next_fill_time_ms > kNoDataSleepMilliseconds)
+ next_fill_time_ms -= kNoDataSleepMilliseconds;
+
+ // Avoid back-to-back writing.
+ if (next_fill_time_ms < kMinIntervalBetweenOnMoreDataCallsInMs)
+ next_fill_time_ms = kMinIntervalBetweenOnMoreDataCallsInMs;
+ } else if (available_frames == alsa_buffer_frames_) {
+ // Buffer is empty, invoke next write immediately.
+ next_fill_time_ms = 0;
+ }
}
// Avoid busy looping if the data source is exhausted.