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 <unordered_set> | |
11 #include <utility> | 10 #include <utility> |
12 | 11 |
13 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
14 #include "base/command_line.h" | 13 #include "base/command_line.h" |
15 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
16 #include "base/memory/ptr_util.h" | |
17 #include "base/memory/weak_ptr.h" | 15 #include "base/memory/weak_ptr.h" |
18 #include "base/numerics/saturated_arithmetic.h" | |
19 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
20 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
21 #include "base/threading/platform_thread.h" | 18 #include "base/threading/platform_thread.h" |
22 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
23 #include "chromecast/base/chromecast_switches.h" | 20 #include "chromecast/base/chromecast_switches.h" |
24 #include "chromecast/media/base/audio_device_ids.h" | |
25 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" | 21 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" |
26 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" | 22 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" |
27 #include "chromecast/media/cma/backend/alsa/filter_group.h" | |
28 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" | 23 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" |
29 #include "media/audio/audio_device_description.h" | |
30 #include "media/base/audio_bus.h" | 24 #include "media/base/audio_bus.h" |
31 #include "media/base/media_switches.h" | 25 #include "media/base/media_switches.h" |
32 | 26 |
33 #define RETURN_REPORT_ERROR(snd_func, ...) \ | 27 #define RETURN_REPORT_ERROR(snd_func, ...) \ |
34 do { \ | 28 do { \ |
35 int err = alsa_->snd_func(__VA_ARGS__); \ | 29 int err = alsa_->snd_func(__VA_ARGS__); \ |
36 if (err < 0) { \ | 30 if (err < 0) { \ |
37 LOG(ERROR) << #snd_func " error: " << alsa_->StrError(err); \ | 31 LOG(ERROR) << #snd_func " error: " << alsa_->StrError(err); \ |
38 SignalError(); \ | 32 SignalError(); \ |
39 return; \ | 33 return; \ |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 // direction they adjusted the requested parameter in, but since we read the | 105 // direction they adjusted the requested parameter in, but since we read the |
112 // output param and then log the information, this module doesn't need to get | 106 // output param and then log the information, this module doesn't need to get |
113 // the direction explicitly. | 107 // the direction explicitly. |
114 static int* kAlsaDirDontCare = nullptr; | 108 static int* kAlsaDirDontCare = nullptr; |
115 | 109 |
116 // These sample formats will be tried in order. 32 bit samples is ideal, but | 110 // These sample formats will be tried in order. 32 bit samples is ideal, but |
117 // some devices do not support 32 bit samples. | 111 // some devices do not support 32 bit samples. |
118 const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32, | 112 const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32, |
119 SND_PCM_FORMAT_S16}; | 113 SND_PCM_FORMAT_S16}; |
120 | 114 |
| 115 // How many seconds of silence should be passed to the filters to flush them. |
| 116 const float kSilenceSecondsToFilter = 1.0f; |
| 117 |
121 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); | 118 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); |
122 | 119 |
123 int64_t TimespecToMicroseconds(struct timespec time) { | 120 int64_t TimespecToMicroseconds(struct timespec time) { |
124 return static_cast<int64_t>(time.tv_sec) * | 121 return static_cast<int64_t>(time.tv_sec) * |
125 base::Time::kMicrosecondsPerSecond + | 122 base::Time::kMicrosecondsPerSecond + |
126 time.tv_nsec / 1000; | 123 time.tv_nsec / 1000; |
127 } | 124 } |
128 | 125 |
129 bool GetSwitchValueAsInt(const std::string& switch_name, | 126 bool GetSwitchValueAsInt(const std::string& switch_name, |
130 int default_value, | 127 int default_value, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 } | 160 } |
164 | 161 |
165 if (*value < 0) { | 162 if (*value < 0) { |
166 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; | 163 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; |
167 *value = default_value; | 164 *value = default_value; |
168 return false; | 165 return false; |
169 } | 166 } |
170 return true; | 167 return true; |
171 } | 168 } |
172 | 169 |
173 void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) { | |
174 for (size_t i = 0; i < size; ++i) { | |
175 dest[i] = base::SaturatedAddition(source[i], dest[i]); | |
176 } | |
177 } | |
178 | |
179 class StreamMixerAlsaInstance : public StreamMixerAlsa { | 170 class StreamMixerAlsaInstance : public StreamMixerAlsa { |
180 public: | 171 public: |
181 StreamMixerAlsaInstance() {} | 172 StreamMixerAlsaInstance() {} |
182 ~StreamMixerAlsaInstance() override {} | 173 ~StreamMixerAlsaInstance() override {} |
183 | 174 |
184 private: | 175 private: |
185 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); | 176 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); |
186 }; | 177 }; |
187 | 178 |
188 base::LazyInstance<StreamMixerAlsaInstance> g_mixer_instance = | 179 base::LazyInstance<StreamMixerAlsaInstance> g_mixer_instance = |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; | 238 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; |
248 } | 239 } |
249 | 240 |
250 fixed_output_samples_per_second_ = fixed_samples_per_second; | 241 fixed_output_samples_per_second_ = fixed_samples_per_second; |
251 | 242 |
252 low_sample_rate_cutoff_ = | 243 low_sample_rate_cutoff_ = |
253 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) | 244 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) |
254 ? kLowSampleRateCutoff | 245 ? kLowSampleRateCutoff |
255 : 0; | 246 : 0; |
256 | 247 |
257 // Create filter groups. | 248 // Create filters |
258 // TODO(bshaya): Switch to filter groups based on AudioContentType | 249 pre_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( |
259 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 250 AudioFilterFactory::PRE_LOOPBACK_FILTER); |
260 std::unordered_set<std::string>( | 251 post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( |
261 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), | 252 AudioFilterFactory::POST_LOOPBACK_FILTER); |
262 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER)); | |
263 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
264 std::unordered_set<std::string>({kAlarmAudioDeviceId}), | |
265 AudioFilterFactory::ALARM_AUDIO_FILTER)); | |
266 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
267 std::unordered_set<std::string>({kTtsAudioDeviceId}), | |
268 AudioFilterFactory::TTS_AUDIO_FILTER)); | |
269 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
270 std::unordered_set<std::string>( | |
271 {::media::AudioDeviceDescription::kDefaultDeviceId, | |
272 kEarconAudioDeviceId}), | |
273 AudioFilterFactory::MEDIA_AUDIO_FILTER)); | |
274 | 253 |
275 DefineAlsaParameters(); | 254 DefineAlsaParameters(); |
276 } | 255 } |
277 | 256 |
278 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 257 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
279 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 258 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
280 } | 259 } |
281 | 260 |
282 void StreamMixerAlsa::DefineAlsaParameters() { | 261 void StreamMixerAlsa::DefineAlsaParameters() { |
283 // Get the ALSA output configuration from the command line. | 262 // Get the ALSA output configuration from the command line. |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
543 int err = SetAlsaPlaybackParams(); | 522 int err = SetAlsaPlaybackParams(); |
544 if (err < 0) { | 523 if (err < 0) { |
545 LOG(ERROR) << "Error setting ALSA playback parameters: " | 524 LOG(ERROR) << "Error setting ALSA playback parameters: " |
546 << alsa_->StrError(err); | 525 << alsa_->StrError(err); |
547 SignalError(); | 526 SignalError(); |
548 return; | 527 return; |
549 } | 528 } |
550 } | 529 } |
551 | 530 |
552 // Initialize filters | 531 // Initialize filters |
553 for (auto&& filter_group : filter_groups_) { | 532 if (pre_loopback_filter_) { |
554 filter_group->Initialize(output_samples_per_second_, | 533 pre_loopback_filter_->SetSampleRateAndFormat( |
555 ::media::SampleFormat::kSampleFormatS32); | 534 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); |
| 535 } |
| 536 |
| 537 if (post_loopback_filter_) { |
| 538 post_loopback_filter_->SetSampleRateAndFormat( |
| 539 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); |
556 } | 540 } |
557 | 541 |
558 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 542 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
559 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); | 543 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); |
560 | 544 |
561 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 545 rendering_delay_.timestamp_microseconds = kNoTimestamp; |
562 rendering_delay_.delay_microseconds = 0; | 546 rendering_delay_.delay_microseconds = 0; |
563 | 547 |
564 state_ = kStateNormalPlayback; | 548 state_ = kStateNormalPlayback; |
565 } | 549 } |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 } | 628 } |
645 | 629 |
646 void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) { | 630 void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) { |
647 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddInput, | 631 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddInput, |
648 base::Passed(std::move(input))); | 632 base::Passed(std::move(input))); |
649 if (!alsa_) { | 633 if (!alsa_) { |
650 alsa_.reset(new AlsaWrapper()); | 634 alsa_.reset(new AlsaWrapper()); |
651 } | 635 } |
652 | 636 |
653 DCHECK(input); | 637 DCHECK(input); |
654 | |
655 // If the new input is a primary one, we may need to change the output | 638 // If the new input is a primary one, we may need to change the output |
656 // sample rate to match its input sample rate. | 639 // sample rate to match its input sample rate. |
657 // We only change the output rate if it is not set to a fixed value. | 640 // We only change the output rate if it is not set to a fixed value. |
658 if (input->primary() && | 641 if (input->primary() && |
659 fixed_output_samples_per_second_ == kInvalidSampleRate) { | 642 fixed_output_samples_per_second_ == kInvalidSampleRate) { |
660 CheckChangeOutputRate(input->input_samples_per_second()); | 643 CheckChangeOutputRate(input->input_samples_per_second()); |
661 } | 644 } |
662 | 645 |
663 check_close_timer_->Stop(); | 646 check_close_timer_->Stop(); |
664 switch (state_) { | 647 if (state_ == kStateUninitialized) { |
665 case kStateUninitialized: | 648 requested_output_samples_per_second_ = input->input_samples_per_second(); |
666 requested_output_samples_per_second_ = input->input_samples_per_second(); | 649 Start(); |
667 Start(); | 650 input->Initialize(rendering_delay_); |
668 // Fallthrough intended | 651 inputs_.push_back(std::move(input)); |
669 case kStateNormalPlayback: { | 652 } else if (state_ == kStateNormalPlayback) { |
670 bool found_filter_group = false; | 653 input->Initialize(rendering_delay_); |
671 input->Initialize(rendering_delay_); | 654 inputs_.push_back(std::move(input)); |
672 for (auto&& filter_group : filter_groups_) { | 655 } else { |
673 if (filter_group->CanProcessInput(input.get())) { | 656 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); |
674 found_filter_group = true; | 657 ignored_inputs_.push_back(std::move(input)); |
675 input->set_filter_group(filter_group.get()); | |
676 break; | |
677 } | |
678 } | |
679 DCHECK(found_filter_group) << "Could not find a filter group for " | |
680 << input->device_id(); | |
681 inputs_.push_back(std::move(input)); | |
682 } break; | |
683 case kStateError: | |
684 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); | |
685 ignored_inputs_.push_back(std::move(input)); | |
686 break; | |
687 default: | |
688 NOTREACHED(); | |
689 } | 658 } |
690 } | 659 } |
691 | 660 |
692 void StreamMixerAlsa::CheckChangeOutputRate(int input_samples_per_second) { | 661 void StreamMixerAlsa::CheckChangeOutputRate(int input_samples_per_second) { |
693 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 662 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
694 if (!pcm_ || | 663 if (!pcm_ || |
695 input_samples_per_second == requested_output_samples_per_second_ || | 664 input_samples_per_second == requested_output_samples_per_second_ || |
696 input_samples_per_second == output_samples_per_second_ || | 665 input_samples_per_second == output_samples_per_second_ || |
697 input_samples_per_second < static_cast<int>(low_sample_rate_cutoff_)) { | 666 input_samples_per_second < static_cast<int>(low_sample_rate_cutoff_)) { |
698 return; | 667 return; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
786 retry_write_frames_timer_->Stop(); | 755 retry_write_frames_timer_->Stop(); |
787 if (TryWriteFrames()) { | 756 if (TryWriteFrames()) { |
788 retry_write_frames_timer_->Start( | 757 retry_write_frames_timer_->Start( |
789 FROM_HERE, base::TimeDelta(), | 758 FROM_HERE, base::TimeDelta(), |
790 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); | 759 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); |
791 } | 760 } |
792 } | 761 } |
793 | 762 |
794 bool StreamMixerAlsa::TryWriteFrames() { | 763 bool StreamMixerAlsa::TryWriteFrames() { |
795 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 764 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
796 DCHECK_GE(filter_groups_.size(), 1u); | |
797 | |
798 if (state_ != kStateNormalPlayback) { | 765 if (state_ != kStateNormalPlayback) { |
799 return false; | 766 return false; |
800 } | 767 } |
801 | 768 |
802 const int min_frames_in_buffer = | 769 const int min_frames_in_buffer = |
803 output_samples_per_second_ * kMinBufferedDataMs / 1000; | 770 output_samples_per_second_ * kMinBufferedDataMs / 1000; |
804 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; | 771 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; |
805 bool is_silence = true; | 772 std::vector<InputQueue*> active_inputs; |
806 for (auto&& filter_group : filter_groups_) { | |
807 filter_group->ClearActiveInputs(); | |
808 } | |
809 for (auto&& input : inputs_) { | 773 for (auto&& input : inputs_) { |
810 int read_size = input->MaxReadSize(); | 774 int read_size = input->MaxReadSize(); |
811 if (read_size > 0) { | 775 if (read_size > 0) { |
812 DCHECK(input->filter_group()); | 776 active_inputs.push_back(input.get()); |
813 input->filter_group()->AddActiveInput(input.get()); | |
814 chunk_size = std::min(chunk_size, read_size); | 777 chunk_size = std::min(chunk_size, read_size); |
815 is_silence = false; | |
816 } else if (input->primary()) { | 778 } else if (input->primary()) { |
817 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { | 779 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { |
818 LOG(ERROR) << "Failed to get status"; | 780 LOG(ERROR) << "Failed to get status"; |
819 return false; | 781 return false; |
820 } | 782 } |
821 | 783 |
822 int frames_in_buffer = | 784 int frames_in_buffer = |
823 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); | 785 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); |
824 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || | 786 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || |
825 frames_in_buffer < min_frames_in_buffer) { | 787 frames_in_buffer < min_frames_in_buffer) { |
826 // If there has been (or soon will be) an underrun, continue without the | 788 // If there has been (or soon will be) an underrun, continue without the |
827 // empty primary input stream. | 789 // empty primary input stream. |
828 input->OnSkipped(); | 790 input->OnSkipped(); |
829 continue; | 791 continue; |
830 } | 792 } |
831 | 793 |
832 // A primary input cannot provide any data, so wait until later. | 794 // A primary input cannot provide any data, so wait until later. |
833 retry_write_frames_timer_->Start( | 795 retry_write_frames_timer_->Start( |
834 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), | 796 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), |
835 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); | 797 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); |
836 return false; | 798 return false; |
837 } else { | 799 } else { |
838 input->OnSkipped(); | 800 input->OnSkipped(); |
839 } | 801 } |
840 } | 802 } |
841 | 803 |
842 if (is_silence) { | 804 if (active_inputs.empty()) { |
843 // No inputs have any data to provide. Push silence to prevent underrun. | 805 // No inputs have any data to provide. Fill with silence to avoid underrun. |
844 chunk_size = kPreventUnderrunChunkSize; | 806 chunk_size = kPreventUnderrunChunkSize; |
| 807 if (!mixed_ || mixed_->frames() < chunk_size) { |
| 808 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); |
| 809 } |
| 810 |
| 811 mixed_->Zero(); |
| 812 WriteMixedPcm(*mixed_, chunk_size, true /* is_silence */); |
| 813 return true; |
845 } | 814 } |
846 | 815 |
847 // Mix and filter each group. | 816 // If |mixed_| has not been allocated, or it is too small, allocate a buffer. |
848 std::vector<uint8_t>* interleaved = nullptr; | 817 if (!mixed_ || mixed_->frames() < chunk_size) { |
849 for (auto&& filter_group : filter_groups_) { | 818 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); |
850 if (filter_group->MixAndFilter(chunk_size)) { | 819 } |
851 if (!interleaved) { | 820 |
852 interleaved = filter_group->GetInterleaved(); | 821 // If |temp_| has not been allocated, or is too small, allocate a buffer. |
853 } else { | 822 if (!temp_ || temp_->frames() < chunk_size) { |
854 DCHECK_EQ(4, BytesPerOutputFormatSample()); | 823 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); |
855 VectorAccumulate( | 824 } |
856 reinterpret_cast<int32_t*>(filter_group->GetInterleaved()->data()), | 825 |
857 chunk_size * kNumOutputChannels, | 826 mixed_->ZeroFramesPartial(0, chunk_size); |
858 reinterpret_cast<int32_t*>(interleaved->data())); | 827 |
859 } | 828 // Loop through active inputs, polling them for data, and mixing them. |
| 829 for (InputQueue* input : active_inputs) { |
| 830 input->GetResampledData(temp_.get(), chunk_size); |
| 831 for (int c = 0; c < kNumOutputChannels; ++c) { |
| 832 input->VolumeScaleAccumulate(c, temp_->channel(c), chunk_size, |
| 833 mixed_->channel(c)); |
860 } | 834 } |
861 } | 835 } |
862 | 836 |
863 if (!interleaved) { | 837 WriteMixedPcm(*mixed_, chunk_size, false /* is_silence */); |
864 // No group has any data, write empty buffer. | |
865 filter_groups_[0]->ClearInterleaved(chunk_size); | |
866 interleaved = filter_groups_[0]->GetInterleaved(); | |
867 } | |
868 | |
869 WriteMixedPcm(interleaved, chunk_size); | |
870 return true; | 838 return true; |
871 } | 839 } |
872 | 840 |
873 size_t StreamMixerAlsa::InterleavedSize(int frames) { | |
874 return BytesPerOutputFormatSample() * | |
875 static_cast<size_t>(frames * kNumOutputChannels); | |
876 } | |
877 | |
878 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { | 841 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { |
879 return alsa_->PcmFormatSize(pcm_format_, 1); | 842 return alsa_->PcmFormatSize(pcm_format_, 1); |
880 } | 843 } |
881 | 844 |
882 void StreamMixerAlsa::WriteMixedPcm(std::vector<uint8_t>* interleaved, | 845 void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, |
883 int frames) { | 846 int frames, bool is_silence) { |
884 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 847 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
885 CHECK_PCM_INITIALIZED(); | 848 CHECK_PCM_INITIALIZED(); |
886 DCHECK(interleaved); | 849 |
887 DCHECK_GE(interleaved->size(), InterleavedSize(frames)); | 850 size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) * |
| 851 BytesPerOutputFormatSample(); |
| 852 if (interleaved_.size() < interleaved_size) { |
| 853 interleaved_.resize(interleaved_size); |
| 854 } |
888 | 855 |
889 int64_t expected_playback_time; | 856 int64_t expected_playback_time; |
890 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { | 857 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { |
891 expected_playback_time = kNoTimestamp; | 858 expected_playback_time = kNoTimestamp; |
892 } else { | 859 } else { |
893 expected_playback_time = rendering_delay_.timestamp_microseconds + | 860 expected_playback_time = rendering_delay_.timestamp_microseconds + |
894 rendering_delay_.delay_microseconds; | 861 rendering_delay_.delay_microseconds; |
895 } | 862 } |
896 | 863 |
| 864 mixed.ToInterleaved(frames, BytesPerOutputFormatSample(), |
| 865 interleaved_.data()); |
| 866 |
| 867 // Ensure that, on onset of silence, at least |kSilenceSecondsToFilter| |
| 868 // second of audio get pushed through the filters to clear any memory. |
| 869 bool filter_frames = true; |
| 870 if (is_silence) { |
| 871 int silence_frames_to_filter = |
| 872 output_samples_per_second_ * kSilenceSecondsToFilter; |
| 873 if (silence_frames_filtered_ < silence_frames_to_filter) { |
| 874 silence_frames_filtered_ += frames; |
| 875 } else { |
| 876 filter_frames = false; |
| 877 } |
| 878 } else { |
| 879 silence_frames_filtered_ = 0; |
| 880 } |
| 881 |
| 882 // Filter, send to observers, and post filter |
| 883 if (pre_loopback_filter_ && filter_frames) { |
| 884 pre_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); |
| 885 } |
| 886 |
897 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { | 887 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { |
898 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, | 888 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, |
899 output_samples_per_second_, kNumOutputChannels, | 889 output_samples_per_second_, kNumOutputChannels, |
900 interleaved->data(), InterleavedSize(frames)); | 890 interleaved_.data(), interleaved_size); |
| 891 } |
| 892 |
| 893 if (post_loopback_filter_ && filter_frames) { |
| 894 post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); |
901 } | 895 } |
902 | 896 |
903 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need | 897 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need |
904 // to be prepared in order for playback to work. | 898 // to be prepared in order for playback to work. |
905 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { | 899 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { |
906 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 900 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
907 } | 901 } |
908 | 902 |
909 int frames_left = frames; | 903 int frames_left = frames; |
910 uint8_t* data = interleaved->data(); | 904 uint8_t* data = &interleaved_[0]; |
911 while (frames_left) { | 905 while (frames_left) { |
912 int frames_or_error; | 906 int frames_or_error; |
913 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { | 907 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { |
914 for (auto* observer : loopback_observers_) { | 908 for (auto* observer : loopback_observers_) { |
915 observer->OnLoopbackInterrupted(); | 909 observer->OnLoopbackInterrupted(); |
916 } | 910 } |
917 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, | 911 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, |
918 kPcmRecoverIsSilent); | 912 kPcmRecoverIsSilent); |
919 } | 913 } |
920 frames_left -= frames_or_error; | 914 frames_left -= frames_or_error; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 956 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
963 observer) != loopback_observers_.end()); | 957 observer) != loopback_observers_.end()); |
964 loopback_observers_.erase(std::remove(loopback_observers_.begin(), | 958 loopback_observers_.erase(std::remove(loopback_observers_.begin(), |
965 loopback_observers_.end(), observer), | 959 loopback_observers_.end(), observer), |
966 loopback_observers_.end()); | 960 loopback_observers_.end()); |
967 observer->OnRemoved(); | 961 observer->OnRemoved(); |
968 } | 962 } |
969 | 963 |
970 } // namespace media | 964 } // namespace media |
971 } // namespace chromecast | 965 } // namespace chromecast |
OLD | NEW |