Chromium Code Reviews| 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" |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 250 AudioFilterFactory::PRE_LOOPBACK_FILTER); | 259 std::unordered_set<std::string>( |
| 251 post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( | 260 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), |
| 252 AudioFilterFactory::POST_LOOPBACK_FILTER); | 261 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER)); |
| 262 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
| 263 std::unordered_set<std::string>({kAlarmAudioDeviceId}), | |
| 264 AudioFilterFactory::ALARM_AUDIO_FILTER)); | |
| 265 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
| 266 std::unordered_set<std::string>({kTtsAudioDeviceId}), | |
| 267 AudioFilterFactory::TTS_AUDIO_FILTER)); | |
| 268 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
| 269 std::unordered_set<std::string>( | |
| 270 {::media::AudioDeviceDescription::kDefaultDeviceId, | |
| 271 kEarconAudioDeviceId}), | |
| 272 AudioFilterFactory::MEDIA_AUDIO_FILTER)); | |
| 253 | 273 |
| 254 DefineAlsaParameters(); | 274 DefineAlsaParameters(); |
| 255 } | 275 } |
| 256 | 276 |
| 257 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 277 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
| 258 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 278 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 259 } | 279 } |
| 260 | 280 |
| 261 void StreamMixerAlsa::DefineAlsaParameters() { | 281 void StreamMixerAlsa::DefineAlsaParameters() { |
| 262 // Get the ALSA output configuration from the command line. | 282 // 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(); | 542 int err = SetAlsaPlaybackParams(); |
| 523 if (err < 0) { | 543 if (err < 0) { |
| 524 LOG(ERROR) << "Error setting ALSA playback parameters: " | 544 LOG(ERROR) << "Error setting ALSA playback parameters: " |
| 525 << alsa_->StrError(err); | 545 << alsa_->StrError(err); |
| 526 SignalError(); | 546 SignalError(); |
| 527 return; | 547 return; |
| 528 } | 548 } |
| 529 } | 549 } |
| 530 | 550 |
| 531 // Initialize filters | 551 // Initialize filters |
| 532 if (pre_loopback_filter_) { | 552 for (auto&& filter_group : filter_groups_) { |
| 533 pre_loopback_filter_->SetSampleRateAndFormat( | 553 filter_group->Initialize(output_samples_per_second_, |
| 534 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | 554 ::media::SampleFormat::kSampleFormatS32); |
| 535 } | |
| 536 | |
| 537 if (post_loopback_filter_) { | |
| 538 post_loopback_filter_->SetSampleRateAndFormat( | |
| 539 output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); | |
| 540 } | 555 } |
| 541 | 556 |
| 542 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 557 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
| 543 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); | 558 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); |
| 544 | 559 |
| 545 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 560 rendering_delay_.timestamp_microseconds = kNoTimestamp; |
| 546 rendering_delay_.delay_microseconds = 0; | 561 rendering_delay_.delay_microseconds = 0; |
| 547 | 562 |
| 548 state_ = kStateNormalPlayback; | 563 state_ = kStateNormalPlayback; |
| 549 } | 564 } |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 628 } | 643 } |
| 629 | 644 |
| 630 void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) { | 645 void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) { |
| 631 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddInput, | 646 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddInput, |
| 632 base::Passed(std::move(input))); | 647 base::Passed(std::move(input))); |
| 633 if (!alsa_) { | 648 if (!alsa_) { |
| 634 alsa_.reset(new AlsaWrapper()); | 649 alsa_.reset(new AlsaWrapper()); |
| 635 } | 650 } |
| 636 | 651 |
| 637 DCHECK(input); | 652 DCHECK(input); |
| 653 | |
| 654 LOG(INFO) << __FUNCTION__; | |
|
kmackay
2017/02/24 18:03:55
Necessary logging?
bshaya
2017/02/27 17:00:15
Done.
| |
| 655 | |
| 638 // If the new input is a primary one, we may need to change the output | 656 // If the new input is a primary one, we may need to change the output |
| 639 // sample rate to match its input sample rate. | 657 // sample rate to match its input sample rate. |
| 640 // We only change the output rate if it is not set to a fixed value. | 658 // We only change the output rate if it is not set to a fixed value. |
| 641 if (input->primary() && | 659 if (input->primary() && |
| 642 fixed_output_samples_per_second_ == kInvalidSampleRate) { | 660 fixed_output_samples_per_second_ == kInvalidSampleRate) { |
| 643 CheckChangeOutputRate(input->input_samples_per_second()); | 661 CheckChangeOutputRate(input->input_samples_per_second()); |
| 644 } | 662 } |
| 645 | 663 |
| 646 check_close_timer_->Stop(); | 664 check_close_timer_->Stop(); |
| 647 if (state_ == kStateUninitialized) { | 665 bool found_filter_group = false; |
|
kmackay
2017/02/24 18:03:55
Move down into the case statement? (you'll need br
bshaya
2017/02/27 17:00:15
Done.
| |
| 648 requested_output_samples_per_second_ = input->input_samples_per_second(); | 666 switch (state_) { |
| 649 Start(); | 667 case kStateUninitialized: |
| 650 input->Initialize(rendering_delay_); | 668 requested_output_samples_per_second_ = input->input_samples_per_second(); |
| 651 inputs_.push_back(std::move(input)); | 669 Start(); |
| 652 } else if (state_ == kStateNormalPlayback) { | 670 // Fallthrough intended |
| 653 input->Initialize(rendering_delay_); | 671 case kStateNormalPlayback: |
| 654 inputs_.push_back(std::move(input)); | 672 input->Initialize(rendering_delay_); |
| 655 } else { | 673 for (auto&& filter_group : filter_groups_) { |
| 656 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); | 674 if (filter_group->CanProcessInput(input.get())) { |
| 657 ignored_inputs_.push_back(std::move(input)); | 675 found_filter_group = true; |
| 676 input->set_filter_group(filter_group.get()); | |
| 677 break; | |
| 678 } | |
| 679 } | |
| 680 DCHECK(found_filter_group) << "Could not find a filter group for " | |
| 681 << input->device_id(); | |
| 682 inputs_.push_back(std::move(input)); | |
| 683 break; | |
| 684 case kStateError: | |
| 685 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); | |
| 686 ignored_inputs_.push_back(std::move(input)); | |
| 687 break; | |
| 688 default: | |
| 689 NOTREACHED(); | |
| 658 } | 690 } |
| 659 } | 691 } |
| 660 | 692 |
| 661 void StreamMixerAlsa::CheckChangeOutputRate(int input_samples_per_second) { | 693 void StreamMixerAlsa::CheckChangeOutputRate(int input_samples_per_second) { |
| 662 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 694 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
| 663 if (!pcm_ || | 695 if (!pcm_ || |
| 664 input_samples_per_second == requested_output_samples_per_second_ || | 696 input_samples_per_second == requested_output_samples_per_second_ || |
| 665 input_samples_per_second == output_samples_per_second_ || | 697 input_samples_per_second == output_samples_per_second_ || |
| 666 input_samples_per_second < static_cast<int>(low_sample_rate_cutoff_)) { | 698 input_samples_per_second < static_cast<int>(low_sample_rate_cutoff_)) { |
| 667 return; | 699 return; |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 755 retry_write_frames_timer_->Stop(); | 787 retry_write_frames_timer_->Stop(); |
| 756 if (TryWriteFrames()) { | 788 if (TryWriteFrames()) { |
| 757 retry_write_frames_timer_->Start( | 789 retry_write_frames_timer_->Start( |
| 758 FROM_HERE, base::TimeDelta(), | 790 FROM_HERE, base::TimeDelta(), |
| 759 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); | 791 base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); |
| 760 } | 792 } |
| 761 } | 793 } |
| 762 | 794 |
| 763 bool StreamMixerAlsa::TryWriteFrames() { | 795 bool StreamMixerAlsa::TryWriteFrames() { |
| 764 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 796 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
| 797 DCHECK_GE(filter_groups_.size(), 1u); | |
| 798 | |
| 765 if (state_ != kStateNormalPlayback) { | 799 if (state_ != kStateNormalPlayback) { |
| 766 return false; | 800 return false; |
| 767 } | 801 } |
| 768 | 802 |
| 769 const int min_frames_in_buffer = | 803 const int min_frames_in_buffer = |
| 770 output_samples_per_second_ * kMinBufferedDataMs / 1000; | 804 output_samples_per_second_ * kMinBufferedDataMs / 1000; |
| 771 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; | 805 int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; |
| 772 std::vector<InputQueue*> active_inputs; | 806 bool is_silence = true; |
| 773 for (auto&& input : inputs_) { | 807 for (auto&& input : inputs_) { |
| 774 int read_size = input->MaxReadSize(); | 808 int read_size = input->MaxReadSize(); |
| 775 if (read_size > 0) { | 809 if (read_size > 0) { |
| 776 active_inputs.push_back(input.get()); | 810 DCHECK(input->filter_group()); |
| 811 input->filter_group()->AddActiveInput(input.get()); | |
| 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 |