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

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

Powered by Google App Engine
This is Rietveld 408576698