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 std::unordered_set<std::string> used_streams; |
| 232 for (auto& stream_pipeline : pipeline_parser->GetStreamPipelines()) { |
| 233 const auto& device_ids = stream_pipeline.stream_types; |
| 234 for (const std::string& stream_type : device_ids) { |
| 235 CHECK(IsOutputDeviceId(stream_type)) |
| 236 << stream_type << " is not a stream type. Stream types are listed " |
| 237 << "in chromecast/media/base/audio_device_ids.cc and " |
| 238 << "media/audio/audio_device_description.cc"; |
| 239 CHECK(used_streams.insert(stream_type).second) |
| 240 << "Multiple instances of stream type '" << stream_type << "' in " |
| 241 << pipeline_parser->GetFilePath() << "."; |
| 242 } |
| 243 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 244 kNumOutputChannels, *device_ids.begin() /* name */, |
| 245 stream_pipeline.pipeline, device_ids, |
| 246 std::vector<FilterGroup*>() /* mixed_inputs */)); |
| 247 if (device_ids.find(::media::AudioDeviceDescription::kDefaultDeviceId) != |
| 248 device_ids.end()) { |
| 249 default_filter_ = filter_groups_.back().get(); |
| 250 } |
| 251 } |
| 252 |
| 253 // Always provide a default filter; OEM may only specify mix filter. |
| 254 if (!default_filter_) { |
| 255 std::string kDefaultDeviceId = |
| 256 ::media::AudioDeviceDescription::kDefaultDeviceId; |
| 257 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 258 kNumOutputChannels, kDefaultDeviceId /* name */, nullptr, |
| 259 std::unordered_set<std::string>({kDefaultDeviceId}), |
| 260 std::vector<FilterGroup*>() /* mixed_inputs */)); |
| 261 default_filter_ = filter_groups_.back().get(); |
| 262 } |
| 263 |
| 264 std::vector<FilterGroup*> filter_group_ptrs(filter_groups_.size()); |
| 265 std::transform( |
| 266 filter_groups_.begin(), filter_groups_.end(), filter_group_ptrs.begin(), |
| 267 [](const std::unique_ptr<FilterGroup>& group) { return group.get(); }); |
| 268 |
| 269 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 270 kNumOutputChannels, "mix", pipeline_parser->GetMixPipeline(), |
| 271 std::unordered_set<std::string>() /* device_ids */, filter_group_ptrs)); |
| 272 mix_filter_ = filter_groups_.back().get(); |
| 273 |
| 274 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 275 kNumOutputChannels, "linearize", pipeline_parser->GetLinearizePipeline(), |
| 276 std::unordered_set<std::string>() /* device_ids */, |
| 277 std::vector<FilterGroup*>({mix_filter_}))); |
| 278 linearize_filter_ = filter_groups_.back().get(); |
| 279 } |
| 280 |
243 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 281 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
244 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 282 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
245 } | 283 } |
246 | 284 |
| 285 void StreamMixerAlsa::ResetPostProcessorsForTest( |
| 286 const std::string& pipeline_json) { |
| 287 LOG(INFO) << __FUNCTION__ << " disregard previous PostProcessor messages."; |
| 288 filter_groups_.clear(); |
| 289 default_filter_ = nullptr; |
| 290 PostProcessingPipelineParser parser(pipeline_json); |
| 291 CreatePostProcessors(&parser); |
| 292 } |
| 293 |
247 void StreamMixerAlsa::DefineAlsaParameters() { | 294 void StreamMixerAlsa::DefineAlsaParameters() { |
248 // Get the ALSA output configuration from the command line. | 295 // Get the ALSA output configuration from the command line. |
249 alsa_buffer_size_ = GetSwitchValueNonNegativeInt( | 296 alsa_buffer_size_ = GetSwitchValueNonNegativeInt( |
250 switches::kAlsaOutputBufferSize, kDefaultOutputBufferSizeFrames); | 297 switches::kAlsaOutputBufferSize, kDefaultOutputBufferSizeFrames); |
251 | 298 |
252 alsa_period_size_ = GetSwitchValueNonNegativeInt( | 299 alsa_period_size_ = GetSwitchValueNonNegativeInt( |
253 switches::kAlsaOutputPeriodSize, alsa_buffer_size_ / 16); | 300 switches::kAlsaOutputPeriodSize, alsa_buffer_size_ / 16); |
254 if (alsa_period_size_ >= alsa_buffer_size_) { | 301 if (alsa_period_size_ >= alsa_buffer_size_) { |
255 LOG(DFATAL) << "ALSA period size must be smaller than the buffer size"; | 302 LOG(DFATAL) << "ALSA period size must be smaller than the buffer size"; |
256 alsa_period_size_ = alsa_buffer_size_ / 2; | 303 alsa_period_size_ = alsa_buffer_size_ / 2; |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
501 } | 548 } |
502 | 549 |
503 // Initialize filters | 550 // Initialize filters |
504 for (auto&& filter_group : filter_groups_) { | 551 for (auto&& filter_group : filter_groups_) { |
505 filter_group->Initialize(output_samples_per_second_); | 552 filter_group->Initialize(output_samples_per_second_); |
506 } | 553 } |
507 | 554 |
508 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 555 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
509 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); | 556 RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); |
510 | 557 |
511 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 558 alsa_rendering_delay_.timestamp_microseconds = kNoTimestamp; |
512 rendering_delay_.delay_microseconds = 0; | 559 alsa_rendering_delay_.delay_microseconds = 0; |
513 | 560 |
514 state_ = kStateNormalPlayback; | 561 state_ = kStateNormalPlayback; |
515 } | 562 } |
516 | 563 |
517 void StreamMixerAlsa::Stop() { | 564 void StreamMixerAlsa::Stop() { |
518 for (auto* observer : loopback_observers_) { | 565 for (auto* observer : loopback_observers_) { |
519 observer->OnLoopbackInterrupted(); | 566 observer->OnLoopbackInterrupted(); |
520 } | 567 } |
521 | 568 |
522 if (alsa_) { | 569 if (alsa_) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 | 623 |
577 void StreamMixerAlsa::SetAlsaWrapperForTest( | 624 void StreamMixerAlsa::SetAlsaWrapperForTest( |
578 std::unique_ptr<AlsaWrapper> alsa_wrapper) { | 625 std::unique_ptr<AlsaWrapper> alsa_wrapper) { |
579 if (alsa_) { | 626 if (alsa_) { |
580 Close(); | 627 Close(); |
581 } | 628 } |
582 | 629 |
583 alsa_ = std::move(alsa_wrapper); | 630 alsa_ = std::move(alsa_wrapper); |
584 } | 631 } |
585 | 632 |
586 void StreamMixerAlsa::DisablePostProcessingForTest() { | |
587 for (auto& filter : filter_groups_) { | |
588 filter->DisablePostProcessingForTest(); | |
589 } | |
590 } | |
591 | |
592 void StreamMixerAlsa::WriteFramesForTest() { | 633 void StreamMixerAlsa::WriteFramesForTest() { |
593 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::WriteFramesForTest); | 634 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::WriteFramesForTest); |
594 WriteFrames(); | 635 WriteFrames(); |
595 } | 636 } |
596 | 637 |
597 void StreamMixerAlsa::ClearInputsForTest() { | 638 void StreamMixerAlsa::ClearInputsForTest() { |
598 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::ClearInputsForTest); | 639 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::ClearInputsForTest); |
599 inputs_.clear(); | 640 inputs_.clear(); |
600 } | 641 } |
601 | 642 |
(...skipping 24 matching lines...) Expand all Loading... |
626 input->SetMuted(volume_info_[type].muted); | 667 input->SetMuted(volume_info_[type].muted); |
627 | 668 |
628 check_close_timer_->Stop(); | 669 check_close_timer_->Stop(); |
629 switch (state_) { | 670 switch (state_) { |
630 case kStateUninitialized: | 671 case kStateUninitialized: |
631 requested_output_samples_per_second_ = input->input_samples_per_second(); | 672 requested_output_samples_per_second_ = input->input_samples_per_second(); |
632 Start(); | 673 Start(); |
633 // Fallthrough intended | 674 // Fallthrough intended |
634 case kStateNormalPlayback: { | 675 case kStateNormalPlayback: { |
635 bool found_filter_group = false; | 676 bool found_filter_group = false; |
636 input->Initialize(rendering_delay_); | 677 input->Initialize(alsa_rendering_delay_); |
637 for (auto&& filter_group : filter_groups_) { | 678 for (auto&& filter_group : filter_groups_) { |
638 if (filter_group->CanProcessInput(input.get())) { | 679 if (filter_group->CanProcessInput(input.get())) { |
639 found_filter_group = true; | 680 found_filter_group = true; |
640 input->set_filter_group(filter_group.get()); | 681 input->set_filter_group(filter_group.get()); |
| 682 LOG(INFO) << "Added input of type " << input->device_id() << " to " |
| 683 << filter_group->name(); |
641 break; | 684 break; |
642 } | 685 } |
643 } | 686 } |
644 DCHECK(found_filter_group) << "Could not find a filter group for " | 687 |
645 << input->device_id(); | 688 // Fallback to default_filter_ if provided |
| 689 if (!found_filter_group && default_filter_) { |
| 690 found_filter_group = true; |
| 691 input->set_filter_group(default_filter_); |
| 692 LOG(INFO) << "Added input of type " << input->device_id() << " to " |
| 693 << default_filter_->name(); |
| 694 } |
| 695 |
| 696 CHECK(found_filter_group) |
| 697 << "Could not find a filter group for " << input->device_id() << "\n" |
| 698 << "(consider adding a 'default' processor)"; |
646 inputs_.push_back(std::move(input)); | 699 inputs_.push_back(std::move(input)); |
647 } break; | 700 } break; |
648 case kStateError: | 701 case kStateError: |
649 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); | 702 input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError); |
650 ignored_inputs_.push_back(std::move(input)); | 703 ignored_inputs_.push_back(std::move(input)); |
651 break; | 704 break; |
652 default: | 705 default: |
653 NOTREACHED(); | 706 NOTREACHED(); |
654 } | 707 } |
655 } | 708 } |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 } else { | 855 } else { |
803 input->OnSkipped(); | 856 input->OnSkipped(); |
804 } | 857 } |
805 } | 858 } |
806 | 859 |
807 if (is_silence) { | 860 if (is_silence) { |
808 // No inputs have any data to provide. Push silence to prevent underrun. | 861 // No inputs have any data to provide. Push silence to prevent underrun. |
809 chunk_size = kPreventUnderrunChunkSize; | 862 chunk_size = kPreventUnderrunChunkSize; |
810 } | 863 } |
811 | 864 |
812 // Mix and filter each group. | 865 // Recursively mix and filter each group. |
813 std::vector<uint8_t>* interleaved = nullptr; | 866 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 | 867 |
828 if (!interleaved) { | 868 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; | 869 return true; |
836 } | 870 } |
837 | 871 |
838 size_t StreamMixerAlsa::InterleavedSize(int frames) { | 872 size_t StreamMixerAlsa::InterleavedSize(int frames) { |
839 return BytesPerOutputFormatSample() * | 873 return BytesPerOutputFormatSample() * |
840 static_cast<size_t>(frames * kNumOutputChannels); | 874 static_cast<size_t>(frames * kNumOutputChannels); |
841 } | 875 } |
842 | 876 |
843 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { | 877 ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() { |
844 return alsa_->PcmFormatSize(pcm_format_, 1); | 878 return alsa_->PcmFormatSize(pcm_format_, 1); |
845 } | 879 } |
846 | 880 |
847 void StreamMixerAlsa::WriteMixedPcm(std::vector<uint8_t>* interleaved, | 881 void StreamMixerAlsa::WriteMixedPcm(int frames) { |
848 int frames) { | |
849 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 882 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
850 CHECK_PCM_INITIALIZED(); | 883 CHECK_PCM_INITIALIZED(); |
851 DCHECK(interleaved); | 884 |
852 DCHECK_GE(interleaved->size(), InterleavedSize(frames)); | 885 // Resize interleaved if necessary. |
| 886 size_t interleaved_size = static_cast<size_t>(frames) * kNumOutputChannels * |
| 887 BytesPerOutputFormatSample(); |
| 888 if (interleaved_.size() < interleaved_size) { |
| 889 interleaved_.resize(interleaved_size); |
| 890 } |
| 891 |
| 892 // Get data for loopback. |
| 893 mix_filter_->data()->ToInterleaved(frames, BytesPerOutputFormatSample(), |
| 894 interleaved_.data()); |
853 | 895 |
854 int64_t expected_playback_time; | 896 int64_t expected_playback_time; |
855 if (rendering_delay_.timestamp_microseconds == kNoTimestamp) { | 897 if (alsa_rendering_delay_.timestamp_microseconds == kNoTimestamp) { |
856 expected_playback_time = kNoTimestamp; | 898 expected_playback_time = kNoTimestamp; |
857 } else { | 899 } else { |
858 expected_playback_time = rendering_delay_.timestamp_microseconds + | 900 expected_playback_time = alsa_rendering_delay_.timestamp_microseconds + |
859 rendering_delay_.delay_microseconds; | 901 alsa_rendering_delay_.delay_microseconds + |
| 902 linearize_filter_->GetRenderingDelayMicroseconds(); |
860 } | 903 } |
861 | 904 |
862 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { | 905 for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { |
863 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, | 906 observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, |
864 output_samples_per_second_, kNumOutputChannels, | 907 output_samples_per_second_, kNumOutputChannels, |
865 interleaved->data(), InterleavedSize(frames)); | 908 interleaved_.data(), InterleavedSize(frames)); |
866 } | 909 } |
867 | 910 |
| 911 // Get data for playout. |
| 912 linearize_filter_->data()->ToInterleaved(frames, BytesPerOutputFormatSample(), |
| 913 interleaved_.data()); |
| 914 |
868 // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need | 915 // 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. | 916 // to be prepared in order for playback to work. |
870 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { | 917 if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { |
871 RETURN_REPORT_ERROR(PcmPrepare, pcm_); | 918 RETURN_REPORT_ERROR(PcmPrepare, pcm_); |
872 } | 919 } |
873 | 920 |
874 int frames_left = frames; | 921 int frames_left = frames; |
875 uint8_t* data = interleaved->data(); | 922 uint8_t* data = interleaved_.data(); |
876 while (frames_left) { | 923 while (frames_left) { |
877 int frames_or_error; | 924 int frames_or_error; |
878 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { | 925 while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { |
879 for (auto* observer : loopback_observers_) { | 926 for (auto* observer : loopback_observers_) { |
880 observer->OnLoopbackInterrupted(); | 927 observer->OnLoopbackInterrupted(); |
881 } | 928 } |
882 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, | 929 RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, |
883 kPcmRecoverIsSilent); | 930 kPcmRecoverIsSilent); |
884 } | 931 } |
885 frames_left -= frames_or_error; | 932 frames_left -= frames_or_error; |
886 DCHECK_GE(frames_left, 0); | 933 DCHECK_GE(frames_left, 0); |
887 data += frames_or_error * kNumOutputChannels * BytesPerOutputFormatSample(); | 934 data += frames_or_error * kNumOutputChannels * BytesPerOutputFormatSample(); |
888 } | 935 } |
889 UpdateRenderingDelay(frames); | 936 UpdateRenderingDelay(frames); |
890 for (auto&& input : inputs_) | 937 MediaPipelineBackendAlsa::RenderingDelay common_rendering_delay = |
891 input->AfterWriteFrames(rendering_delay_); | 938 alsa_rendering_delay_; |
| 939 common_rendering_delay.delay_microseconds += |
| 940 linearize_filter_->GetRenderingDelayMicroseconds() + |
| 941 mix_filter_->GetRenderingDelayMicroseconds(); |
| 942 for (auto&& input : inputs_) { |
| 943 MediaPipelineBackendAlsa::RenderingDelay stream_rendering_delay = |
| 944 common_rendering_delay; |
| 945 stream_rendering_delay.delay_microseconds += |
| 946 input->filter_group()->GetRenderingDelayMicroseconds(); |
| 947 input->AfterWriteFrames(stream_rendering_delay); |
| 948 } |
892 } | 949 } |
893 | 950 |
894 void StreamMixerAlsa::UpdateRenderingDelay(int newly_pushed_frames) { | 951 void StreamMixerAlsa::UpdateRenderingDelay(int newly_pushed_frames) { |
895 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); | 952 DCHECK(mixer_task_runner_->BelongsToCurrentThread()); |
896 CHECK_PCM_INITIALIZED(); | 953 CHECK_PCM_INITIALIZED(); |
897 | 954 |
898 // TODO(bshaya): Add rendering delay from post-processors. | 955 // TODO(bshaya): Add rendering delay from post-processors. |
899 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0 || | 956 if (alsa_->PcmStatus(pcm_, pcm_status_) != 0 || |
900 alsa_->PcmStatusGetState(pcm_status_) != SND_PCM_STATE_RUNNING) { | 957 alsa_->PcmStatusGetState(pcm_status_) != SND_PCM_STATE_RUNNING) { |
901 rendering_delay_.timestamp_microseconds = kNoTimestamp; | 958 alsa_rendering_delay_.timestamp_microseconds = kNoTimestamp; |
902 rendering_delay_.delay_microseconds = 0; | 959 alsa_rendering_delay_.delay_microseconds = 0; |
903 return; | 960 return; |
904 } | 961 } |
905 | 962 |
906 snd_htimestamp_t status_timestamp = {}; | 963 snd_htimestamp_t status_timestamp = {}; |
907 alsa_->PcmStatusGetHtstamp(pcm_status_, &status_timestamp); | 964 alsa_->PcmStatusGetHtstamp(pcm_status_, &status_timestamp); |
908 rendering_delay_.timestamp_microseconds = | 965 alsa_rendering_delay_.timestamp_microseconds = |
909 TimespecToMicroseconds(status_timestamp); | 966 TimespecToMicroseconds(status_timestamp); |
910 snd_pcm_sframes_t delay_frames = alsa_->PcmStatusGetDelay(pcm_status_); | 967 snd_pcm_sframes_t delay_frames = alsa_->PcmStatusGetDelay(pcm_status_); |
911 rendering_delay_.delay_microseconds = static_cast<int64_t>(delay_frames) * | 968 alsa_rendering_delay_.delay_microseconds = |
912 base::Time::kMicrosecondsPerSecond / | 969 static_cast<int64_t>(delay_frames) * base::Time::kMicrosecondsPerSecond / |
913 output_samples_per_second_; | 970 output_samples_per_second_; |
914 } | 971 } |
915 | 972 |
916 void StreamMixerAlsa::AddLoopbackAudioObserver( | 973 void StreamMixerAlsa::AddLoopbackAudioObserver( |
917 CastMediaShlib::LoopbackAudioObserver* observer) { | 974 CastMediaShlib::LoopbackAudioObserver* observer) { |
918 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddLoopbackAudioObserver, observer); | 975 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddLoopbackAudioObserver, observer); |
919 DCHECK(observer); | 976 DCHECK(observer); |
920 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 977 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
921 observer) == loopback_observers_.end()); | 978 observer) == loopback_observers_.end()); |
922 loopback_observers_.push_back(observer); | 979 loopback_observers_.push_back(observer); |
923 } | 980 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
976 for (auto&& input : inputs_) { | 1033 for (auto&& input : inputs_) { |
977 // Volume limits don't apply to effects streams. | 1034 // Volume limits don't apply to effects streams. |
978 if (input->primary() && input->content_type() == type) { | 1035 if (input->primary() && input->content_type() == type) { |
979 input->SetContentTypeVolume(effective_volume, fade_ms); | 1036 input->SetContentTypeVolume(effective_volume, fade_ms); |
980 } | 1037 } |
981 } | 1038 } |
982 } | 1039 } |
983 | 1040 |
984 } // namespace media | 1041 } // namespace media |
985 } // namespace chromecast | 1042 } // namespace chromecast |
OLD | NEW |