OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" | 5 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <limits> | 9 #include <limits> |
10 #include <utility> | 10 #include <utility> |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
57 #define POST_TASK_TO_MIXER_THREAD(task, ...) \ | 57 #define POST_TASK_TO_MIXER_THREAD(task, ...) \ |
58 mixer_task_runner_->PostTask( \ | 58 mixer_task_runner_->PostTask( \ |
59 FROM_HERE, base::Bind(task, base::Unretained(this), ##__VA_ARGS__)); | 59 FROM_HERE, base::Bind(task, base::Unretained(this), ##__VA_ARGS__)); |
60 | 60 |
61 namespace chromecast { | 61 namespace chromecast { |
62 namespace media { | 62 namespace media { |
63 | 63 |
64 namespace { | 64 namespace { |
65 | 65 |
66 const char kOutputDeviceDefaultName[] = "default"; | 66 const char kOutputDeviceDefaultName[] = "default"; |
67 const int kDefaultNumOutputChannels = 2; | 67 const int kNumOutputChannels = 2; |
68 | 68 |
69 const int kDefaultOutputBufferSizeFrames = 4096; | 69 const int kDefaultOutputBufferSizeFrames = 4096; |
70 const bool kPcmRecoverIsSilent = false; | 70 const bool kPcmRecoverIsSilent = false; |
71 // The number of frames of silence to write (to prevent underrun) when no inputs | 71 // The number of frames of silence to write (to prevent underrun) when no inputs |
72 // are present. | 72 // are present. |
73 const int kPreventUnderrunChunkSize = 512; | 73 const int kPreventUnderrunChunkSize = 512; |
74 const int kDefaultCheckCloseTimeoutMs = 2000; | 74 const int kDefaultCheckCloseTimeoutMs = 2000; |
75 | 75 |
76 // A list of supported sample rates. | 76 // A list of supported sample rates. |
77 // TODO(jyw): move this up into chromecast/public for 1) documentation and | 77 // TODO(jyw): move this up into chromecast/public for 1) documentation and |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 | 198 |
199 alsa_device_name_ = kOutputDeviceDefaultName; | 199 alsa_device_name_ = kOutputDeviceDefaultName; |
200 if (base::CommandLine::InitializedForCurrentProcess() && | 200 if (base::CommandLine::InitializedForCurrentProcess() && |
201 base::CommandLine::ForCurrentProcess()->HasSwitch( | 201 base::CommandLine::ForCurrentProcess()->HasSwitch( |
202 switches::kAlsaOutputDevice)) { | 202 switches::kAlsaOutputDevice)) { |
203 alsa_device_name_ = | 203 alsa_device_name_ = |
204 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 204 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
205 switches::kAlsaOutputDevice); | 205 switches::kAlsaOutputDevice); |
206 } | 206 } |
207 | 207 |
208 GetSwitchValueAsNonNegativeInt(switches::kAlsaNumOutputChannels, | |
209 kDefaultNumOutputChannels, | |
210 &num_output_channels_); | |
211 | |
212 int fixed_samples_per_second; | 208 int fixed_samples_per_second; |
213 GetSwitchValueAsNonNegativeInt(switches::kAlsaFixedOutputSampleRate, | 209 GetSwitchValueAsNonNegativeInt(switches::kAlsaFixedOutputSampleRate, |
214 kInvalidSampleRate, &fixed_samples_per_second); | 210 kInvalidSampleRate, &fixed_samples_per_second); |
215 if (fixed_samples_per_second != kInvalidSampleRate) | 211 if (fixed_samples_per_second != kInvalidSampleRate) |
216 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; | 212 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; |
217 | |
218 fixed_output_samples_per_second_ = fixed_samples_per_second; | 213 fixed_output_samples_per_second_ = fixed_samples_per_second; |
219 | 214 |
220 DefineAlsaParameters(); | 215 DefineAlsaParameters(); |
221 } | 216 } |
222 | 217 |
223 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 218 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
224 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 219 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
225 } | 220 } |
226 | 221 |
227 void StreamMixerAlsa::DefineAlsaParameters() { | 222 void StreamMixerAlsa::DefineAlsaParameters() { |
(...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
708 } | 703 } |
709 | 704 |
710 if (active_inputs.empty()) { | 705 if (active_inputs.empty()) { |
711 // No inputs have any data to provide. | 706 // No inputs have any data to provide. |
712 if (!inputs_.empty()) | 707 if (!inputs_.empty()) |
713 return false; // If there are some inputs, don't fill with silence. | 708 return false; // If there are some inputs, don't fill with silence. |
714 | 709 |
715 // If we have no inputs, fill with silence to avoid underrun. | 710 // If we have no inputs, fill with silence to avoid underrun. |
716 chunk_size = kPreventUnderrunChunkSize; | 711 chunk_size = kPreventUnderrunChunkSize; |
717 if (!mixed_ || mixed_->frames() < chunk_size) | 712 if (!mixed_ || mixed_->frames() < chunk_size) |
718 mixed_ = ::media::AudioBus::Create(num_output_channels_, chunk_size); | 713 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); |
719 mixed_->Zero(); | 714 mixed_->Zero(); |
720 WriteMixedPcm(*mixed_, chunk_size); | 715 WriteMixedPcm(*mixed_, chunk_size); |
721 return true; | 716 return true; |
722 } | 717 } |
723 | 718 |
724 // If |mixed_| has not been allocated, or it is too small, allocate a buffer. | 719 // If |mixed_| has not been allocated, or it is too small, allocate a buffer. |
725 if (!mixed_ || mixed_->frames() < chunk_size) | 720 if (!mixed_ || mixed_->frames() < chunk_size) |
726 mixed_ = ::media::AudioBus::Create(num_output_channels_, chunk_size); | 721 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); |
727 // If |temp_| has not been allocated, or is too small, allocate a buffer. | 722 // If |temp_| has not been allocated, or is too small, allocate a buffer. |
728 if (!temp_ || temp_->frames() < chunk_size) | 723 if (!temp_ || temp_->frames() < chunk_size) |
729 temp_ = ::media::AudioBus::Create(num_output_channels_, chunk_size); | 724 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); |
730 | 725 |
731 mixed_->ZeroFramesPartial(0, chunk_size); | 726 mixed_->ZeroFramesPartial(0, chunk_size); |
732 | 727 |
733 // Loop through active inputs, polling them for data, and mixing them. | 728 // Loop through active inputs, polling them for data, and mixing them. |
734 for (InputQueue* input : active_inputs) { | 729 for (InputQueue* input : active_inputs) { |
735 input->GetResampledData(temp_.get(), chunk_size); | 730 input->GetResampledData(temp_.get(), chunk_size); |
736 for (int c = 0; c < num_output_channels_; ++c) { | 731 for (int c = 0; c < kNumOutputChannels; ++c) { |
737 float volume_scalar = input->volume_multiplier(); | 732 float volume_scalar = input->volume_multiplier(); |
738 DCHECK(volume_scalar >= 0.0 && volume_scalar <= 1.0) << volume_scalar; | 733 DCHECK(volume_scalar >= 0.0 && volume_scalar <= 1.0) << volume_scalar; |
739 ::media::vector_math::FMAC(temp_->channel(c), volume_scalar, chunk_size, | 734 ::media::vector_math::FMAC(temp_->channel(c), volume_scalar, chunk_size, |
740 mixed_->channel(c)); | 735 mixed_->channel(c)); |
741 } | 736 } |
742 } | 737 } |
743 | 738 |
744 WriteMixedPcm(*mixed_, chunk_size); | 739 WriteMixedPcm(*mixed_, chunk_size); |
745 return true; | 740 return true; |
746 } | 741 } |
747 | 742 |
748 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { | 743 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { |
749 return alsa_->PcmFormatSize(pcm_format_, 1); | 744 return alsa_->PcmFormatSize(pcm_format_, 1); |
750 } | 745 } |
751 | 746 |
752 void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, | 747 void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, |
753 int frames) { | 748 int frames) { |
754 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 749 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
755 CHECK_PCM_INITIALIZED(); | 750 CHECK_PCM_INITIALIZED(); |
756 | 751 |
757 size_t interleaved_size = static_cast<size_t>(frames * num_output_channels_) * | 752 size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) * |
758 BytesPerOutputFormatSample(); | 753 BytesPerOutputFormatSample(); |
759 if (interleaved_.size() < interleaved_size) | 754 if (interleaved_.size() < interleaved_size) |
760 interleaved_.resize(interleaved_size); | 755 interleaved_.resize(interleaved_size); |
761 | 756 |
762 int64_t expected_playback_time = rendering_delay_.timestamp_microseconds + | 757 int64_t expected_playback_time = rendering_delay_.timestamp_microseconds + |
763 rendering_delay_.delay_microseconds; | 758 rendering_delay_.delay_microseconds; |
764 mixed.ToInterleaved(frames, BytesPerOutputFormatSample(), | 759 mixed.ToInterleaved(frames, BytesPerOutputFormatSample(), |
765 interleaved_.data()); | 760 interleaved_.data()); |
766 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { | 761 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { |
767 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, | 762 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, |
768 output_samples_per_second_, num_output_channels_, | 763 output_samples_per_second_, num_output_channels_, |
769 interleaved_.data(), interleaved_size); | 764 interleaved_.data(), interleaved_size); |
770 } | 765 } |
771 | 766 |
772 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need | 767 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need |
773 // to be prepared in order for playback to work. | 768 // to be prepared in order for playback to work. |
774 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) | 769 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) |
775 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 770 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
776 | 771 |
777 int frames_left = frames; | 772 int frames_left = frames; |
778 uint8_t* data = &interleaved_[0]; | 773 uint8_t* data = &interleaved_[0]; |
779 while (frames_left) { | 774 while (frames_left) { |
780 int frames_or_error; | 775 int frames_or_error; |
781 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { | 776 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { |
782 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, | 777 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, |
783 kPcmRecoverIsSilent); | 778 kPcmRecoverIsSilent); |
784 } | 779 } |
785 frames_left -= frames_or_error; | 780 frames_left -= frames_or_error; |
786 DCHECK_GE(frames_left, 0); | 781 DCHECK_GE(frames_left, 0); |
787 data += | 782 data += frames_or_error * kNumOutputChannels * BytesPerOutputFormatSample(); |
788 frames_or_error * num_output_channels_ * BytesPerOutputFormatSample(); | |
789 } | 783 } |
790 UpdateRenderingDelay(frames); | 784 UpdateRenderingDelay(frames); |
791 for (auto&& input : inputs_) | 785 for (auto&& input : inputs_) |
792 input->AfterWriteFrames(rendering_delay_); | 786 input->AfterWriteFrames(rendering_delay_); |
793 } | 787 } |
794 | 788 |
795 void StreamMixerAlsa::UpdateRenderingDelay(int newly_pushed_frames) { | 789 void StreamMixerAlsa::UpdateRenderingDelay(int newly_pushed_frames) { |
796 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 790 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
797 CHECK_PCM_INITIALIZED(); | 791 CHECK_PCM_INITIALIZED(); |
798 | 792 |
(...skipping 30 matching lines...) Expand all Loading... |
829 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 823 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
830 observer) != loopback_observers_.end()); | 824 observer) != loopback_observers_.end()); |
831 loopback_observers_.erase(std::remove(loopback_observers_.begin(), | 825 loopback_observers_.erase(std::remove(loopback_observers_.begin(), |
832 loopback_observers_.end(), observer), | 826 loopback_observers_.end(), observer), |
833 loopback_observers_.end()); | 827 loopback_observers_.end()); |
834 observer->OnRemoved(); | 828 observer->OnRemoved(); |
835 } | 829 } |
836 | 830 |
837 } // namespace media | 831 } // namespace media |
838 } // namespace chromecast | 832 } // namespace chromecast |
OLD | NEW |