summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authorxians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-05 01:01:37 +0000
committerxians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-05 01:01:37 +0000
commit1c6893e13f60b3e458e88d3b181a6448c4d79192 (patch)
tree7899527ec1e2445c7627bbbe1bbf7cc2b668aaf7 /media/audio
parentbabed2e690d3880c9a0e39a92ee68e6dfde9ba95 (diff)
downloadchromium_src-1c6893e13f60b3e458e88d3b181a6448c4d79192.zip
chromium_src-1c6893e13f60b3e458e88d3b181a6448c4d79192.tar.gz
chromium_src-1c6893e13f60b3e458e88d3b181a6448c4d79192.tar.bz2
Re-write the alsa polling scheduling to make it work with low buffer audio, for example, 10ms for playout.
Before this patch, the alsa scheme sets a frame_avail_wanted as half of the ALSA buffer size, then it checks if the available_frame buffer slot in ALSA is smaller than frame_avail_wanted, if it is, which means that ALSA has more buffer than we need, then it schedule the next time to be FrameToMills(frame_avail_wanted - available_frame). Otherwise, we invoke the next write immediately. But this scheduling scheme doesn't handle the following cases well: #1, It has a chance running into a dead lock that we always have less than half of the buffer filled, where it runs into a busy looping. This happens mostly when the buffer size is small. #2, We can write more data to ALSA than what it needs. This patch fixes the problems by initiating the next time for the moment when half of packet is played out. Then it checks with ALSA on how many frames are available, and do a re-scheduling: if the available frames are not enough for a packet, it schedules the next write for the moment when the buffer for a packet becomes available, if the next write happens in less than 10ms, then it will use 10ms in order to avoid back to back writing; if the existing frames are less than half packet, it make next write to be immediate. Review URL: http://codereview.chromium.org/7976047 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@104043 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/linux/alsa_output.cc52
-rw-r--r--media/audio/linux/alsa_output_unittest.cc17
2 files changed, 45 insertions, 24 deletions
diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc
index 91b1efd..0fb967b 100644
--- a/media/audio/linux/alsa_output.cc
+++ b/media/audio/linux/alsa_output.cc
@@ -347,7 +347,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_ = 2 * frames_per_packet_;
} else {
alsa_buffer_frames_ = buffer_size;
}
@@ -597,25 +598,44 @@ 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;
+ // Next write is initially scheduled for the moment when half of a packet
+ // has been played out.
+ uint32 minimal_frames_wanted = frames_per_packet_ / 2;
+ uint32 next_fill_time_ms = FramesToMillis(minimal_frames_wanted,
+ sample_rate_);
- // 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_);
+ // Set existing_frames to be minimal_frames_wanted to avoid busy looping
+ // in case alsa_buffer_frames_ is smaller than available_freams.
+ uint32 available_frames = GetAvailableFrames();
+ uint32 existing_frames = (alsa_buffer_frames_ >= available_frames) ?
+ (alsa_buffer_frames_ - available_frames) : minimal_frames_wanted;
+
+ // ALSA is full, schedule another write when some buffer is available.
+ // This helps reduce the CPU usage.
+ if (available_frames == 0) {
+ next_fill_time_ms = std::min(next_fill_time_ms, kNoDataSleepMilliseconds);
+ message_loop_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AlsaPcmOutputStream::ScheduleNextWrite,
+ weak_factory_.GetWeakPtr(), false),
+ next_fill_time_ms);
+ return;
}
- // Adjust for timer resolution issues.
- if (next_fill_time_ms < kSleepErrorMilliseconds) {
+
+ // Re-schedule the next write for the moment when the available buffer is
+ // enough for a packet. Avoid back-to-back writing by setting
+ // kNoDataSleepMilliseconds as the minimal interval.
+ if (available_frames < frames_per_packet_) {
+ next_fill_time_ms = std::max(
+ kNoDataSleepMilliseconds,
+ FramesToMillis(frames_per_packet_ - available_frames,
+ sample_rate_));
+ }
+ else if (existing_frames < minimal_frames_wanted) {
+ // If less than half of the packet in the buffer, invoke the write callback
+ // immediately to fill more data.
next_fill_time_ms = 0;
- } else {
- next_fill_time_ms -= kSleepErrorMilliseconds;
}
// Avoid busy looping if the data source is exhausted.
diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc
index 9fe2b92..875814b 100644
--- a/media/audio/linux/alsa_output_unittest.cc
+++ b/media/audio/linux/alsa_output_unittest.cc
@@ -436,13 +436,6 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(kFakeHandle, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0), Return(0)));
- EXPECT_CALL(mock_callback,
- OnMoreData(test_stream_.get(), _, kTestPacketSize, _))
- .Times(2)
- .WillOnce(Return(kTestPacketSize))
- .WillOnce(Return(0));
- EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
- .WillOnce(Return(kTestFramesPerPacket));
// Expect scheduling.
EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
@@ -451,7 +444,15 @@ TEST_F(AlsaPcmOutputStreamTest, StartStop) {
.WillOnce(Return(kTestFramesPerPacket))
.WillRepeatedly(DoAll(InvokeWithoutArgs(&message_loop_,
&MessageLoop::QuitNow),
- Return(0))); // Buffer is full.
+ Return(0))); // Buffer is full.
+
+ EXPECT_CALL(mock_callback,
+ OnMoreData(test_stream_.get(), _, kTestPacketSize, _))
+ .Times(2)
+ .WillOnce(Return(kTestPacketSize))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
+ .WillOnce(Return(kTestFramesPerPacket));
test_stream_->Start(&mock_callback);
message_loop_.RunAllPending();