Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // THREAD SAFETY | 5 // THREAD SAFETY |
| 6 // | 6 // |
| 7 // AlsaPcmOutputStream object is *not* thread-safe -- we assume that | 7 // AlsaPcmOutputStream object is *not* thread-safe -- we assume that |
| 8 // client thread - creates the object and calls the public APIs. | 8 // client thread - creates the object and calls the public APIs. |
| 9 // message loop thread - executes all the internal tasks including querying | 9 // message loop thread - executes all the internal tasks including querying |
| 10 // the data source for more data, writing to the alsa device, and closing | 10 // the data source for more data, writing to the alsa device, and closing |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 51 #include "media/audio/linux/alsa_util.h" | 51 #include "media/audio/linux/alsa_util.h" |
| 52 #include "media/audio/linux/alsa_wrapper.h" | 52 #include "media/audio/linux/alsa_wrapper.h" |
| 53 #include "media/audio/linux/audio_manager_linux.h" | 53 #include "media/audio/linux/audio_manager_linux.h" |
| 54 #include "media/base/data_buffer.h" | 54 #include "media/base/data_buffer.h" |
| 55 #include "media/base/seekable_buffer.h" | 55 #include "media/base/seekable_buffer.h" |
| 56 | 56 |
| 57 // Amount of time to wait if we've exhausted the data source. This is to avoid | 57 // Amount of time to wait if we've exhausted the data source. This is to avoid |
| 58 // busy looping. | 58 // busy looping. |
| 59 static const uint32 kNoDataSleepMilliseconds = 10; | 59 static const uint32 kNoDataSleepMilliseconds = 10; |
| 60 | 60 |
| 61 // Mininum interval between OnMoreData() calls. | |
| 62 const uint32 kMinIntervalBetweenOnMoreDataCallsInMs = 5; | |
| 63 | |
| 61 // According to the linux nanosleep manpage, nanosleep on linux can miss the | 64 // According to the linux nanosleep manpage, nanosleep on linux can miss the |
| 62 // deadline by up to 10ms because the kernel timeslice is 10ms. Give a 2x | 65 // deadline by up to 10ms because the kernel timeslice is 10ms. Give a 2x |
| 63 // buffer to compensate for the timeslice, and any additional slowdowns. | 66 // buffer to compensate for the timeslice, and any additional slowdowns. |
| 64 static const uint32 kSleepErrorMilliseconds = 20; | 67 static const uint32 kSleepErrorMilliseconds = 20; |
| 65 | 68 |
| 66 // Set to 0 during debugging if you want error messages due to underrun | 69 // Set to 0 during debugging if you want error messages due to underrun |
| 67 // events or other recoverable errors. | 70 // events or other recoverable errors. |
| 68 #if defined(NDEBUG) | 71 #if defined(NDEBUG) |
| 69 static const int kPcmRecoverIsSilent = 1; | 72 static const int kPcmRecoverIsSilent = 1; |
| 70 #else | 73 #else |
| (...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 340 buffer_.reset(new media::SeekableBuffer(0, output_packet_size)); | 343 buffer_.reset(new media::SeekableBuffer(0, output_packet_size)); |
| 341 | 344 |
| 342 // Get alsa buffer size. | 345 // Get alsa buffer size. |
| 343 snd_pcm_uframes_t buffer_size; | 346 snd_pcm_uframes_t buffer_size; |
| 344 snd_pcm_uframes_t period_size; | 347 snd_pcm_uframes_t period_size; |
| 345 int error = wrapper_->PcmGetParams(playback_handle_, &buffer_size, | 348 int error = wrapper_->PcmGetParams(playback_handle_, &buffer_size, |
| 346 &period_size); | 349 &period_size); |
| 347 if (error < 0) { | 350 if (error < 0) { |
| 348 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " | 351 LOG(ERROR) << "Failed to get playback buffer size from ALSA: " |
| 349 << wrapper_->StrError(error); | 352 << wrapper_->StrError(error); |
| 350 alsa_buffer_frames_ = frames_per_packet_; | 353 // Buffer size is at least twice of packet size. |
| 354 alsa_buffer_frames_ = frames_per_packet_ * 2; | |
|
Raymond Toy (Google)
2011/11/11 22:01:25
Why is this twice now when it used to be the same?
| |
| 351 } else { | 355 } else { |
| 352 alsa_buffer_frames_ = buffer_size; | 356 alsa_buffer_frames_ = buffer_size; |
| 353 } | 357 } |
| 354 } | 358 } |
| 355 } | 359 } |
| 356 | 360 |
| 357 void AlsaPcmOutputStream::StartTask() { | 361 void AlsaPcmOutputStream::StartTask() { |
| 358 DCHECK_EQ(message_loop_, MessageLoop::current()); | 362 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 359 | 363 |
| 360 if (stop_stream_) { | 364 if (stop_stream_) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 | 424 |
| 421 // If stopped, simulate a 0-lengthed packet. | 425 // If stopped, simulate a 0-lengthed packet. |
| 422 if (stop_stream_) { | 426 if (stop_stream_) { |
| 423 buffer_->Clear(); | 427 buffer_->Clear(); |
| 424 *source_exhausted = true; | 428 *source_exhausted = true; |
| 425 return; | 429 return; |
| 426 } | 430 } |
| 427 | 431 |
| 428 *source_exhausted = false; | 432 *source_exhausted = false; |
| 429 | 433 |
| 430 // Request more data if we have capacity. | 434 // Request more data only when we run out of data in the buffer, because |
| 431 if (buffer_->forward_capacity() > buffer_->forward_bytes()) { | 435 // WritePacket() comsumes only the current chunk of data. |
| 436 if (!buffer_->forward_bytes()) { | |
| 432 // Before making a request to source for data we need to determine the | 437 // Before making a request to source for data we need to determine the |
| 433 // delay (in bytes) for the requested data to be played. | 438 // delay (in bytes) for the requested data to be played. |
| 434 | 439 |
| 435 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / | 440 uint32 buffer_delay = buffer_->forward_bytes() * bytes_per_frame_ / |
| 436 bytes_per_output_frame_; | 441 bytes_per_output_frame_; |
| 437 | 442 |
| 438 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; | 443 uint32 hardware_delay = GetCurrentDelay() * bytes_per_frame_; |
| 439 | 444 |
| 440 scoped_refptr<media::DataBuffer> packet = | 445 scoped_refptr<media::DataBuffer> packet = |
| 441 new media::DataBuffer(packet_size_); | 446 new media::DataBuffer(packet_size_); |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 590 ScheduleNextWrite(source_exhausted); | 595 ScheduleNextWrite(source_exhausted); |
| 591 } | 596 } |
| 592 | 597 |
| 593 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { | 598 void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) { |
| 594 DCHECK_EQ(message_loop_, MessageLoop::current()); | 599 DCHECK_EQ(message_loop_, MessageLoop::current()); |
| 595 | 600 |
| 596 if (stop_stream_) { | 601 if (stop_stream_) { |
| 597 return; | 602 return; |
| 598 } | 603 } |
| 599 | 604 |
| 600 // Next write is scheduled for the moment when half of the buffer is | |
| 601 // available. | |
| 602 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; | 605 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; |
| 603 uint32 available_frames = GetAvailableFrames(); | 606 uint32 available_frames = GetAvailableFrames(); |
| 604 uint32 next_fill_time_ms = 0; | 607 uint32 frames_in_buffer = buffer_->forward_bytes() / bytes_per_output_frame_; |
| 605 | 608 |
| 606 // It's possible to have more frames available than what we want, in which | 609 // Next write is initially scheduled for the moment when half of a packet |
| 607 // case we'll leave our |next_fill_time_ms| at 0ms. | 610 // has been played out. |
| 608 if (available_frames < frames_avail_wanted) { | 611 uint32 next_fill_time_ms = |
| 609 uint32 frames_until_empty_enough = frames_avail_wanted - available_frames; | 612 FramesToMillis(frames_per_packet_ / 2, sample_rate_); |
| 610 next_fill_time_ms = | |
| 611 FramesToMillis(frames_until_empty_enough, sample_rate_); | |
| 612 } | |
| 613 | 613 |
| 614 // Adjust for timer resolution issues. | 614 if (frames_in_buffer && (frames_in_buffer <= available_frames)) { |
| 615 if (next_fill_time_ms < kSleepErrorMilliseconds) { | 615 // There is data in the current buffer, consume them immediately if we have |
| 616 // enough space in the soundcard. | |
| 616 next_fill_time_ms = 0; | 617 next_fill_time_ms = 0; |
| 617 } else { | 618 } else { |
| 618 next_fill_time_ms -= kSleepErrorMilliseconds; | 619 // Otherwise schedule the next write for the moment when half of the alsa |
| 620 // buffer becomes available. | |
| 621 if (available_frames < frames_avail_wanted) { | |
| 622 uint32 frames_until_empty_enough = frames_avail_wanted - available_frames; | |
| 623 next_fill_time_ms = | |
| 624 FramesToMillis(frames_until_empty_enough, sample_rate_); | |
| 625 | |
| 626 // Adjust for time resolution. | |
| 627 if (next_fill_time_ms > kNoDataSleepMilliseconds) | |
| 628 next_fill_time_ms -= kNoDataSleepMilliseconds; | |
| 629 | |
| 630 // Avoid back-to-back writing. | |
| 631 if (next_fill_time_ms < kMinIntervalBetweenOnMoreDataCallsInMs) | |
| 632 next_fill_time_ms = kMinIntervalBetweenOnMoreDataCallsInMs; | |
| 633 } else if ((available_frames == alsa_buffer_frames_)) { | |
| 634 // Buffer is empty, invoke next write immediately. | |
| 635 next_fill_time_ms = 0; | |
| 636 } | |
| 619 } | 637 } |
| 620 | 638 |
| 621 // Avoid busy looping if the data source is exhausted. | 639 // Avoid busy looping if the data source is exhausted. |
| 622 if (source_exhausted) { | 640 if (source_exhausted) { |
| 623 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); | 641 next_fill_time_ms = std::max(next_fill_time_ms, kNoDataSleepMilliseconds); |
| 624 } | 642 } |
| 625 | 643 |
| 626 // Only schedule more reads/writes if we are still in the playing state. | 644 // Only schedule more reads/writes if we are still in the playing state. |
| 627 if (state() == kIsPlaying) { | 645 if (state() == kIsPlaying) { |
| 628 if (next_fill_time_ms == 0) { | 646 if (next_fill_time_ms == 0) { |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 870 source_callback_->OnError(this, code); | 888 source_callback_->OnError(this, code); |
| 871 } | 889 } |
| 872 } | 890 } |
| 873 | 891 |
| 874 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 892 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 875 // release ownership of the currently registered callback. | 893 // release ownership of the currently registered callback. |
| 876 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 894 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
| 877 DCHECK_EQ(MessageLoop::current(), message_loop_); | 895 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 878 source_callback_ = callback; | 896 source_callback_ = callback; |
| 879 } | 897 } |
| OLD | NEW |