OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 and should only be used | 7 // AlsaPcmOutputStream object is *not* thread-safe and should only be used |
8 // from the audio thread. We DCHECK on this assumption whenever we can. | 8 // from the audio thread. We DCHECK on this assumption whenever we can. |
9 // | 9 // |
10 // SEMANTICS OF Close() | 10 // SEMANTICS OF Close() |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 // Since we expect to only be able to wake up with a resolution of | 175 // Since we expect to only be able to wake up with a resolution of |
176 // kSleepErrorMilliseconds, double that for our minimum required latency. | 176 // kSleepErrorMilliseconds, double that for our minimum required latency. |
177 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = | 177 const uint32 AlsaPcmOutputStream::kMinLatencyMicros = |
178 kSleepErrorMilliseconds * 2 * 1000; | 178 kSleepErrorMilliseconds * 2 * 1000; |
179 | 179 |
180 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 180 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
181 const AudioParameters& params, | 181 const AudioParameters& params, |
182 AlsaWrapper* wrapper, | 182 AlsaWrapper* wrapper, |
183 AudioManagerLinux* manager) | 183 AudioManagerLinux* manager) |
184 : requested_device_name_(device_name), | 184 : requested_device_name_(device_name), |
185 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), | 185 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())), |
186 channels_(params.channels), | 186 channels_(params.channels()), |
187 sample_rate_(params.sample_rate), | 187 samples_per_second_(params.samples_per_second()), |
188 bytes_per_sample_(params.bits_per_sample / 8), | 188 bytes_per_sample_(params.bits_per_sample() / 8), |
189 bytes_per_frame_(channels_ * params.bits_per_sample / 8), | 189 bytes_per_frame_(channels_ * params.bits_per_sample() / 8), |
190 should_downmix_(false), | 190 should_downmix_(false), |
191 packet_size_(params.GetPacketSize()), | 191 packet_size_(params.GetPacketSize()), |
192 micros_per_packet_(FramesToMicros( | 192 micros_per_packet_(FramesToMicros( |
193 params.samples_per_packet, sample_rate_)), | 193 params.samples_per_packet(), samples_per_second_)), |
194 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, | 194 latency_micros_(std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
195 micros_per_packet_ * 2)), | 195 micros_per_packet_ * 2)), |
196 bytes_per_output_frame_(bytes_per_frame_), | 196 bytes_per_output_frame_(bytes_per_frame_), |
197 alsa_buffer_frames_(0), | 197 alsa_buffer_frames_(0), |
198 stop_stream_(false), | 198 stop_stream_(false), |
199 wrapper_(wrapper), | 199 wrapper_(wrapper), |
200 manager_(manager), | 200 manager_(manager), |
201 playback_handle_(NULL), | 201 playback_handle_(NULL), |
202 frames_per_packet_(packet_size_ / bytes_per_frame_), | 202 frames_per_packet_(packet_size_ / bytes_per_frame_), |
203 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 203 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
204 state_(kCreated), | 204 state_(kCreated), |
205 volume_(1.0f), | 205 volume_(1.0f), |
206 source_callback_(NULL) { | 206 source_callback_(NULL) { |
207 DCHECK(IsOnAudioThread()); | 207 DCHECK(IsOnAudioThread()); |
208 | 208 |
209 // Sanity check input values. | 209 // Sanity check input values. |
210 if ((params.sample_rate > kAlsaMaxSampleRate) || (params.sample_rate <= 0)) { | 210 if (params.samples_per_second() > kAlsaMaxSampleRate || |
| 211 params.samples_per_second() <= 0) { |
211 LOG(WARNING) << "Unsupported audio frequency."; | 212 LOG(WARNING) << "Unsupported audio frequency."; |
212 TransitionTo(kInError); | 213 TransitionTo(kInError); |
213 } | 214 } |
214 | 215 |
215 if (AudioParameters::AUDIO_PCM_LINEAR != params.format && | 216 if (AudioParameters::AUDIO_PCM_LINEAR != params.format() && |
216 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format) { | 217 AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format()) { |
217 LOG(WARNING) << "Unsupported audio format"; | 218 LOG(WARNING) << "Unsupported audio format"; |
218 TransitionTo(kInError); | 219 TransitionTo(kInError); |
219 } | 220 } |
220 | 221 |
221 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 222 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
222 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample; | 223 LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample(); |
223 TransitionTo(kInError); | 224 TransitionTo(kInError); |
224 } | 225 } |
225 } | 226 } |
226 | 227 |
227 AlsaPcmOutputStream::~AlsaPcmOutputStream() { | 228 AlsaPcmOutputStream::~AlsaPcmOutputStream() { |
228 InternalState current_state = state(); | 229 InternalState current_state = state(); |
229 DCHECK(current_state == kCreated || | 230 DCHECK(current_state == kCreated || |
230 current_state == kIsClosed || | 231 current_state == kIsClosed || |
231 current_state == kInError); | 232 current_state == kInError); |
232 DCHECK(!playback_handle_); | 233 DCHECK(!playback_handle_); |
(...skipping 16 matching lines...) Expand all Loading... |
249 // transition out from under us. | 250 // transition out from under us. |
250 TransitionTo(kIsOpened); | 251 TransitionTo(kIsOpened); |
251 | 252 |
252 // Try to open the device. | 253 // Try to open the device. |
253 if (requested_device_name_ == kAutoSelectDevice) { | 254 if (requested_device_name_ == kAutoSelectDevice) { |
254 playback_handle_ = AutoSelectDevice(latency_micros_); | 255 playback_handle_ = AutoSelectDevice(latency_micros_); |
255 if (playback_handle_) | 256 if (playback_handle_) |
256 DVLOG(1) << "Auto-selected device: " << device_name_; | 257 DVLOG(1) << "Auto-selected device: " << device_name_; |
257 } else { | 258 } else { |
258 device_name_ = requested_device_name_; | 259 device_name_ = requested_device_name_; |
259 playback_handle_ = alsa_util::OpenPlaybackDevice(wrapper_, | 260 playback_handle_ = alsa_util::OpenPlaybackDevice( |
260 device_name_.c_str(), | 261 wrapper_, device_name_.c_str(), channels_, samples_per_second_, |
261 channels_, sample_rate_, | 262 pcm_format_, latency_micros_); |
262 pcm_format_, | |
263 latency_micros_); | |
264 } | 263 } |
265 | 264 |
266 // Finish initializing the stream if the device was opened successfully. | 265 // Finish initializing the stream if the device was opened successfully. |
267 if (playback_handle_ == NULL) { | 266 if (playback_handle_ == NULL) { |
268 stop_stream_ = true; | 267 stop_stream_ = true; |
269 TransitionTo(kInError); | 268 TransitionTo(kInError); |
270 return false; | 269 return false; |
271 } else { | 270 } else { |
272 bytes_per_output_frame_ = should_downmix_ ? 2 * bytes_per_sample_ : | 271 bytes_per_output_frame_ = should_downmix_ ? 2 * bytes_per_sample_ : |
273 bytes_per_frame_; | 272 bytes_per_frame_; |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 if (stop_stream_) | 557 if (stop_stream_) |
559 return; | 558 return; |
560 | 559 |
561 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; | 560 uint32 frames_avail_wanted = alsa_buffer_frames_ / 2; |
562 uint32 available_frames = GetAvailableFrames(); | 561 uint32 available_frames = GetAvailableFrames(); |
563 uint32 frames_in_buffer = buffer_->forward_bytes() / bytes_per_output_frame_; | 562 uint32 frames_in_buffer = buffer_->forward_bytes() / bytes_per_output_frame_; |
564 | 563 |
565 // Next write is initially scheduled for the moment when half of a packet | 564 // Next write is initially scheduled for the moment when half of a packet |
566 // has been played out. | 565 // has been played out. |
567 uint32 next_fill_time_ms = | 566 uint32 next_fill_time_ms = |
568 FramesToMillis(frames_per_packet_ / 2, sample_rate_); | 567 FramesToMillis(frames_per_packet_ / 2, samples_per_second_); |
569 | 568 |
570 if (frames_in_buffer && (frames_in_buffer <= available_frames)) { | 569 if (frames_in_buffer && (frames_in_buffer <= available_frames)) { |
571 // There is data in the current buffer, consume them immediately if we have | 570 // There is data in the current buffer, consume them immediately if we have |
572 // enough space in the soundcard. | 571 // enough space in the soundcard. |
573 next_fill_time_ms = 0; | 572 next_fill_time_ms = 0; |
574 } else { | 573 } else { |
575 // Otherwise schedule the next write for the moment when half of the alsa | 574 // Otherwise schedule the next write for the moment when half of the alsa |
576 // buffer becomes available. | 575 // buffer becomes available. |
577 if (available_frames < frames_avail_wanted) { | 576 if (available_frames < frames_avail_wanted) { |
578 uint32 frames_until_empty_enough = frames_avail_wanted - available_frames; | 577 uint32 frames_until_empty_enough = frames_avail_wanted - available_frames; |
579 next_fill_time_ms = | 578 next_fill_time_ms = |
580 FramesToMillis(frames_until_empty_enough, sample_rate_); | 579 FramesToMillis(frames_until_empty_enough, samples_per_second_); |
581 | 580 |
582 // Adjust for time resolution. | 581 // Adjust for time resolution. |
583 if (next_fill_time_ms > kNoDataSleepMilliseconds) | 582 if (next_fill_time_ms > kNoDataSleepMilliseconds) |
584 next_fill_time_ms -= kNoDataSleepMilliseconds; | 583 next_fill_time_ms -= kNoDataSleepMilliseconds; |
585 | 584 |
586 // Avoid back-to-back writing. | 585 // Avoid back-to-back writing. |
587 if (next_fill_time_ms < kMinIntervalBetweenOnMoreDataCallsInMs) | 586 if (next_fill_time_ms < kMinIntervalBetweenOnMoreDataCallsInMs) |
588 next_fill_time_ms = kMinIntervalBetweenOnMoreDataCallsInMs; | 587 next_fill_time_ms = kMinIntervalBetweenOnMoreDataCallsInMs; |
589 } else if (available_frames == alsa_buffer_frames_) { | 588 } else if (available_frames == alsa_buffer_frames_) { |
590 // Buffer is empty, invoke next write immediately. | 589 // Buffer is empty, invoke next write immediately. |
(...skipping 15 matching lines...) Expand all Loading... |
606 // base/metrics/histogram.h. | 605 // base/metrics/histogram.h. |
607 manager_->GetMessageLoop()->PostDelayedTask( | 606 manager_->GetMessageLoop()->PostDelayedTask( |
608 FROM_HERE, | 607 FROM_HERE, |
609 base::Bind(&AlsaPcmOutputStream::WriteTask, | 608 base::Bind(&AlsaPcmOutputStream::WriteTask, |
610 weak_factory_.GetWeakPtr()), | 609 weak_factory_.GetWeakPtr()), |
611 base::TimeDelta::FromMilliseconds(next_fill_time_ms)); | 610 base::TimeDelta::FromMilliseconds(next_fill_time_ms)); |
612 } | 611 } |
613 } | 612 } |
614 } | 613 } |
615 | 614 |
616 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, uint32 sample_rate) { | 615 uint32 AlsaPcmOutputStream::FramesToMicros(uint32 frames, |
617 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 616 uint32 samples_per_second) { |
| 617 return frames * base::Time::kMicrosecondsPerSecond / samples_per_second; |
618 } | 618 } |
619 | 619 |
620 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, uint32 sample_rate) { | 620 uint32 AlsaPcmOutputStream::FramesToMillis(uint32 frames, |
621 return frames * base::Time::kMillisecondsPerSecond / sample_rate; | 621 uint32 samples_per_second) { |
| 622 return frames * base::Time::kMillisecondsPerSecond / samples_per_second; |
622 } | 623 } |
623 | 624 |
624 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) { | 625 std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) { |
625 // Constants specified by the ALSA API for device hints. | 626 // Constants specified by the ALSA API for device hints. |
626 static const int kGetAllDevices = -1; | 627 static const int kGetAllDevices = -1; |
627 static const char kPcmInterfaceName[] = "pcm"; | 628 static const char kPcmInterfaceName[] = "pcm"; |
628 static const char kIoHintName[] = "IOID"; | 629 static const char kIoHintName[] = "IOID"; |
629 static const char kNameHintName[] = "NAME"; | 630 static const char kNameHintName[] = "NAME"; |
630 | 631 |
631 const char* wanted_device = GuessSpecificDeviceName(channels); | 632 const char* wanted_device = GuessSpecificDeviceName(channels); |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
726 // remap do some software conversion to make it work. | 727 // remap do some software conversion to make it work. |
727 // 3) Fallback to kDefaultDevice. | 728 // 3) Fallback to kDefaultDevice. |
728 // 4) If that fails too, try the "plug:" version of kDefaultDevice. | 729 // 4) If that fails too, try the "plug:" version of kDefaultDevice. |
729 // 5) Give up. | 730 // 5) Give up. |
730 snd_pcm_t* handle = NULL; | 731 snd_pcm_t* handle = NULL; |
731 device_name_ = FindDeviceForChannels(channels_); | 732 device_name_ = FindDeviceForChannels(channels_); |
732 | 733 |
733 // Step 1. | 734 // Step 1. |
734 if (!device_name_.empty()) { | 735 if (!device_name_.empty()) { |
735 if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), | 736 if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), |
736 channels_, sample_rate_, | 737 channels_, samples_per_second_, |
737 pcm_format_, | 738 pcm_format_, |
738 latency)) != NULL) { | 739 latency)) != NULL) { |
739 return handle; | 740 return handle; |
740 } | 741 } |
741 | 742 |
742 // Step 2. | 743 // Step 2. |
743 device_name_ = kPlugPrefix + device_name_; | 744 device_name_ = kPlugPrefix + device_name_; |
744 if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), | 745 if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), |
745 channels_, sample_rate_, | 746 channels_, samples_per_second_, |
746 pcm_format_, | 747 pcm_format_, |
747 latency)) != NULL) { | 748 latency)) != NULL) { |
748 return handle; | 749 return handle; |
749 } | 750 } |
750 } | 751 } |
751 | 752 |
752 // For the kDefaultDevice device, we can only reliably depend on 2-channel | 753 // For the kDefaultDevice device, we can only reliably depend on 2-channel |
753 // output to have the correct ordering according to Lennart. For the channel | 754 // output to have the correct ordering according to Lennart. For the channel |
754 // formats that we know how to downmix from (3 channel to 8 channel), setup | 755 // formats that we know how to downmix from (3 channel to 8 channel), setup |
755 // downmixing. | 756 // downmixing. |
756 // | 757 // |
757 // TODO(ajwong): We need a SupportsFolding() function. | 758 // TODO(ajwong): We need a SupportsFolding() function. |
758 uint32 default_channels = channels_; | 759 uint32 default_channels = channels_; |
759 if (default_channels > 2 && default_channels <= 8) { | 760 if (default_channels > 2 && default_channels <= 8) { |
760 should_downmix_ = true; | 761 should_downmix_ = true; |
761 default_channels = 2; | 762 default_channels = 2; |
762 } | 763 } |
763 | 764 |
764 // Step 3. | 765 // Step 3. |
765 device_name_ = kDefaultDevice; | 766 device_name_ = kDefaultDevice; |
766 if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), | 767 if ((handle = alsa_util::OpenPlaybackDevice( |
767 default_channels, sample_rate_, | 768 wrapper_, device_name_.c_str(), default_channels, samples_per_second_, |
768 pcm_format_, latency)) != NULL) { | 769 pcm_format_, latency)) != NULL) { |
769 return handle; | 770 return handle; |
770 } | 771 } |
771 | 772 |
772 // Step 4. | 773 // Step 4. |
773 device_name_ = kPlugPrefix + device_name_; | 774 device_name_ = kPlugPrefix + device_name_; |
774 if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), | 775 if ((handle = alsa_util::OpenPlaybackDevice( |
775 default_channels, sample_rate_, | 776 wrapper_, device_name_.c_str(), default_channels, samples_per_second_, |
776 pcm_format_, latency)) != NULL) { | 777 pcm_format_, latency)) != NULL) { |
777 return handle; | 778 return handle; |
778 } | 779 } |
779 | 780 |
780 // Unable to open any device. | 781 // Unable to open any device. |
781 device_name_.clear(); | 782 device_name_.clear(); |
782 return NULL; | 783 return NULL; |
783 } | 784 } |
784 | 785 |
785 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) { | 786 bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) { |
786 switch (state_) { | 787 switch (state_) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
845 if (source_callback_) | 846 if (source_callback_) |
846 source_callback_->OnError(this, code); | 847 source_callback_->OnError(this, code); |
847 } | 848 } |
848 | 849 |
849 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 850 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
850 // release ownership of the currently registered callback. | 851 // release ownership of the currently registered callback. |
851 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { | 852 void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) { |
852 DCHECK(IsOnAudioThread()); | 853 DCHECK(IsOnAudioThread()); |
853 source_callback_ = callback; | 854 source_callback_ = callback; |
854 } | 855 } |
OLD | NEW |