| 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 |