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; |
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 |