| // Copyright 2016 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. |
| |
| #ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DEVICE_MANAGER_H_ |
| #define SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DEVICE_MANAGER_H_ |
| |
| #include <fuchsia/media/cpp/fidl.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/fidl/cpp/binding_set.h> |
| #include <lib/fit/function.h> |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <memory> |
| #include <unordered_map> |
| |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/ref_ptr.h> |
| |
| #include "src/media/audio/audio_core/audio_device.h" |
| #include "src/media/audio/audio_core/audio_driver.h" |
| #include "src/media/audio/audio_core/audio_input.h" |
| #include "src/media/audio/audio_core/audio_output.h" |
| #include "src/media/audio/audio_core/base_renderer.h" |
| #include "src/media/audio/audio_core/device_registry.h" |
| #include "src/media/audio/audio_core/plug_detector.h" |
| #include "src/media/audio/audio_core/route_graph.h" |
| #include "src/media/audio/audio_core/threading_model.h" |
| |
| namespace media::audio { |
| |
| class BaseCapturer; |
| class SystemGainMuteProvider; |
| |
| class AudioDeviceManager : public fuchsia::media::AudioDeviceEnumerator, public DeviceRegistry { |
| public: |
| AudioDeviceManager(ThreadingModel& threading_model, std::unique_ptr<PlugDetector> plug_detector, |
| RouteGraph& route_graph, LinkMatrix& link_matrix, |
| ProcessConfig& process_config); |
| ~AudioDeviceManager(); |
| |
| fidl::InterfaceRequestHandler<fuchsia::media::AudioDeviceEnumerator> GetFidlRequestHandler() { |
| return bindings_.GetHandler(this); |
| } |
| |
| ThreadingModel& threading_model() { return threading_model_; } |
| |
| // Initialize the input/output manager. |
| zx_status_t Init(); |
| |
| // Blocking call. Called by the service, once, when it is time to shutdown the service |
| // implementation. While this function is blocking, it must never block for long. Our process is |
| // going away; this is our last chance to perform a clean shutdown. If an unclean shutdown must |
| // be performed in order to implode in a timely fashion, so be it. |
| // |
| // Shutdown must be idempotent and safe to call from this object's destructor (although this |
| // should never be necessary). If a shutdown called from this destructor must do real work, |
| // something has gone Very Seriously Wrong. |
| void Shutdown(); |
| |
| // Sends an update message to each effect with the name 'instance_name' across all devices. |
| // If 'persist' is true, the effect update is persisted and applied to new devices as they are |
| // plugged. Only the latest update will be persisted for each 'instance_name'. |
| // |
| // Returns UpdateEffectError::INVALID_CONFIG if any effect matching 'instance_name' is found, but |
| // rejects 'message'. Returns UpdateEffectError::NOT_FOUND if no effect is found across any |
| // device. Returns success if at least one effect named 'instance_name' has accepted 'message' |
| // without any other effects matching 'effect_name' rejecting the 'message'. |
| fit::promise<void, fuchsia::media::audio::UpdateEffectError> UpdateEffect( |
| const std::string& instance_name, const std::string& message, bool persist = false); |
| |
| // Sends an update message to the effect specified by 'instance_name' for the device specified by |
| // 'device_id'. |
| // |
| // Return values match those of 'UpdateEffect' above, with the addition of an |
| // UpdateEffectError::NOT_FOUND if the device specified by 'device_id' is not found. |
| fit::promise<void, fuchsia::media::audio::UpdateEffectError> UpdateDeviceEffect( |
| const std::string device_id, const std::string& instance_name, const std::string& message); |
| |
| fit::promise<void, zx_status_t> UpdatePipelineConfig(const std::string device_id, |
| const PipelineConfig& config, |
| const VolumeCurve& volume_curve); |
| |
| // |media::audio::DeviceRegistry| |
| void AddDevice(const std::shared_ptr<AudioDevice>& device) override; |
| void ActivateDevice(const std::shared_ptr<AudioDevice>& device) override; |
| void RemoveDevice(const std::shared_ptr<AudioDevice>& device) override; |
| void OnPlugStateChanged(const std::shared_ptr<AudioDevice>& device, bool plugged, |
| zx::time plug_time) override; |
| std::vector<fuchsia::media::AudioDeviceInfo> GetDeviceInfos() override; |
| |
| // |fuchsia::media::AudioDeviceEnumerator| |
| void GetDevices(GetDevicesCallback cbk) final; |
| void GetDeviceGain(uint64_t device_token, GetDeviceGainCallback cbk) final; |
| void SetDeviceGain(uint64_t device_token, fuchsia::media::AudioGainInfo gain_info, |
| fuchsia::media::AudioGainValidFlags) final; |
| void GetDefaultInputDevice(GetDefaultInputDeviceCallback cbk) final; |
| void GetDefaultOutputDevice(GetDefaultOutputDeviceCallback cbk) final; |
| void AddDeviceByChannel(::zx::channel device_channel, std::string device_name, |
| bool is_input) final; |
| void AddDeviceByChannel2( |
| std::string device_name, bool is_input, |
| fidl::InterfaceHandle<fuchsia::hardware::audio::StreamConfig> channel) final; |
| void AddDeviceByVersion(zx::channel device_channel, std::string device_name, bool is_input, |
| AudioDriverVersion version); |
| |
| private: |
| // Find the most-recently plugged device (per type: input or output) excluding throttle_output. If |
| // allow_unplugged, return the most-recently UNplugged device if no plugged devices are found -- |
| // otherwise return nullptr. |
| std::shared_ptr<AudioDevice> FindLastPlugged(AudioObject::Type type, |
| bool allow_unplugged = false); |
| |
| std::shared_ptr<AudioOutput> FindLastPluggedOutput(bool allow_unplugged = false) { |
| auto dev = FindLastPlugged(AudioObject::Type::Output, allow_unplugged); |
| FX_DCHECK(!dev || (dev->type() == AudioObject::Type::Output)); |
| return std::static_pointer_cast<AudioOutput>(std::move(dev)); |
| } |
| |
| std::shared_ptr<AudioInput> FindLastPluggedInput(bool allow_unplugged = false) { |
| auto dev = FindLastPlugged(AudioObject::Type::Input, allow_unplugged); |
| FX_DCHECK(!dev || (dev->type() == AudioObject::Type::Input)); |
| return std::static_pointer_cast<AudioInput>(std::move(dev)); |
| } |
| |
| // Methods to handle routing policy -- when an existing device is unplugged or completely removed, |
| // or when a new device is plugged or added to the system. |
| void OnDeviceUnplugged(const std::shared_ptr<AudioDevice>& device, zx::time plug_time); |
| void OnDevicePlugged(const std::shared_ptr<AudioDevice>& device, zx::time plug_time); |
| |
| // Send notification to users that this device's gain settings have changed. |
| void NotifyDeviceGainChanged(const AudioDevice& device); |
| |
| // Re-evaluate which device is the default. Notify users, if this has changed. |
| void UpdateDefaultDevice(bool input); |
| |
| ThreadingModel& threading_model_; |
| RouteGraph& route_graph_; |
| std::unique_ptr<PlugDetector> plug_detector_; |
| LinkMatrix& link_matrix_; |
| ProcessConfig& process_config_; |
| |
| // The set of AudioDeviceEnumerator clients we are currently tending to. |
| fidl::BindingSet<fuchsia::media::AudioDeviceEnumerator> bindings_; |
| |
| // Our sets of currently active audio devices, AudioCapturers, and AudioRenderers. |
| // |
| // These must only be manipulated on main message loop thread. No synchronization should be |
| // needed. |
| |
| // These maps are keyed on device token. |
| std::unordered_map<uint64_t, std::shared_ptr<AudioDevice>> devices_pending_init_; |
| std::unordered_map<uint64_t, std::shared_ptr<AudioDevice>> devices_; |
| |
| uint64_t default_output_token_ = ZX_KOID_INVALID; |
| uint64_t default_input_token_ = ZX_KOID_INVALID; |
| |
| // Persisted effects updates. Mapping from instant_name to message. |
| std::unordered_map<std::string, std::string> persisted_effects_updates_; |
| }; |
| |
| } // namespace media::audio |
| |
| #endif // SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DEVICE_MANAGER_H_ |