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> |
11 | 11 |
12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
14 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
15 #include "base/memory/weak_ptr.h" | 15 #include "base/memory/weak_ptr.h" |
16 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
18 #include "base/threading/platform_thread.h" | 18 #include "base/threading/platform_thread.h" |
19 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
20 #include "chromecast/base/chromecast_switches.h" | 20 #include "chromecast/base/chromecast_switches.h" |
21 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" | 21 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" |
22 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" | 22 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" |
23 #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" |
24 #include "chromecast/public/media/audio_device_ids.h" | |
24 #include "media/base/audio_bus.h" | 25 #include "media/base/audio_bus.h" |
25 #include "media/base/media_switches.h" | 26 #include "media/base/media_switches.h" |
26 | 27 |
27 #define RETURN_REPORT_ERROR(snd_func, ...) \ | 28 #define RETURN_REPORT_ERROR(snd_func, ...) \ |
28 do { \ | 29 do { \ |
29 int err = alsa_->snd_func(__VA_ARGS__); \ | 30 int err = alsa_->snd_func(__VA_ARGS__); \ |
30 if (err < 0) { \ | 31 if (err < 0) { \ |
31 LOG(ERROR) << #snd_func " error: " << alsa_->StrError(err); \ | 32 LOG(ERROR) << #snd_func " error: " << alsa_->StrError(err); \ |
32 SignalError(); \ | 33 SignalError(); \ |
33 return; \ | 34 return; \ |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
110 // These sample formats will be tried in order. 32 bit samples is ideal, but | 111 // These sample formats will be tried in order. 32 bit samples is ideal, but |
111 // some devices do not support 32 bit samples. | 112 // some devices do not support 32 bit samples. |
112 const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32, | 113 const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32, |
113 SND_PCM_FORMAT_S16}; | 114 SND_PCM_FORMAT_S16}; |
114 | 115 |
115 // How many seconds of silence should be passed to the filters to flush them. | 116 // How many seconds of silence should be passed to the filters to flush them. |
116 const float kSilenceSecondsToFilter = 1.0f; | 117 const float kSilenceSecondsToFilter = 1.0f; |
117 | 118 |
118 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); | 119 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); |
119 | 120 |
121 const AudioFilterFactory::FilterType kFilterTypes[kNumFilterGroups] = { | |
122 AudioFilterFactory::MEDIA_FILTER, AudioFilterFactory::SYSTEM_AUDIO_FILTER}; | |
123 | |
124 int GetFilterGroup(std::string stream_name) { | |
125 if (stream_name == kSystemAudioDeviceId) { | |
126 return 1; | |
127 } | |
128 return 0; | |
129 } | |
130 | |
120 int64_t TimespecToMicroseconds(struct timespec time) { | 131 int64_t TimespecToMicroseconds(struct timespec time) { |
121 return static_cast<int64_t>(time.tv_sec) * | 132 return static_cast<int64_t>(time.tv_sec) * |
122 base::Time::kMicrosecondsPerSecond + | 133 base::Time::kMicrosecondsPerSecond + |
123 time.tv_nsec / 1000; | 134 time.tv_nsec / 1000; |
124 } | 135 } |
125 | 136 |
126 bool GetSwitchValueAsInt(const std::string& switch_name, | 137 bool GetSwitchValueAsInt(const std::string& switch_name, |
127 int default_value, | 138 int default_value, |
128 int* value) { | 139 int* value) { |
129 DCHECK(value); | 140 DCHECK(value); |
(...skipping 30 matching lines...) Expand all Loading... | |
160 } | 171 } |
161 | 172 |
162 if (*value < 0) { | 173 if (*value < 0) { |
163 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; | 174 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; |
164 *value = default_value; | 175 *value = default_value; |
165 return false; | 176 return false; |
166 } | 177 } |
167 return true; | 178 return true; |
168 } | 179 } |
169 | 180 |
181 void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) { | |
182 for (size_t i = 0; i < size; ++i) { | |
183 dest[i] += source[i]; | |
184 } | |
185 } | |
186 | |
170 class StreamMixerAlsaInstance : public StreamMixerAlsa { | 187 class StreamMixerAlsaInstance : public StreamMixerAlsa { |
171 public: | 188 public: |
172 StreamMixerAlsaInstance() {} | 189 StreamMixerAlsaInstance() {} |
173 ~StreamMixerAlsaInstance() override {} | 190 ~StreamMixerAlsaInstance() override {} |
174 | 191 |
175 private: | 192 private: |
176 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); | 193 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); |
177 }; | 194 }; |
178 | 195 |
179 base::LazyInstance<StreamMixerAlsaInstance> g_mixer_instance = | 196 base::LazyInstance<StreamMixerAlsaInstance> g_mixer_instance = |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
239 } | 256 } |
240 | 257 |
241 fixed_output_samples_per_second_ = fixed_samples_per_second; | 258 fixed_output_samples_per_second_ = fixed_samples_per_second; |
242 | 259 |
243 low_sample_rate_cutoff_ = | 260 low_sample_rate_cutoff_ = |
244 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) | 261 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) |
245 ? kLowSampleRateCutoff | 262 ? kLowSampleRateCutoff |
246 : 0; | 263 : 0; |
247 | 264 |
248 // Create filters | 265 // Create filters |
249 pre_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( | 266 for (int filter = 0; filter < kNumFilterGroups; ++filter) { |
250 AudioFilterFactory::PRE_LOOPBACK_FILTER); | 267 pre_loopback_filter_[filter] = |
268 AudioFilterFactory::MakeAudioFilter(kFilterTypes[filter]); | |
269 } | |
251 post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( | 270 post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( |
252 AudioFilterFactory::POST_LOOPBACK_FILTER); | 271 AudioFilterFactory::POST_LOOPBACK_FILTER); |
253 | 272 |
254 DefineAlsaParameters(); | 273 DefineAlsaParameters(); |
255 } | 274 } |
256 | 275 |
257 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 276 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
258 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 277 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
259 } | 278 } |
260 | 279 |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
522 int err = SetAlsaPlaybackParams(); | 541 int err = SetAlsaPlaybackParams(); |
523 if (err < 0) { | 542 if (err < 0) { |
524 LOG(ERROR) << "Error setting ALSA playback parameters: " | 543 LOG(ERROR) << "Error setting ALSA playback parameters: " |
525 << alsa_->StrError(err); | 544 << alsa_->StrError(err); |
526 SignalError(); | 545 SignalError(); |
527 return; | 546 return; |
528 } | 547 } |
529 } | 548 } |
530 | 549 |
531 // Initialize filters | 550 // Initialize filters |
532 if (pre_loopback_filter_) { | 551 for (int filter = 0; filter < kNumFilterGroups; ++filter) { |
533 pre_loopback_filter_->SetSampleRateAndFormat( | 552 if (pre_loopback_filter_[filter]) { |
534 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | 553 pre_loopback_filter_[filter]->SetSampleRateAndFormat( |
554 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | |
555 } | |
535 } | 556 } |
536 | 557 |
537 if (post_loopback_filter_) { | 558 if (post_loopback_filter_) { |
538 post_loopback_filter_->SetSampleRateAndFormat( | 559 post_loopback_filter_->SetSampleRateAndFormat( |
539 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | 560 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); |
540 } | 561 } |
541 | 562 |
542 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 563 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
543 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); | 564 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); |
544 | 565 |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
762 | 783 |
763 bool StreamMixerAlsa::TryWriteFrames() { | 784 bool StreamMixerAlsa::TryWriteFrames() { |
764 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 785 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
765 if (state_ != kStateNormalPlayback) { | 786 if (state_ != kStateNormalPlayback) { |
766 return false; | 787 return false; |
767 } | 788 } |
768 | 789 |
769 const int min_frames_in_buffer = | 790 const int min_frames_in_buffer = |
770 output_samples_per_second_ * kMinBufferedDataMs / 1000; | 791 output_samples_per_second_ * kMinBufferedDataMs / 1000; |
771 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; | 792 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; |
772 std::vector<InputQueue*> active_inputs; | 793 std::vector<InputQueue*> active_inputs[kNumFilterGroups]; |
794 bool is_silence = true; | |
773 for (auto&& input : inputs_) { | 795 for (auto&& input : inputs_) { |
774 int read_size = input->MaxReadSize(); | 796 int read_size = input->MaxReadSize(); |
775 if (read_size > 0) { | 797 if (read_size > 0) { |
776 active_inputs.push_back(input.get()); | 798 active_inputs[GetFilterGroup(input->name())].push_back(input.get()); |
777 chunk_size = std::min(chunk_size, read_size); | 799 chunk_size = std::min(chunk_size, read_size); |
800 is_silence = false; | |
778 } else if (input->primary()) { | 801 } else if (input->primary()) { |
779 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { | 802 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { |
780 LOG(ERROR) << "Failed to get status"; | 803 LOG(ERROR) << "Failed to get status"; |
781 return false; | 804 return false; |
782 } | 805 } |
783 | 806 |
784 int frames_in_buffer = | 807 int frames_in_buffer = |
785 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); | 808 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); |
786 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || | 809 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || |
787 frames_in_buffer < min_frames_in_buffer) { | 810 frames_in_buffer < min_frames_in_buffer) { |
788 // If there has been (or soon will be) an underrun, continue without the | 811 // If there has been (or soon will be) an underrun, continue without the |
789 // empty primary input stream. | 812 // empty primary input stream. |
790 input->OnSkipped(); | 813 input->OnSkipped(); |
791 continue; | 814 continue; |
792 } | 815 } |
793 | 816 |
794 // A primary input cannot provide any data, so wait until later. | 817 // A primary input cannot provide any data, so wait until later. |
795 retry_write_frames_timer_->Start( | 818 retry_write_frames_timer_->Start( |
796 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), | 819 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), |
797 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); | 820 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); |
798 return false; | 821 return false; |
799 } else { | 822 } else { |
800 input->OnSkipped(); | 823 input->OnSkipped(); |
801 } | 824 } |
802 } | 825 } |
803 | 826 |
804 if (active_inputs.empty()) { | 827 if (is_silence) { |
805 // No inputs have any data to provide. Fill with silence to avoid underrun. | 828 // No inputs have any data to provide. Push silence to prevent underrun. |
806 chunk_size = kPreventUnderrunChunkSize; | 829 chunk_size = kPreventUnderrunChunkSize; |
807 if (!mixed_ || mixed_->frames() < chunk_size) { | 830 ResizeBuffersIfNecessary(chunk_size); |
808 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | 831 memset(interleaved_.data(), 0, |
809 } | 832 static_cast<size_t>(chunk_size * kNumOutputChannels) * |
810 | 833 BytesPerOutputFormatSample()); |
811 mixed_->Zero(); | 834 } else { |
812 WriteMixedPcm(*mixed_, chunk_size, true /* is_silence */); | 835 ResizeBuffersIfNecessary(chunk_size); |
813 return true; | |
814 } | 836 } |
815 | 837 |
816 // If |mixed_| has not been allocated, or it is too small, allocate a buffer. | 838 // If |mixed_|, |temp_|, |interleaved_|, or |interleaved_intermediate_| have |
817 if (!mixed_ || mixed_->frames() < chunk_size) { | 839 // not been allocated, or are too small, allocate a buffer. |
818 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | 840 |
841 // Mix each group, pass through corresponding audio filter, and | |
842 // accumulate into |interleaved_|. | |
843 bool filter_frames = false; | |
844 for (int filter_group = 0; filter_group < kNumFilterGroups; ++filter_group) { | |
845 filter_frames = | |
846 filter_frames || | |
847 MixAndFilterGroup(active_inputs[filter_group], filter_group, chunk_size, | |
848 filter_frames /* accumulate */); | |
819 } | 849 } |
820 | 850 |
821 // If |temp_| has not been allocated, or is too small, allocate a buffer. | 851 WriteMixedPcm(chunk_size, filter_frames); |
822 if (!temp_ || temp_->frames() < chunk_size) { | 852 return true; |
823 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | 853 } |
824 } | |
825 | 854 |
826 mixed_->ZeroFramesPartial(0, chunk_size); | 855 bool StreamMixerAlsa::MixAndFilterGroup( |
827 | 856 const std::vector<InputQueue*>& active_inputs, |
828 // Loop through active inputs, polling them for data, and mixing them. | 857 int filter_group, |
829 for (InputQueue* input : active_inputs) { | 858 int frames, |
830 input->GetResampledData(temp_.get(), chunk_size); | 859 bool accumulate) { |
831 for (int c = 0; c < kNumOutputChannels; ++c) { | 860 // Mix into group buffer, |mixed_|. |
832 input->VolumeScaleAccumulate(c, temp_->channel(c), chunk_size, | 861 mixed_->ZeroFramesPartial(0, frames); |
833 mixed_->channel(c)); | 862 bool is_silence = true; |
863 if (!active_inputs.empty()) { | |
864 is_silence = false; | |
865 // Loop through active inputs, polling them for data, and mixing them. | |
866 for (InputQueue* input : active_inputs) { | |
867 input->GetResampledData(temp_.get(), frames); | |
868 for (int c = 0; c < kNumOutputChannels; ++c) { | |
869 input->VolumeScaleAccumulate(c, temp_->channel(c), frames, | |
870 mixed_->channel(c)); | |
871 } | |
834 } | 872 } |
835 } | 873 } |
836 | 874 |
837 WriteMixedPcm(*mixed_, chunk_size, false /* is_silence */); | 875 // If no data has been written to |interleaved_| thus far, |
838 return true; | 876 // we can write to |interleaved_| directly, skipping a vector addition step. |
877 std::vector<uint8_t>* interleaved_group = &interleaved_intermediate_; | |
878 if (!accumulate) { | |
879 interleaved_group = &interleaved_; | |
880 } | |
881 | |
882 // Convert to interleaved for post-processing. | |
883 mixed_->ToInterleaved(frames, BytesPerOutputFormatSample(), | |
884 interleaved_group->data()); | |
885 | |
886 // Ensure that, on onset of silence, at least |kSilenceSecondsToFilter| | |
887 // second of audio get pushed through the filters to clear any memory. | |
888 bool filter_frames = true; | |
kmackay
2017/02/17 06:11:25
The design for mixing seems a bit clunky. I'd rath
bshaya
2017/02/17 18:26:53
Hmm, how about that, and:
* Have an InputGroup cla
kmackay
2017/02/17 19:42:42
Your suggestions sound good. I meant for the per-s
bshaya
2017/02/21 23:30:13
Done.
| |
889 if (is_silence) { | |
890 int silence_frames_to_filter = | |
891 output_samples_per_second_ * kSilenceSecondsToFilter; | |
892 if (silence_frames_filtered_[filter_group] < silence_frames_to_filter) { | |
893 silence_frames_filtered_[filter_group] += frames; | |
894 } else { | |
895 return false; // Output will be silence, no need to mix. | |
896 } | |
897 } else { | |
898 silence_frames_filtered_[filter_group] = 0; | |
899 } | |
900 | |
901 // Filter the mixed group. | |
902 if (pre_loopback_filter_[filter_group] && filter_frames) { | |
903 pre_loopback_filter_[filter_group]->ProcessInterleaved( | |
904 interleaved_group->data(), frames); | |
905 } | |
906 | |
907 // Exit if we already wrote to |interleaved_|. | |
908 if (!accumulate) { | |
909 return filter_frames; | |
910 } | |
911 | |
912 // Mix into final output buffer, |interleaved_| | |
913 DCHECK_EQ(4, BytesPerOutputFormatSample()); | |
914 VectorAccumulate(reinterpret_cast<int32_t*>(interleaved_group->data()), | |
915 frames * kNumOutputChannels, | |
916 reinterpret_cast<int32_t*>(interleaved_.data())); | |
917 return filter_frames; | |
839 } | 918 } |
840 | 919 |
841 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { | 920 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { |
842 return alsa_->PcmFormatSize(pcm_format_, 1); | 921 return alsa_->PcmFormatSize(pcm_format_, 1); |
843 } | 922 } |
844 | 923 |
845 void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, | 924 void StreamMixerAlsa::WriteMixedPcm(int frames, bool filter_frames) { |
846 int frames, bool is_silence) { | |
847 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 925 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
848 CHECK_PCM_INITIALIZED(); | 926 CHECK_PCM_INITIALIZED(); |
849 | 927 |
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 } | |
855 | |
856 int64_t expected_playback_time; | 928 int64_t expected_playback_time; |
857 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { | 929 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { |
858 expected_playback_time = kNoTimestamp; | 930 expected_playback_time = kNoTimestamp; |
859 } else { | 931 } else { |
860 expected_playback_time = rendering_delay_.timestamp_microseconds + | 932 expected_playback_time = rendering_delay_.timestamp_microseconds + |
861 rendering_delay_.delay_microseconds; | 933 rendering_delay_.delay_microseconds; |
862 } | 934 } |
863 | 935 |
864 mixed.ToInterleaved(frames, BytesPerOutputFormatSample(), | 936 size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) * |
kmackay
2017/02/17 06:11:25
use int instead of size_t
bshaya
2017/02/17 18:26:54
See the first point in "Types" in https://chromium
| |
865 interleaved_.data()); | 937 BytesPerOutputFormatSample(); |
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_) { | 938 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { |
888 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, | 939 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, |
889 output_samples_per_second_, kNumOutputChannels, | 940 output_samples_per_second_, kNumOutputChannels, |
890 interleaved_.data(), interleaved_size); | 941 interleaved_.data(), interleaved_size); |
891 } | 942 } |
892 | 943 |
893 if (post_loopback_filter_ && filter_frames) { | 944 if (post_loopback_filter_ && filter_frames) { |
kmackay
2017/02/17 06:11:25
Do we ever use post_loopback_filter? If not, let's
bshaya
2017/02/17 18:26:54
Done.
| |
894 post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); | 945 post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); |
895 } | 946 } |
896 | 947 |
897 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need | 948 // 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. | 949 // to be prepared in order for playback to work. |
899 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { | 950 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { |
900 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 951 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
901 } | 952 } |
902 | 953 |
903 int frames_left = frames; | 954 int frames_left = frames; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
954 CastMediaShlib::LoopbackAudioObserver* observer) { | 1005 CastMediaShlib::LoopbackAudioObserver* observer) { |
955 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); | 1006 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); |
956 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 1007 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
957 observer) != loopback_observers_.end()); | 1008 observer) != loopback_observers_.end()); |
958 loopback_observers_.erase(std::remove(loopback_observers_.begin(), | 1009 loopback_observers_.erase(std::remove(loopback_observers_.begin(), |
959 loopback_observers_.end(), observer), | 1010 loopback_observers_.end(), observer), |
960 loopback_observers_.end()); | 1011 loopback_observers_.end()); |
961 observer->OnRemoved(); | 1012 observer->OnRemoved(); |
962 } | 1013 } |
963 | 1014 |
1015 void StreamMixerAlsa::ResizeBuffersIfNecessary(int chunk_size) { | |
1016 if (!mixed_ || mixed_->frames() < chunk_size) { | |
1017 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | |
1018 } | |
1019 if (!temp_ || temp_->frames() < chunk_size) { | |
1020 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); | |
1021 } | |
1022 | |
1023 size_t interleaved_size = | |
1024 static_cast<size_t>(chunk_size * kNumOutputChannels) * | |
1025 BytesPerOutputFormatSample(); | |
1026 if (interleaved_.size() < interleaved_size) { | |
1027 interleaved_.resize(interleaved_size); | |
1028 } | |
1029 if (interleaved_intermediate_.size() < interleaved_size) { | |
1030 interleaved_intermediate_.resize(interleaved_size); | |
1031 } | |
1032 } | |
1033 | |
964 } // namespace media | 1034 } // namespace media |
965 } // namespace chromecast | 1035 } // namespace chromecast |
OLD | NEW |