Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(160)

Side by Side Diff: chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc

Issue 2701613006: [Chromecast] Process streams with different post-processing. (Closed)
Patch Set: Fix nits Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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];
wzhong 2017/02/21 16:03:46 No clipping or saturation?
bshaya 2017/02/21 23:30:14 Done.
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
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] =
251 post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( 268 AudioFilterFactory::MakeAudioFilter(kFilterTypes[filter]);
252 AudioFilterFactory::POST_LOOPBACK_FILTER); 269 }
253 270
254 DefineAlsaParameters(); 271 DefineAlsaParameters();
255 } 272 }
256 273
257 void StreamMixerAlsa::ResetTaskRunnerForTest() { 274 void StreamMixerAlsa::ResetTaskRunnerForTest() {
258 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); 275 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get();
259 } 276 }
260 277
261 void StreamMixerAlsa::DefineAlsaParameters() { 278 void StreamMixerAlsa::DefineAlsaParameters() {
262 // Get the ALSA output configuration from the command line. 279 // Get the ALSA output configuration from the command line.
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
522 int err = SetAlsaPlaybackParams(); 539 int err = SetAlsaPlaybackParams();
523 if (err < 0) { 540 if (err < 0) {
524 LOG(ERROR) << "Error setting ALSA playback parameters: " 541 LOG(ERROR) << "Error setting ALSA playback parameters: "
525 << alsa_->StrError(err); 542 << alsa_->StrError(err);
526 SignalError(); 543 SignalError();
527 return; 544 return;
528 } 545 }
529 } 546 }
530 547
531 // Initialize filters 548 // Initialize filters
532 if (pre_loopback_filter_) { 549 for (int filter = 0; filter < kNumFilterGroups; ++filter) {
533 pre_loopback_filter_->SetSampleRateAndFormat( 550 if (pre_loopback_filter_[filter]) {
534 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); 551 pre_loopback_filter_[filter]->SetSampleRateAndFormat(
535 } 552 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32);
536 553 }
537 if (post_loopback_filter_) {
538 post_loopback_filter_->SetSampleRateAndFormat(
539 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32);
540 } 554 }
541 555
542 RETURN_REPORT_ERROR(PcmPrepare, pcm_); 556 RETURN_REPORT_ERROR(PcmPrepare, pcm_);
543 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); 557 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_);
544 558
545 rendering_delay_.timestamp_microseconds = kNoTimestamp; 559 rendering_delay_.timestamp_microseconds = kNoTimestamp;
546 rendering_delay_.delay_microseconds = 0; 560 rendering_delay_.delay_microseconds = 0;
547 561
548 state_ = kStateNormalPlayback; 562 state_ = kStateNormalPlayback;
549 } 563 }
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
762 776
763 bool StreamMixerAlsa::TryWriteFrames() { 777 bool StreamMixerAlsa::TryWriteFrames() {
764 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); 778 DCHECK(mixer_task_runner_->BelongsToCurrentThread());
765 if (state_ != kStateNormalPlayback) { 779 if (state_ != kStateNormalPlayback) {
766 return false; 780 return false;
767 } 781 }
768 782
769 const int min_frames_in_buffer = 783 const int min_frames_in_buffer =
770 output_samples_per_second_ * kMinBufferedDataMs / 1000; 784 output_samples_per_second_ * kMinBufferedDataMs / 1000;
771 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; 785 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000;
772 std::vector<InputQueue*> active_inputs; 786 std::vector<InputQueue*> active_inputs[kNumFilterGroups];
787 bool is_silence = true;
773 for (auto&& input : inputs_) { 788 for (auto&& input : inputs_) {
774 int read_size = input->MaxReadSize(); 789 int read_size = input->MaxReadSize();
775 if (read_size > 0) { 790 if (read_size > 0) {
776 active_inputs.push_back(input.get()); 791 active_inputs[GetFilterGroup(input->name())].push_back(input.get());
777 chunk_size = std::min(chunk_size, read_size); 792 chunk_size = std::min(chunk_size, read_size);
793 is_silence = false;
778 } else if (input->primary()) { 794 } else if (input->primary()) {
779 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) { 795 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) {
780 LOG(ERROR) << "Failed to get status"; 796 LOG(ERROR) << "Failed to get status";
781 return false; 797 return false;
782 } 798 }
783 799
784 int frames_in_buffer = 800 int frames_in_buffer =
785 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_); 801 alsa_buffer_size_ - alsa_->PcmStatusGetAvail(pcm_status_);
786 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN || 802 if (alsa_->PcmStatusGetState(pcm_status_) == SND_PCM_STATE_XRUN ||
787 frames_in_buffer < min_frames_in_buffer) { 803 frames_in_buffer < min_frames_in_buffer) {
788 // If there has been (or soon will be) an underrun, continue without the 804 // If there has been (or soon will be) an underrun, continue without the
789 // empty primary input stream. 805 // empty primary input stream.
790 input->OnSkipped(); 806 input->OnSkipped();
791 continue; 807 continue;
792 } 808 }
793 809
794 // A primary input cannot provide any data, so wait until later. 810 // A primary input cannot provide any data, so wait until later.
795 retry_write_frames_timer_->Start( 811 retry_write_frames_timer_->Start(
796 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), 812 FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2),
797 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); 813 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this)));
798 return false; 814 return false;
799 } else { 815 } else {
800 input->OnSkipped(); 816 input->OnSkipped();
801 } 817 }
802 } 818 }
803 819
804 if (active_inputs.empty()) { 820 if (is_silence) {
805 // No inputs have any data to provide. Fill with silence to avoid underrun. 821 // No inputs have any data to provide. Push silence to prevent underrun.
806 chunk_size = kPreventUnderrunChunkSize; 822 chunk_size = kPreventUnderrunChunkSize;
807 if (!mixed_ || mixed_->frames() < chunk_size) { 823 ResizeBuffersIfNecessary(chunk_size);
808 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); 824 memset(interleaved_.data(), 0,
809 } 825 static_cast<size_t>(chunk_size * kNumOutputChannels) *
810 826 BytesPerOutputFormatSample());
811 mixed_->Zero(); 827 } else {
812 WriteMixedPcm(*mixed_, chunk_size, true /* is_silence */); 828 ResizeBuffersIfNecessary(chunk_size);
813 return true;
814 } 829 }
815 830
816 // If |mixed_| has not been allocated, or it is too small, allocate a buffer. 831 // If |mixed_|, |temp_|, |interleaved_|, or |interleaved_intermediate_| have
817 if (!mixed_ || mixed_->frames() < chunk_size) { 832 // not been allocated, or are too small, allocate a buffer.
818 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); 833
834 // Mix each group, pass through corresponding audio filter, and
835 // accumulate into |interleaved_|.
836 bool non_zero_data = false;
837 for (int filter_group = 0; filter_group < kNumFilterGroups; ++filter_group) {
838 non_zero_data =
839 non_zero_data ||
840 MixAndFilterGroup(active_inputs[filter_group], filter_group, chunk_size,
841 non_zero_data /* accumulate */);
819 } 842 }
820 843
821 // If |temp_| has not been allocated, or is too small, allocate a buffer. 844 WriteMixedPcm(chunk_size);
822 if (!temp_ || temp_->frames() < chunk_size) { 845 return true;
823 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); 846 }
824 }
825 847
826 mixed_->ZeroFramesPartial(0, chunk_size); 848 bool StreamMixerAlsa::MixAndFilterGroup(
827 849 const std::vector<InputQueue*>& active_inputs,
828 // Loop through active inputs, polling them for data, and mixing them. 850 int filter_group,
829 for (InputQueue* input : active_inputs) { 851 int frames,
830 input->GetResampledData(temp_.get(), chunk_size); 852 bool accumulate) {
831 for (int c = 0; c < kNumOutputChannels; ++c) { 853 // Mix into group buffer, |mixed_|.
832 input->VolumeScaleAccumulate(c, temp_->channel(c), chunk_size, 854 mixed_->ZeroFramesPartial(0, frames);
833 mixed_->channel(c)); 855 bool is_silence = true;
tianyuwang1 2017/02/18 00:17:08 Do you need thi is_silence? Just use active_input
bshaya 2017/02/21 23:30:14 Outdated
856 if (!active_inputs.empty()) {
857 is_silence = false;
858 // Loop through active inputs, polling them for data, and mixing them.
859 for (InputQueue* input : active_inputs) {
860 input->GetResampledData(temp_.get(), frames);
861 for (int c = 0; c < kNumOutputChannels; ++c) {
862 input->VolumeScaleAccumulate(c, temp_->channel(c), frames,
863 mixed_->channel(c));
864 }
834 } 865 }
835 } 866 }
836 867
837 WriteMixedPcm(*mixed_, chunk_size, false /* is_silence */); 868 // If no data has been written to |interleaved_| thus far,
838 return true; 869 // we can write to |interleaved_| directly, skipping a vector addition step.
870 std::vector<uint8_t>* interleaved_group = &interleaved_intermediate_;
871 if (!accumulate) {
872 interleaved_group = &interleaved_;
873 }
874
875 // Convert to interleaved for post-processing.
876 mixed_->ToInterleaved(frames, BytesPerOutputFormatSample(),
877 interleaved_group->data());
878
879 // Ensure that, on onset of silence, at least |kSilenceSecondsToFilter|
880 // second of audio get pushed through the filters to clear any memory.
881 bool filter_frames = true;
tianyuwang1 2017/02/18 00:17:08 filter_frames doesn't seem to change.
bshaya 2017/02/21 23:30:14 Done.
882 if (is_silence) {
tianyuwang1 2017/02/18 00:17:08 move this block up after if (!active_inputs.empty(
bshaya 2017/02/21 23:30:14 Done.
883 int silence_frames_to_filter =
884 output_samples_per_second_ * kSilenceSecondsToFilter;
885 if (silence_frames_filtered_[filter_group] < silence_frames_to_filter) {
886 silence_frames_filtered_[filter_group] += frames;
887 } else {
888 return false; // Output will be silence, no need to mix.
889 }
890 } else {
891 silence_frames_filtered_[filter_group] = 0;
892 }
893
894 // Filter the mixed group.
895 if (pre_loopback_filter_[filter_group] && filter_frames) {
896 pre_loopback_filter_[filter_group]->ProcessInterleaved(
897 interleaved_group->data(), frames);
898 }
899
900 // Exit if we already wrote to |interleaved_|.
901 if (!accumulate) {
902 return filter_frames;
903 }
904
905 // Mix into final output buffer, |interleaved_|
906 DCHECK_EQ(4, BytesPerOutputFormatSample());
907 VectorAccumulate(reinterpret_cast<int32_t*>(interleaved_group->data()),
908 frames * kNumOutputChannels,
909 reinterpret_cast<int32_t*>(interleaved_.data()));
910 return filter_frames;
839 } 911 }
840 912
841 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { 913 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() {
842 return alsa_->PcmFormatSize(pcm_format_, 1); 914 return alsa_->PcmFormatSize(pcm_format_, 1);
843 } 915 }
844 916
845 void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, 917 void StreamMixerAlsa::WriteMixedPcm(int frames) {
846 int frames, bool is_silence) {
847 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); 918 DCHECK(mixer_task_runner_->BelongsToCurrentThread());
848 CHECK_PCM_INITIALIZED(); 919 CHECK_PCM_INITIALIZED();
849 920
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; 921 int64_t expected_playback_time;
857 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { 922 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) {
858 expected_playback_time = kNoTimestamp; 923 expected_playback_time = kNoTimestamp;
859 } else { 924 } else {
860 expected_playback_time = rendering_delay_.timestamp_microseconds + 925 expected_playback_time = rendering_delay_.timestamp_microseconds +
861 rendering_delay_.delay_microseconds; 926 rendering_delay_.delay_microseconds;
862 } 927 }
863 928
864 mixed.ToInterleaved(frames, BytesPerOutputFormatSample(), 929 size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) *
865 interleaved_.data()); 930 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_) { 931 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) {
888 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, 932 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32,
889 output_samples_per_second_, kNumOutputChannels, 933 output_samples_per_second_, kNumOutputChannels,
890 interleaved_.data(), interleaved_size); 934 interleaved_.data(), interleaved_size);
891 } 935 }
892 936
893 if (post_loopback_filter_ && filter_frames) {
894 post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames);
895 }
896
897 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need 937 // 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. 938 // to be prepared in order for playback to work.
899 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { 939 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) {
900 RETURN_REPORT_ERROR(PcmPrepare, pcm_); 940 RETURN_REPORT_ERROR(PcmPrepare, pcm_);
901 } 941 }
902 942
903 int frames_left = frames; 943 int frames_left = frames;
904 uint8_t* data = &interleaved_[0]; 944 uint8_t* data = &interleaved_[0];
905 while (frames_left) { 945 while (frames_left) {
906 int frames_or_error; 946 int frames_or_error;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
954 CastMediaShlib::LoopbackAudioObserver* observer) { 994 CastMediaShlib::LoopbackAudioObserver* observer) {
955 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); 995 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer);
956 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), 996 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(),
957 observer) != loopback_observers_.end()); 997 observer) != loopback_observers_.end());
958 loopback_observers_.erase(std::remove(loopback_observers_.begin(), 998 loopback_observers_.erase(std::remove(loopback_observers_.begin(),
959 loopback_observers_.end(), observer), 999 loopback_observers_.end(), observer),
960 loopback_observers_.end()); 1000 loopback_observers_.end());
961 observer->OnRemoved(); 1001 observer->OnRemoved();
962 } 1002 }
963 1003
1004 void StreamMixerAlsa::ResizeBuffersIfNecessary(int chunk_size) {
1005 if (!mixed_ || mixed_->frames() < chunk_size) {
1006 mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size);
1007 }
1008 if (!temp_ || temp_->frames() < chunk_size) {
1009 temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size);
1010 }
1011
1012 size_t interleaved_size =
1013 static_cast<size_t>(chunk_size * kNumOutputChannels) *
1014 BytesPerOutputFormatSample();
1015 if (interleaved_.size() < interleaved_size) {
1016 interleaved_.resize(interleaved_size);
1017 }
1018 if (interleaved_intermediate_.size() < interleaved_size) {
1019 interleaved_intermediate_.resize(interleaved_size);
1020 }
1021 }
1022
964 } // namespace media 1023 } // namespace media
965 } // namespace chromecast 1024 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698