| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/media/audio/audio_core/idle_policy.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <optional> |
| #include <unordered_set> |
| |
| #include "src/media/audio/audio_core/active_stream_count_reporter.h" |
| #include "src/media/audio/audio_core/context.h" |
| #include "src/media/audio/audio_core/logging_flags.h" |
| #include "src/media/audio/audio_core/route_graph.h" |
| #include "src/media/audio/audio_core/stream_usage.h" |
| |
| namespace media::audio { |
| |
| std::optional<zx::duration> IdlePolicy::idle_countdown_duration_; |
| std::optional<zx::duration> IdlePolicy::startup_idle_countdown_duration_; |
| bool IdlePolicy::use_all_ultrasonic_channels_ = true; |
| |
| // AudioAdmin::ActiveStreamCountReporter implementation |
| // |
| // Will be called on the FIDL thread |
| void IdlePolicy::OnActiveRenderCountChanged(RenderUsage usage, uint32_t count) { |
| if (!IdlePolicy::idle_countdown_duration().has_value() || IdlePolicy::kDisableIdlePolicy) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << " exiting early (idle policy disabled)"; |
| } |
| return; |
| } |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << "(" << RenderUsageToString(usage) << ", " << count << ")"; |
| } |
| |
| const RoutingScope scope = (usage == RenderUsage::ULTRASOUND ? kUltrasonicOnly : kAudibleOnly); |
| |
| std::lock_guard<std::mutex> lock(idle_state_mutex_); |
| PrepareForRoutingChange(/* is_input = */ false, scope); |
| |
| if (count) { |
| active_render_usages_.insert(StreamUsage::WithRenderUsage(usage)); |
| } else { |
| active_render_usages_.erase(StreamUsage::WithRenderUsage(usage)); |
| } |
| |
| DigestRoutingChange(/* is_input = */ false, scope); |
| } |
| |
| // DeviceRouter implementation |
| // |
| // Will be called on the FIDL thread |
| void IdlePolicy::AddDeviceToRoutes(AudioDevice* device) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << "(" << device << ")"; |
| } |
| |
| std::lock_guard<std::mutex> lock(idle_state_mutex_); |
| PrepareForRoutingChange(device->is_input(), kAudibleAndUltrasonic); |
| |
| context_->route_graph().AddDeviceToRoutes(device); |
| |
| DigestRoutingChange(device->is_input(), kAudibleAndUltrasonic); |
| } |
| |
| // Will be called on the FIDL thread |
| void IdlePolicy::RemoveDeviceFromRoutes(AudioDevice* device) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << "(" << device << ")"; |
| } |
| |
| std::lock_guard<std::mutex> lock(idle_state_mutex_); |
| PrepareForRoutingChange(device->is_input(), kAudibleAndUltrasonic); |
| |
| context_->route_graph().RemoveDeviceFromRoutes(device); |
| |
| DigestRoutingChange(device->is_input(), kAudibleAndUltrasonic); |
| } |
| |
| // Will be called on the FIDL thread |
| void IdlePolicy::PrepareForRoutingChange(bool device_is_input, RoutingScope scope) { |
| if (device_is_input) { |
| return; |
| } |
| |
| if (!IdlePolicy::idle_countdown_duration().has_value() || IdlePolicy::kDisableIdlePolicy) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << ": not caching routing state (idle policy disabled)"; |
| } |
| return; |
| } |
| |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << ": caching state before RouteGraph::AddDeviceToRoutes"; |
| } |
| |
| if (scope == kAudibleOnly || scope == kAudibleAndUltrasonic) { |
| audible_devices_before_device_change_ = ActiveDevices(/* ultrasonic_only = */ false); |
| } |
| |
| if (scope == kUltrasonicOnly || scope == kAudibleAndUltrasonic) { |
| ultrasonic_devices_before_device_change_ = ActiveDevices(/* ultrasonic_only = */ true); |
| } |
| } |
| |
| // Will be called on the FIDL thread |
| void IdlePolicy::DigestRoutingChange(bool device_is_input, RoutingScope scope) { |
| if (device_is_input) { |
| return; |
| } |
| |
| if (!IdlePolicy::idle_countdown_duration().has_value() || IdlePolicy::kDisableIdlePolicy) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << ": not changing active channels (idle policy disabled)"; |
| } |
| return; |
| } |
| |
| if (scope == kAudibleOnly || scope == kAudibleAndUltrasonic) { |
| auto audible_devices_after = ActiveDevices(/* ultrasonic_only = */ false); |
| |
| // First take care of devices that were - but are no longer - routed to an active RenderUsage |
| for (auto& dev : audible_devices_before_device_change_) { |
| if (audible_devices_after.count(dev)) { |
| audible_devices_after.erase(dev); // still active after, so remove it from our attention |
| continue; |
| } |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << " calling StartCountdownToDisableAudible"; |
| } |
| if (IdlePolicy::idle_countdown_duration().has_value()) { |
| dev->StartCountdownToDisableAudible(*IdlePolicy::idle_countdown_duration()); |
| } |
| } |
| audible_devices_before_device_change_.clear(); |
| |
| // Only devices remaining are ones that are newly targeted by an active RenderUsage |
| for (auto& dev : audible_devices_after) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << " calling EnableAudible"; |
| } |
| dev->EnableAudible(); |
| } |
| } |
| |
| if (scope == kUltrasonicOnly || scope == kAudibleAndUltrasonic) { |
| auto ultrasonic_devices_after = ActiveDevices(/* ultrasonic_only = */ true); |
| |
| for (auto& dev : ultrasonic_devices_before_device_change_) { |
| if (ultrasonic_devices_after.count(dev)) { |
| ultrasonic_devices_after.erase(dev); |
| continue; |
| } |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << " calling StartCountdownToDisableUltrasonic"; |
| } |
| if (IdlePolicy::idle_countdown_duration().has_value()) { |
| dev->StartCountdownToDisableUltrasonic(*IdlePolicy::idle_countdown_duration()); |
| } |
| } |
| |
| ultrasonic_devices_before_device_change_.clear(); |
| |
| for (auto& dev : ultrasonic_devices_after) { |
| if constexpr (kLogIdlePolicyCounts) { |
| FX_LOGS(INFO) << __FUNCTION__ << " calling EnableUltrasonic"; |
| } |
| dev->EnableUltrasonic(); |
| } |
| } |
| } |
| |
| std::unordered_set<AudioDevice*> IdlePolicy::ActiveDevices(bool ultrasonic_only) { |
| std::unordered_set<AudioDevice*> active_devices; |
| |
| for (const auto& usage : kRenderUsages) { |
| if (ultrasonic_only == (usage == RenderUsage::ULTRASOUND)) { |
| if (active_render_usages_.contains(StreamUsage::WithRenderUsage(usage))) { |
| auto outputs = context_->route_graph().TargetsForRenderUsage(usage); |
| active_devices.insert(outputs.begin(), outputs.end()); |
| } |
| } |
| } |
| |
| return active_devices; |
| } |
| |
| } // namespace media::audio |