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 <unordered_set> |
11 #include <utility> | 11 #include <utility> |
12 | 12 |
13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
15 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
16 #include "base/memory/ptr_util.h" | 16 #include "base/memory/ptr_util.h" |
17 #include "base/memory/weak_ptr.h" | 17 #include "base/memory/weak_ptr.h" |
18 #include "base/numerics/saturated_arithmetic.h" | 18 #include "base/numerics/saturated_arithmetic.h" |
19 #include "base/single_thread_task_runner.h" | 19 #include "base/single_thread_task_runner.h" |
20 #include "base/threading/platform_thread.h" | 20 #include "base/threading/platform_thread.h" |
21 #include "base/threading/thread_task_runner_handle.h" | 21 #include "base/threading/thread_task_runner_handle.h" |
| 22 #include "base/time/time.h" |
22 #include "chromecast/base/chromecast_switches.h" | 23 #include "chromecast/base/chromecast_switches.h" |
23 #include "chromecast/media/base/audio_device_ids.h" | 24 #include "chromecast/media/base/audio_device_ids.h" |
24 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" | 25 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" |
25 #include "chromecast/media/cma/backend/alsa/filter_group.h" | 26 #include "chromecast/media/cma/backend/alsa/filter_group.h" |
26 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h" | 27 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h" |
27 #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" |
28 #include "media/audio/audio_device_description.h" | 29 #include "media/audio/audio_device_description.h" |
29 #include "media/base/audio_bus.h" | 30 #include "media/base/audio_bus.h" |
30 #include "media/base/media_switches.h" | 31 #include "media/base/media_switches.h" |
31 | 32 |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 const int kUseDefaultFade = -1; | 123 const int kUseDefaultFade = -1; |
123 const int kMediaDuckFadeMs = 150; | 124 const int kMediaDuckFadeMs = 150; |
124 const int kMediaUnduckFadeMs = 700; | 125 const int kMediaUnduckFadeMs = 700; |
125 | 126 |
126 int64_t TimespecToMicroseconds(struct timespec time) { | 127 int64_t TimespecToMicroseconds(struct timespec time) { |
127 return static_cast<int64_t>(time.tv_sec) * | 128 return static_cast<int64_t>(time.tv_sec) * |
128 base::Time::kMicrosecondsPerSecond + | 129 base::Time::kMicrosecondsPerSecond + |
129 time.tv_nsec / 1000; | 130 time.tv_nsec / 1000; |
130 } | 131 } |
131 | 132 |
132 void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) { | 133 bool IsOutputDeviceId(const std::string& device) { |
133 for (size_t i = 0; i < size; ++i) { | 134 return device == ::media::AudioDeviceDescription::kDefaultDeviceId || |
134 dest[i] = base::SaturatedAddition(source[i], dest[i]); | 135 device == ::media::AudioDeviceDescription::kCommunicationsDeviceId || |
135 } | 136 device == kLocalAudioDeviceId || device == kAlarmAudioDeviceId || |
| 137 device == kTtsAudioDeviceId; |
136 } | 138 } |
137 | 139 |
138 class StreamMixerAlsaInstance : public StreamMixerAlsa { | 140 class StreamMixerAlsaInstance : public StreamMixerAlsa { |
139 public: | 141 public: |
140 StreamMixerAlsaInstance() {} | 142 StreamMixerAlsaInstance() {} |
141 ~StreamMixerAlsaInstance() override {} | 143 ~StreamMixerAlsaInstance() override {} |
142 | 144 |
143 private: | 145 private: |
144 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); | 146 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); |
145 }; | 147 }; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
210 | 212 |
211 fixed_output_samples_per_second_ = fixed_samples_per_second; | 213 fixed_output_samples_per_second_ = fixed_samples_per_second; |
212 | 214 |
213 low_sample_rate_cutoff_ = | 215 low_sample_rate_cutoff_ = |
214 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) | 216 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) |
215 ? kLowSampleRateCutoff | 217 ? kLowSampleRateCutoff |
216 : 0; | 218 : 0; |
217 | 219 |
218 // Read post-processing configuration file | 220 // Read post-processing configuration file |
219 PostProcessingPipelineParser pipeline_parser; | 221 PostProcessingPipelineParser pipeline_parser; |
220 pipeline_parser.Initialize(); | |
221 | 222 |
222 // Media filter group: | 223 CreatePostProcessors(&pipeline_parser); |
223 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
224 std::unordered_set<std::string>( | |
225 {::media::AudioDeviceDescription::kDefaultDeviceId, | |
226 kLocalAudioDeviceId, "", kAlarmAudioDeviceId}), | |
227 AudioContentType::kMedia, kNumOutputChannels, | |
228 pipeline_parser.GetPipelineByDeviceId( | |
229 ::media::AudioDeviceDescription::kDefaultDeviceId))); | |
230 | |
231 // Voice filter group: | |
232 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | |
233 std::unordered_set<std::string>( | |
234 {kTtsAudioDeviceId, | |
235 ::media::AudioDeviceDescription::kCommunicationsDeviceId}), | |
236 AudioContentType::kCommunication, kNumOutputChannels, | |
237 pipeline_parser.GetPipelineByDeviceId(kTtsAudioDeviceId))); | |
238 | 224 |
239 // TODO(bshaya): Add support for final mix AudioPostProcessor. | 225 // TODO(bshaya): Add support for final mix AudioPostProcessor. |
240 DefineAlsaParameters(); | 226 DefineAlsaParameters(); |
241 } | 227 } |
242 | 228 |
| 229 void StreamMixerAlsa::CreatePostProcessors( |
| 230 PostProcessingPipelineParser* pipeline_parser, |
| 231 bool is_test) { |
| 232 std::unordered_set<std::string> used_streams; |
| 233 |
| 234 for (auto& stream_pipeline : pipeline_parser->GetStreamPipelines()) { |
| 235 const auto& device_ids = stream_pipeline.stream_types; |
| 236 for (const std::string& stream_type : device_ids) { |
| 237 if (!is_test) { |
| 238 CHECK(IsOutputDeviceId(stream_type)) |
| 239 << stream_type << " is not a stream type. Stream types are listed " |
| 240 << "in chromecast/media/base/audio_device_ids.cc and " |
| 241 << "media/audio/audio_device_description.cc"; |
| 242 } |
| 243 CHECK(used_streams.insert(stream_type).second) |
| 244 << "Multiple instances of stream type '" << stream_type << "' in " |
| 245 << pipeline_parser->file_path() << "."; |
| 246 } |
| 247 filter_groups_.push_back( |
| 248 base::MakeUnique<FilterGroup>(kNumOutputChannels, *device_ids.begin(), |
| 249 stream_pipeline.pipeline, device_ids)); |
| 250 if (device_ids.find(::media::AudioDeviceDescription::kDefaultDeviceId) != |
| 251 device_ids.end()) { |
| 252 default_filter_ = filter_groups_.back().get(); |
| 253 } |
| 254 } |
| 255 |
| 256 std::vector<FilterGroup*> filter_group_ptrs(filter_groups_.size()); |
| 257 std::transform( |
| 258 filter_groups_.begin(), filter_groups_.end(), filter_group_ptrs.begin(), |
| 259 [](const std::unique_ptr<FilterGroup>& group) { return group.get(); }); |
| 260 |
| 261 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 262 kNumOutputChannels, "mix", pipeline_parser->GetMixPipeline(), |
| 263 filter_group_ptrs)); |
| 264 mix_filter_ = filter_groups_.back().get(); |
| 265 |
| 266 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 267 kNumOutputChannels, "linearize", pipeline_parser->GetLinearizePipeline(), |
| 268 std::vector<FilterGroup*>({mix_filter_}))); |
| 269 linearize_filter_ = filter_groups_.back().get(); |
| 270 } |
| 271 |
243 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 272 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
244 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 273 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
245 } | 274 } |
246 | 275 |
| 276 void StreamMixerAlsa::ResetPostProcessorsForTest( |
| 277 const std::string& pipeline_json) { |
| 278 LOG(INFO) << __FUNCTION__ << " disregard previous PostProcessor messages."; |
| 279 filter_groups_.clear(); |
| 280 PostProcessingPipelineParser parser(pipeline_json); |
| 281 CreatePostProcessors(&parser, true /* is_test */); |
| 282 } |
| 283 |
247 void StreamMixerAlsa::DefineAlsaParameters() { | 284 void StreamMixerAlsa::DefineAlsaParameters() { |
248 // Get the ALSA output configuration from the command line. | 285 // Get the ALSA output configuration from the command line. |
249 alsa_buffer_size_ = GetSwitchValueNonNegativeInt( | 286 alsa_buffer_size_ = GetSwitchValueNonNegativeInt( |
250 switches::kAlsaOutputBufferSize, kDefaultOutputBufferSizeFrames); | 287 switches::kAlsaOutputBufferSize, kDefaultOutputBufferSizeFrames); |
251 | 288 |
252 alsa_period_size_ = GetSwitchValueNonNegativeInt( | 289 alsa_period_size_ = GetSwitchValueNonNegativeInt( |
253 switches::kAlsaOutputPeriodSize, alsa_buffer_size_ / 16); | 290 switches::kAlsaOutputPeriodSize, alsa_buffer_size_ / 16); |
254 if (alsa_period_size_ >= alsa_buffer_size_) { | 291 if (alsa_period_size_ >= alsa_buffer_size_) { |
255 LOG(DFATAL) << "ALSA period size must be smaller than the buffer size"; | 292 LOG(DFATAL) << "ALSA period size must be smaller than the buffer size"; |
256 alsa_period_size_ = alsa_buffer_size_ / 2; | 293 alsa_period_size_ = alsa_buffer_size_ / 2; |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
501 } | 538 } |
502 | 539 |
503 // Initialize filters | 540 // Initialize filters |
504 for (auto&& filter_group : filter_groups_) { | 541 for (auto&& filter_group : filter_groups_) { |
505 filter_group->Initialize(output_samples_per_second_); | 542 filter_group->Initialize(output_samples_per_second_); |
506 } | 543 } |
507 | 544 |
508 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 545 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
509 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); | 546 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); |
510 | 547 |
511 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 548 alsa_rendering_delay_.timestamp_microseconds = kNoTimestamp; |
512 rendering_delay_.delay_microseconds = 0; | 549 alsa_rendering_delay_.delay_microseconds = 0; |
513 | 550 |
514 state_ = kStateNormalPlayback; | 551 state_ = kStateNormalPlayback; |
515 } | 552 } |
516 | 553 |
517 void StreamMixerAlsa::Stop() { | 554 void StreamMixerAlsa::Stop() { |
518 for (auto* observer : loopback_observers_) { | 555 for (auto* observer : loopback_observers_) { |
519 observer->OnLoopbackInterrupted(); | 556 observer->OnLoopbackInterrupted(); |
520 } | 557 } |
521 | 558 |
522 if (alsa_) { | 559 if (alsa_) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 | 613 |
577 void StreamMixerAlsa::SetAlsaWrapperForTest( | 614 void StreamMixerAlsa::SetAlsaWrapperForTest( |
578 std::unique_ptr<AlsaWrapper> alsa_wrapper) { | 615 std::unique_ptr<AlsaWrapper> alsa_wrapper) { |
579 if (alsa_) { | 616 if (alsa_) { |
580 Close(); | 617 Close(); |
581 } | 618 } |
582 | 619 |
583 alsa_ = std::move(alsa_wrapper); | 620 alsa_ = std::move(alsa_wrapper); |
584 } | 621 } |
585 | 622 |
586 void StreamMixerAlsa::DisablePostProcessingForTest() { | |
587 for (auto& filter : filter_groups_) { | |
588 filter->DisablePostProcessingForTest(); | |
589 } | |
590 } | |
591 | |
592 void StreamMixerAlsa::WriteFramesForTest() { | 623 void StreamMixerAlsa::WriteFramesForTest() { |
593 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::WriteFramesForTest); | 624 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::WriteFramesForTest); |
594 WriteFrames(); | 625 WriteFrames(); |
595 } | 626 } |
596 | 627 |
597 void StreamMixerAlsa::ClearInputsForTest() { | 628 void StreamMixerAlsa::ClearInputsForTest() { |
598 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::ClearInputsForTest); | 629 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::ClearInputsForTest); |
599 inputs_.clear(); | 630 inputs_.clear(); |
600 } | 631 } |
601 | 632 |
(...skipping 24 matching lines...) Expand all Loading... |
626 input->SetMuted(volume_info_[type].muted); | 657 input->SetMuted(volume_info_[type].muted); |
627 | 658 |
628 check_close_timer_->Stop(); | 659 check_close_timer_->Stop(); |
629 switch (state_) { | 660 switch (state_) { |
630 case kStateUninitialized: | 661 case kStateUninitialized: |
631 requested_output_samples_per_second_ = input->input_samples_per_second(); | 662 requested_output_samples_per_second_ = input->input_samples_per_second(); |
632 Start(); | 663 Start(); |
633 // Fallthrough intended | 664 // Fallthrough intended |
634 case kStateNormalPlayback: { | 665 case kStateNormalPlayback: { |
635 bool found_filter_group = false; | 666 bool found_filter_group = false; |
636 input->Initialize(rendering_delay_); | 667 input->Initialize(alsa_rendering_delay_); |
637 for (auto&& filter_group : filter_groups_) { | 668 for (auto&& filter_group : filter_groups_) { |
638 if (filter_group->CanProcessInput(input.get())) { | 669 if (filter_group->CanProcessInput(input.get())) { |
639 found_filter_group = true; | 670 found_filter_group = true; |
640 input->set_filter_group(filter_group.get()); | 671 input->set_filter_group(filter_group.get()); |
| 672 LOG(INFO) << "Added input of type " << input->device_id() << " to " |
| 673 << filter_group->name(); |
641 break; | 674 break; |
642 } | 675 } |
643 } | 676 } |
644 DCHECK(found_filter_group) << "Could not find a filter group for " | 677 |
645 << input->device_id(); | 678 // Fallback to default_filter_ if provided |
| 679 if (!found_filter_group && default_filter_) { |
| 680 found_filter_group = true; |
| 681 input->set_filter_group(default_filter_); |
| 682 LOG(INFO) << "Added input of type " << input->device_id() << " to " |
| 683 << default_filter_->name(); |
| 684 } |
| 685 |
| 686 CHECK(found_filter_group) |
| 687 << "Could not find a filter group for " << input->device_id() << "\n" |
| 688 << "(consider adding a 'default' processor)"; |
646 inputs_.push_back(std::move(input)); | 689 inputs_.push_back(std::move(input)); |
647 } break; | 690 } break; |
648 case kStateError: | 691 case kStateError: |
649 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); | 692 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); |
650 ignored_inputs_.push_back(std::move(input)); | 693 ignored_inputs_.push_back(std::move(input)); |
651 break; | 694 break; |
652 default: | 695 default: |
653 NOTREACHED(); | 696 NOTREACHED(); |
654 } | 697 } |
655 } | 698 } |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 } else { | 845 } else { |
803 input->OnSkipped(); | 846 input->OnSkipped(); |
804 } | 847 } |
805 } | 848 } |
806 | 849 |
807 if (is_silence) { | 850 if (is_silence) { |
808 // No inputs have any data to provide. Push silence to prevent underrun. | 851 // No inputs have any data to provide. Push silence to prevent underrun. |
809 chunk_size = kPreventUnderrunChunkSize; | 852 chunk_size = kPreventUnderrunChunkSize; |
810 } | 853 } |
811 | 854 |
812 // Mix and filter each group. | 855 // Recursively mix and filter each group. |
813 std::vector<uint8_t>* interleaved = nullptr; | 856 linearize_filter_->MixAndFilter(chunk_size); |
814 for (auto&& filter_group : filter_groups_) { | |
815 if (filter_group->MixAndFilter(chunk_size)) { | |
816 if (!interleaved) { | |
817 interleaved = filter_group->GetInterleaved(); | |
818 } else { | |
819 DCHECK_EQ(4, BytesPerOutputFormatSample()); | |
820 VectorAccumulate( | |
821 reinterpret_cast<int32_t*>(filter_group->GetInterleaved()->data()), | |
822 chunk_size * kNumOutputChannels, | |
823 reinterpret_cast<int32_t*>(interleaved->data())); | |
824 } | |
825 } | |
826 } | |
827 | 857 |
828 if (!interleaved) { | 858 WriteMixedPcm(chunk_size); |
829 // No group has any data, write empty buffer. | |
830 filter_groups_[0]->ClearInterleaved(chunk_size); | |
831 interleaved = filter_groups_[0]->GetInterleaved(); | |
832 } | |
833 | |
834 WriteMixedPcm(interleaved, chunk_size); | |
835 return true; | 859 return true; |
836 } | 860 } |
837 | 861 |
838 size_t StreamMixerAlsa::InterleavedSize(int frames) { | 862 size_t StreamMixerAlsa::InterleavedSize(int frames) { |
839 return BytesPerOutputFormatSample() * | 863 return BytesPerOutputFormatSample() * |
840 static_cast<size_t>(frames * kNumOutputChannels); | 864 static_cast<size_t>(frames * kNumOutputChannels); |
841 } | 865 } |
842 | 866 |
843 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { | 867 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { |
844 return alsa_->PcmFormatSize(pcm_format_, 1); | 868 return alsa_->PcmFormatSize(pcm_format_, 1); |
845 } | 869 } |
846 | 870 |
847 void StreamMixerAlsa::WriteMixedPcm(std::vector<uint8_t>* interleaved, | 871 void StreamMixerAlsa::WriteMixedPcm(int frames) { |
848 int frames) { | |
849 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 872 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
850 CHECK_PCM_INITIALIZED(); | 873 CHECK_PCM_INITIALIZED(); |
851 DCHECK(interleaved); | 874 |
852 DCHECK_GE(interleaved->size(), InterleavedSize(frames)); | 875 // Resize interleaved if necessary. |
| 876 size_t interleaved_size = static_cast<size_t>(frames) * kNumOutputChannels * |
| 877 BytesPerOutputFormatSample(); |
| 878 if (interleaved_.size() < interleaved_size) { |
| 879 interleaved_.resize(interleaved_size); |
| 880 } |
| 881 |
| 882 // Get data for loopback. |
| 883 mix_filter_->data()->ToInterleaved(frames, BytesPerOutputFormatSample(), |
| 884 interleaved_.data()); |
853 | 885 |
854 int64_t expected_playback_time; | 886 int64_t expected_playback_time; |
855 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { | 887 if (alsa_rendering_delay_.timestamp_microseconds == kNoTimestamp) { |
856 expected_playback_time = kNoTimestamp; | 888 expected_playback_time = kNoTimestamp; |
857 } else { | 889 } else { |
858 expected_playback_time = rendering_delay_.timestamp_microseconds + | 890 expected_playback_time = alsa_rendering_delay_.timestamp_microseconds + |
859 rendering_delay_.delay_microseconds; | 891 alsa_rendering_delay_.delay_microseconds + |
| 892 linearize_filter_->GetRenderingDelayMicroseconds(); |
860 } | 893 } |
861 | 894 |
862 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { | 895 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { |
863 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, | 896 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, |
864 output_samples_per_second_, kNumOutputChannels, | 897 output_samples_per_second_, kNumOutputChannels, |
865 interleaved->data(), InterleavedSize(frames)); | 898 interleaved_.data(), InterleavedSize(frames)); |
866 } | 899 } |
867 | 900 |
| 901 // Get data for playout. |
| 902 linearize_filter_->data()->ToInterleaved(frames, BytesPerOutputFormatSample(), |
| 903 interleaved_.data()); |
| 904 |
868 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need | 905 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need |
869 // to be prepared in order for playback to work. | 906 // to be prepared in order for playback to work. |
870 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { | 907 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { |
871 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 908 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
872 } | 909 } |
873 | 910 |
874 int frames_left = frames; | 911 int frames_left = frames; |
875 uint8_t* data = interleaved->data(); | 912 uint8_t* data = interleaved_.data(); |
876 while (frames_left) { | 913 while (frames_left) { |
877 int frames_or_error; | 914 int frames_or_error; |
878 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { | 915 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { |
879 for (auto* observer : loopback_observers_) { | 916 for (auto* observer : loopback_observers_) { |
880 observer->OnLoopbackInterrupted(); | 917 observer->OnLoopbackInterrupted(); |
881 } | 918 } |
882 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, | 919 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, |
883 kPcmRecoverIsSilent); | 920 kPcmRecoverIsSilent); |
884 } | 921 } |
885 frames_left -= frames_or_error; | 922 frames_left -= frames_or_error; |
886 DCHECK_GE(frames_left, 0); | 923 DCHECK_GE(frames_left, 0); |
887 data += frames_or_error * kNumOutputChannels * BytesPerOutputFormatSample(); | 924 data += frames_or_error * kNumOutputChannels * BytesPerOutputFormatSample(); |
888 } | 925 } |
889 UpdateRenderingDelay(frames); | 926 UpdateRenderingDelay(frames); |
890 for (auto&& input : inputs_) | 927 MediaPipelineBackendAlsa::RenderingDelay common_rendering_delay = |
891 input->AfterWriteFrames(rendering_delay_); | 928 alsa_rendering_delay_; |
| 929 common_rendering_delay.delay_microseconds += |
| 930 linearize_filter_->GetRenderingDelayMicroseconds() + |
| 931 mix_filter_->GetRenderingDelayMicroseconds(); |
| 932 for (auto&& input : inputs_) { |
| 933 MediaPipelineBackendAlsa::RenderingDelay stream_rendering_delay = |
| 934 common_rendering_delay; |
| 935 stream_rendering_delay.delay_microseconds += |
| 936 input->filter_group()->GetRenderingDelayMicroseconds(); |
| 937 input->AfterWriteFrames(stream_rendering_delay); |
| 938 } |
892 } | 939 } |
893 | 940 |
894 void StreamMixerAlsa::UpdateRenderingDelay(int newly_pushed_frames) { | 941 void StreamMixerAlsa::UpdateRenderingDelay(int newly_pushed_frames) { |
895 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 942 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
896 CHECK_PCM_INITIALIZED(); | 943 CHECK_PCM_INITIALIZED(); |
897 | 944 |
898 // TODO(bshaya): Add rendering delay from post-processors. | 945 // TODO(bshaya): Add rendering delay from post-processors. |
899 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0 || | 946 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0 || |
900 alsa_->PcmStatusGetState(pcm_status_) != SND_PCM_STATE_RUNNING) { | 947 alsa_->PcmStatusGetState(pcm_status_) != SND_PCM_STATE_RUNNING) { |
901 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 948 alsa_rendering_delay_.timestamp_microseconds = kNoTimestamp; |
902 rendering_delay_.delay_microseconds = 0; | 949 alsa_rendering_delay_.delay_microseconds = 0; |
903 return; | 950 return; |
904 } | 951 } |
905 | 952 |
906 snd_htimestamp_t status_timestamp = {}; | 953 snd_htimestamp_t status_timestamp = {}; |
907 alsa_->PcmStatusGetHtstamp(pcm_status_, &status_timestamp); | 954 alsa_->PcmStatusGetHtstamp(pcm_status_, &status_timestamp); |
908 rendering_delay_.timestamp_microseconds = | 955 alsa_rendering_delay_.timestamp_microseconds = |
909 TimespecToMicroseconds(status_timestamp); | 956 TimespecToMicroseconds(status_timestamp); |
910 snd_pcm_sframes_t delay_frames = alsa_->PcmStatusGetDelay(pcm_status_); | 957 snd_pcm_sframes_t delay_frames = alsa_->PcmStatusGetDelay(pcm_status_); |
911 rendering_delay_.delay_microseconds = static_cast<int64_t>(delay_frames) * | 958 alsa_rendering_delay_.delay_microseconds = |
912 base::Time::kMicrosecondsPerSecond / | 959 static_cast<int64_t>(delay_frames) * base::Time::kMicrosecondsPerSecond / |
913 output_samples_per_second_; | 960 output_samples_per_second_; |
914 } | 961 } |
915 | 962 |
916 void StreamMixerAlsa::AddLoopbackAudioObserver( | 963 void StreamMixerAlsa::AddLoopbackAudioObserver( |
917 CastMediaShlib::LoopbackAudioObserver* observer) { | 964 CastMediaShlib::LoopbackAudioObserver* observer) { |
918 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddLoopbackAudioObserver, observer); | 965 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddLoopbackAudioObserver, observer); |
919 DCHECK(observer); | 966 DCHECK(observer); |
920 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 967 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
921 observer) == loopback_observers_.end()); | 968 observer) == loopback_observers_.end()); |
922 loopback_observers_.push_back(observer); | 969 loopback_observers_.push_back(observer); |
923 } | 970 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
976 for (auto&& input : inputs_) { | 1023 for (auto&& input : inputs_) { |
977 // Volume limits don't apply to effects streams. | 1024 // Volume limits don't apply to effects streams. |
978 if (input->primary() && input->content_type() == type) { | 1025 if (input->primary() && input->content_type() == type) { |
979 input->SetContentTypeVolume(effective_volume, fade_ms); | 1026 input->SetContentTypeVolume(effective_volume, fade_ms); |
980 } | 1027 } |
981 } | 1028 } |
982 } | 1029 } |
983 | 1030 |
984 } // namespace media | 1031 } // namespace media |
985 } // namespace chromecast | 1032 } // namespace chromecast |
OLD | NEW |