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