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> | |
10 #include <utility> | 11 #include <utility> |
11 | 12 |
12 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
13 #include "base/command_line.h" | 14 #include "base/command_line.h" |
14 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
16 #include "base/memory/ptr_util.h" | |
15 #include "base/memory/weak_ptr.h" | 17 #include "base/memory/weak_ptr.h" |
16 #include "base/single_thread_task_runner.h" | 18 #include "base/single_thread_task_runner.h" |
17 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
18 #include "base/threading/platform_thread.h" | 20 #include "base/threading/platform_thread.h" |
19 #include "base/threading/thread_task_runner_handle.h" | 21 #include "base/threading/thread_task_runner_handle.h" |
20 #include "chromecast/base/chromecast_switches.h" | 22 #include "chromecast/base/chromecast_switches.h" |
23 #include "chromecast/media/base/audio_device_ids.h" | |
21 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" | 24 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" |
22 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" | 25 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" |
26 #include "chromecast/media/cma/backend/alsa/filter_group.h" | |
23 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" | 27 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" |
28 #include "media/audio/audio_device_description.h" | |
24 #include "media/base/audio_bus.h" | 29 #include "media/base/audio_bus.h" |
25 #include "media/base/media_switches.h" | 30 #include "media/base/media_switches.h" |
26 | 31 |
27 #define RETURN_REPORT_ERROR(snd_func, ...) \ | 32 #define RETURN_REPORT_ERROR(snd_func, ...) \ |
28 do { \ | 33 do { \ |
29 int err = alsa_->snd_func(__VA_ARGS__); \ | 34 int err = alsa_->snd_func(__VA_ARGS__); \ |
30 if (err < 0) { \ | 35 if (err < 0) { \ |
31 LOG(ERROR) << #snd_func " error: " << alsa_->StrError(err); \ | 36 LOG(ERROR) << #snd_func " error: " << alsa_->StrError(err); \ |
32 SignalError(); \ | 37 SignalError(); \ |
33 return; \ | 38 return; \ |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
105 // direction they adjusted the requested parameter in, but since we read the | 110 // direction they adjusted the requested parameter in, but since we read the |
106 // output param and then log the information, this module doesn't need to get | 111 // output param and then log the information, this module doesn't need to get |
107 // the direction explicitly. | 112 // the direction explicitly. |
108 static int* kAlsaDirDontCare = nullptr; | 113 static int* kAlsaDirDontCare = nullptr; |
109 | 114 |
110 // These sample formats will be tried in order. 32 bit samples is ideal, but | 115 // These sample formats will be tried in order. 32 bit samples is ideal, but |
111 // some devices do not support 32 bit samples. | 116 // some devices do not support 32 bit samples. |
112 const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32, | 117 const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32, |
113 SND_PCM_FORMAT_S16}; | 118 SND_PCM_FORMAT_S16}; |
114 | 119 |
115 // How many seconds of silence should be passed to the filters to flush them. | |
116 const float kSilenceSecondsToFilter = 1.0f; | |
117 | |
118 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); | 120 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); |
119 | 121 |
120 int64_t TimespecToMicroseconds(struct timespec time) { | 122 int64_t TimespecToMicroseconds(struct timespec time) { |
121 return static_cast<int64_t>(time.tv_sec) * | 123 return static_cast<int64_t>(time.tv_sec) * |
122 base::Time::kMicrosecondsPerSecond + | 124 base::Time::kMicrosecondsPerSecond + |
123 time.tv_nsec / 1000; | 125 time.tv_nsec / 1000; |
124 } | 126 } |
125 | 127 |
126 bool GetSwitchValueAsInt(const std::string& switch_name, | 128 bool GetSwitchValueAsInt(const std::string& switch_name, |
127 int default_value, | 129 int default_value, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
160 } | 162 } |
161 | 163 |
162 if (*value < 0) { | 164 if (*value < 0) { |
163 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; | 165 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; |
164 *value = default_value; | 166 *value = default_value; |
165 return false; | 167 return false; |
166 } | 168 } |
167 return true; | 169 return true; |
168 } | 170 } |
169 | 171 |
172 // Computes |A| +|B| with clipping | |
173 int32_t SaturateSum(int32_t a, int32_t b) { | |
kmackay
2017/02/23 20:52:56
base/numerics/saturated_arithmetic.h
bshaya
2017/02/24 00:17:08
Done.
| |
174 if (a > 0 && std::numeric_limits<int32_t>::max() - a < b) { | |
175 return std::numeric_limits<int32_t>::max(); | |
176 } | |
177 if (a < 0 && std::numeric_limits<int32_t>::min() - a > b) { | |
178 return std::numeric_limits<int32_t>::min(); | |
179 } | |
180 return a + b; | |
181 } | |
182 | |
183 void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) { | |
184 for (size_t i = 0; i < size; ++i) { | |
185 dest[i] = SaturateSum(source[i], dest[i]); | |
186 } | |
187 } | |
188 | |
170 class StreamMixerAlsaInstance : public StreamMixerAlsa { | 189 class StreamMixerAlsaInstance : public StreamMixerAlsa { |
171 public: | 190 public: |
172 StreamMixerAlsaInstance() {} | 191 StreamMixerAlsaInstance() {} |
173 ~StreamMixerAlsaInstance() override {} | 192 ~StreamMixerAlsaInstance() override {} |
174 | 193 |
175 private: | 194 private: |
176 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); | 195 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); |
177 }; | 196 }; |
178 | 197 |
179 base::LazyInstance<StreamMixerAlsaInstance> g_mixer_instance = | 198 base::LazyInstance<StreamMixerAlsaInstance> g_mixer_instance = |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
238 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; | 257 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; |
239 } | 258 } |
240 | 259 |
241 fixed_output_samples_per_second_ = fixed_samples_per_second; | 260 fixed_output_samples_per_second_ = fixed_samples_per_second; |
242 | 261 |
243 low_sample_rate_cutoff_ = | 262 low_sample_rate_cutoff_ = |
244 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) | 263 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) |
245 ? kLowSampleRateCutoff | 264 ? kLowSampleRateCutoff |
246 : 0; | 265 : 0; |
247 | 266 |
248 // Create filters | 267 // Create filter groups. |
249 pre_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( | 268 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
250 AudioFilterFactory::PRE_LOOPBACK_FILTER); | 269 std::unordered_set<std::string>( |
251 post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( | 270 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), |
252 AudioFilterFactory::POST_LOOPBACK_FILTER); | 271 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER)); |
272 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
273 std::unordered_set<std::string>({kAlarmAudioDeviceId}), | |
274 AudioFilterFactory::ALARM_AUDIO_FILTER)); | |
275 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
276 std::unordered_set<std::string>({kTtsAudioDeviceId}), | |
277 AudioFilterFactory::TTS_AUDIO_FILTER)); | |
278 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
279 std::unordered_set<std::string>( | |
280 {::media::AudioDeviceDescription::kDefaultDeviceId, | |
281 kEarconAudioDeviceId}), | |
282 AudioFilterFactory::MEDIA_AUDIO_FILTER)); | |
gfhuang
2017/02/23 21:24:56
I don't think this is the right thing to do.
Thes
kmackay
2017/02/23 21:30:36
I think this is OK. It's only temporary (as I unde
bshaya
2017/02/24 00:17:08
The long term probably still has this. For instanc
kmackay
2017/02/24 17:24:57
We haven't planned any way to distinguish content
bshaya
2017/02/27 17:00:14
Done.
| |
253 | 283 |
254 DefineAlsaParameters(); | 284 DefineAlsaParameters(); |
255 } | 285 } |
256 | 286 |
257 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 287 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
258 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 288 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
259 } | 289 } |
260 | 290 |
261 void StreamMixerAlsa::DefineAlsaParameters() { | 291 void StreamMixerAlsa::DefineAlsaParameters() { |
262 // Get the ALSA output configuration from the command line. | 292 // Get the ALSA output configuration from the command line. |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
522 int err = SetAlsaPlaybackParams(); | 552 int err = SetAlsaPlaybackParams(); |
523 if (err < 0) { | 553 if (err < 0) { |
524 LOG(ERROR) << "Error setting ALSA playback parameters: " | 554 LOG(ERROR) << "Error setting ALSA playback parameters: " |
525 << alsa_->StrError(err); | 555 << alsa_->StrError(err); |
526 SignalError(); | 556 SignalError(); |
527 return; | 557 return; |
528 } | 558 } |
529 } | 559 } |
530 | 560 |
531 // Initialize filters | 561 // Initialize filters |
532 if (pre_loopback_filter_) { | 562 for (auto&& filter_group : filter_groups_) { |
533 pre_loopback_filter_->SetSampleRateAndFormat( | 563 filter_group->Initialize(output_samples_per_second_, |
534 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | 564 ::media::SampleFormat::kSampleFormatS32); |
535 } | |
536 | |
537 if (post_loopback_filter_) { | |
538 post_loopback_filter_->SetSampleRateAndFormat( | |
539 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | |
540 } | 565 } |
541 | 566 |
542 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 567 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
543 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); | 568 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); |
544 | 569 |
545 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 570 rendering_delay_.timestamp_microseconds = kNoTimestamp; |
546 rendering_delay_.delay_microseconds = 0; | 571 rendering_delay_.delay_microseconds = 0; |
547 | 572 |
548 state_ = kStateNormalPlayback; | 573 state_ = kStateNormalPlayback; |
549 } | 574 } |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
755 retry_write_frames_timer_->Stop(); | 780 retry_write_frames_timer_->Stop(); |
756 if (TryWriteFrames()) { | 781 if (TryWriteFrames()) { |
757 retry_write_frames_timer_->Start( | 782 retry_write_frames_timer_->Start( |
758 FROM_HERE, base::TimeDelta(), | 783 FROM_HERE, base::TimeDelta(), |
759 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); | 784 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); |
760 } | 785 } |
761 } | 786 } |
762 | 787 |
763 bool StreamMixerAlsa::TryWriteFrames() { | 788 bool StreamMixerAlsa::TryWriteFrames() { |
764 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 789 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
790 DCHECK_GE(filter_groups_.size(), 1u); | |
791 | |
765 if (state_ != kStateNormalPlayback) { | 792 if (state_ != kStateNormalPlayback) { |
766 return false; | 793 return false; |
767 } | 794 } |
768 | 795 |
769 const int min_frames_in_buffer = | 796 const int min_frames_in_buffer = |
770 output_samples_per_second_ * kMinBufferedDataMs / 1000; | 797 output_samples_per_second_ * kMinBufferedDataMs / 1000; |
771 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; | 798 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; |
772 std::vector<InputQueue*> active_inputs; | 799 bool is_silence = true; |
773 for (auto&& input : inputs_) { | 800 for (auto&& input : inputs_) { |
774 int read_size = input->MaxReadSize(); | 801 int read_size = input->MaxReadSize(); |
775 if (read_size > 0) { | 802 if (read_size > 0) { |
776 active_inputs.push_back(input.get()); | 803 bool group_match = false; |
804 for (auto&& filter_group : filter_groups_) { | |
805 if (filter_group->TryAddActiveInput(input.get())) { | |
806 group_match = true; | |
807 break; | |
808 } | |
809 } | |
810 DCHECK(group_match) << "Could not find a filter group for " | |
811 << input->name(); | |
777 chunk_size = std::min(chunk_size, read_size); | 812 chunk_size = std::min(chunk_size, read_size); |
813 is_silence = false; | |
778 } else if (input->primary()) { | 814 } else if (input->primary()) { |
779 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { | 815 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { |
780 LOG(ERROR) << "Failed to get status"; | 816 LOG(ERROR) << "Failed to get status"; |
781 return false; | 817 return false; |
782 } | 818 } |
783 | 819 |
784 int frames_in_buffer = | 820 int frames_in_buffer = |
785 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); | 821 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); |
786 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || | 822 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || |
787 frames_in_buffer < min_frames_in_buffer) { | 823 frames_in_buffer < min_frames_in_buffer) { |
788 // If there has been (or soon will be) an underrun, continue without the | 824 // If there has been (or soon will be) an underrun, continue without the |
789 // empty primary input stream. | 825 // empty primary input stream. |
790 input->OnSkipped(); | 826 input->OnSkipped(); |
791 continue; | 827 continue; |
792 } | 828 } |
793 | 829 |
794 // A primary input cannot provide any data, so wait until later. | 830 // A primary input cannot provide any data, so wait until later. |
795 retry_write_frames_timer_->Start( | 831 retry_write_frames_timer_->Start( |
796 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), | 832 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), |
797 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); | 833 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); |
798 return false; | 834 return false; |
799 } else { | 835 } else { |
800 input->OnSkipped(); | 836 input->OnSkipped(); |
801 } | 837 } |
802 } | 838 } |
803 | 839 |
804 if (active_inputs.empty()) { | 840 if (is_silence) { |
805 // No inputs have any data to provide. Fill with silence to avoid underrun. | 841 // No inputs have any data to provide. Push silence to prevent underrun. |
806 chunk_size = kPreventUnderrunChunkSize; | 842 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; | |
814 } | 843 } |
815 | 844 |
816 // If |mixed_| has not been allocated, or it is too small, allocate a buffer. | 845 // Mix and filter each group. |
817 if (!mixed_ || mixed_->frames() < chunk_size) { | 846 std::vector<uint8_t>* interleaved = nullptr; |
818 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | 847 for (auto&& filter_group : filter_groups_) { |
819 } | 848 if (filter_group->MixAndFilter(chunk_size)) { |
820 | 849 if (!interleaved) { |
821 // If |temp_| has not been allocated, or is too small, allocate a buffer. | 850 interleaved = filter_group->GetInterleaved(); |
822 if (!temp_ || temp_->frames() < chunk_size) { | 851 } else { |
823 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | 852 DCHECK_EQ(4, BytesPerOutputFormatSample()); |
824 } | 853 VectorAccumulate( |
825 | 854 reinterpret_cast<int32_t*>(filter_group->GetInterleaved()->data()), |
826 mixed_->ZeroFramesPartial(0, chunk_size); | 855 chunk_size * kNumOutputChannels, |
827 | 856 reinterpret_cast<int32_t*>(interleaved->data())); |
828 // Loop through active inputs, polling them for data, and mixing them. | 857 } |
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)); | |
834 } | 858 } |
835 } | 859 } |
836 | 860 |
837 WriteMixedPcm(*mixed_, chunk_size, false /* is_silence */); | 861 if (!interleaved) { |
862 // No group has any data, write empty buffer. | |
863 filter_groups_[0]->ClearInterleaved(chunk_size); | |
864 interleaved = filter_groups_[0]->GetInterleaved(); | |
865 } | |
866 | |
867 WriteMixedPcm(interleaved, chunk_size); | |
838 return true; | 868 return true; |
839 } | 869 } |
840 | 870 |
871 size_t StreamMixerAlsa::InterleavedSize(int frames) { | |
872 return BytesPerOutputFormatSample() * | |
873 static_cast<size_t>(frames * kNumOutputChannels); | |
874 } | |
875 | |
841 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { | 876 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { |
842 return alsa_->PcmFormatSize(pcm_format_, 1); | 877 return alsa_->PcmFormatSize(pcm_format_, 1); |
843 } | 878 } |
844 | 879 |
845 void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, | 880 void StreamMixerAlsa::WriteMixedPcm(std::vector<uint8_t>* interleaved, |
846 int frames, bool is_silence) { | 881 int frames) { |
847 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 882 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
848 CHECK_PCM_INITIALIZED(); | 883 CHECK_PCM_INITIALIZED(); |
849 | 884 DCHECK(interleaved); |
850 size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) * | 885 DCHECK_GE(interleaved->size(), InterleavedSize(frames)); |
851 BytesPerOutputFormatSample(); | |
852 if (interleaved_.size() < interleaved_size) { | |
853 interleaved_.resize(interleaved_size); | |
854 } | |
855 | 886 |
856 int64_t expected_playback_time; | 887 int64_t expected_playback_time; |
857 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { | 888 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { |
858 expected_playback_time = kNoTimestamp; | 889 expected_playback_time = kNoTimestamp; |
859 } else { | 890 } else { |
860 expected_playback_time = rendering_delay_.timestamp_microseconds + | 891 expected_playback_time = rendering_delay_.timestamp_microseconds + |
861 rendering_delay_.delay_microseconds; | 892 rendering_delay_.delay_microseconds; |
862 } | 893 } |
863 | 894 |
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 | |
887 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { | 895 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { |
888 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, | 896 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, |
889 output_samples_per_second_, kNumOutputChannels, | 897 output_samples_per_second_, kNumOutputChannels, |
890 interleaved_.data(), interleaved_size); | 898 interleaved->data(), InterleavedSize(frames)); |
891 } | |
892 | |
893 if (post_loopback_filter_ && filter_frames) { | |
894 post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); | |
895 } | 899 } |
896 | 900 |
897 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need | 901 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need |
898 // to be prepared in order for playback to work. | 902 // to be prepared in order for playback to work. |
899 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { | 903 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { |
900 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 904 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
901 } | 905 } |
902 | 906 |
903 int frames_left = frames; | 907 int frames_left = frames; |
904 uint8_t* data = &interleaved_[0]; | 908 uint8_t* data = interleaved->data(); |
905 while (frames_left) { | 909 while (frames_left) { |
906 int frames_or_error; | 910 int frames_or_error; |
907 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { | 911 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { |
908 for (auto* observer : loopback_observers_) { | 912 for (auto* observer : loopback_observers_) { |
909 observer->OnLoopbackInterrupted(); | 913 observer->OnLoopbackInterrupted(); |
910 } | 914 } |
911 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, | 915 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, |
912 kPcmRecoverIsSilent); | 916 kPcmRecoverIsSilent); |
913 } | 917 } |
914 frames_left -= frames_or_error; | 918 frames_left -= frames_or_error; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
956 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 960 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
957 observer) != loopback_observers_.end()); | 961 observer) != loopback_observers_.end()); |
958 loopback_observers_.erase(std::remove(loopback_observers_.begin(), | 962 loopback_observers_.erase(std::remove(loopback_observers_.begin(), |
959 loopback_observers_.end(), observer), | 963 loopback_observers_.end(), observer), |
960 loopback_observers_.end()); | 964 loopback_observers_.end()); |
961 observer->OnRemoved(); | 965 observer->OnRemoved(); |
962 } | 966 } |
963 | 967 |
964 } // namespace media | 968 } // namespace media |
965 } // namespace chromecast | 969 } // namespace chromecast |
OLD | NEW |