blob: dd43ecc7d4f18d443ffbcbb3c7eb9a977d7b9ff9 [file] [log] [blame]
// 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 GARNET_BIN_MEDIA_AUDIO_CORE_AUDIO_DEVICE_MANAGER_H_
#define GARNET_BIN_MEDIA_AUDIO_CORE_AUDIO_DEVICE_MANAGER_H_
#include <fbl/intrusive_double_list.h>
#include <fbl/ref_ptr.h>
#include <fuchsia/media/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <set>
#include "garnet/bin/media/audio_core/audio_device.h"
#include "garnet/bin/media/audio_core/audio_input.h"
#include "garnet/bin/media/audio_core/audio_output.h"
#include "garnet/bin/media/audio_core/audio_plug_detector.h"
#include "garnet/bin/media/audio_core/fwd_decls.h"
#include "garnet/bin/media/audio_core/mixer/fx_loader.h"
#include "lib/fidl/cpp/binding_set.h"
namespace media {
namespace audio {
class AudioCapturerImpl;
class AudioDeviceManager : public ::fuchsia::media::AudioDeviceEnumerator {
public:
explicit AudioDeviceManager(AudioCoreImpl* service);
~AudioDeviceManager();
// Initialize the output manager. Called from the service implementation,
// once, at startup time. Should...
//
// 1) Initialize the mixing thread pool.
// 2) Instantiate all of the built-in audio output devices.
// 3) Monitor for plug/unplug events for pluggable audio output devices.
// 4) Load the device effects library.
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();
// Add a new client for the device enumerator functionality. Called from the
// service framework each time a new client attempts to connect.
void AddDeviceEnumeratorClient(zx::channel ch);
// Add an AudioRenderer to the set of active AudioRenderers.
void AddAudioRenderer(fbl::RefPtr<AudioRendererImpl> audio_renderer) {
FXL_DCHECK(audio_renderer);
audio_renderers_.push_back(std::move(audio_renderer));
}
// Remove an AudioRenderer from the set of active AudioRenderers.
void RemoveAudioRenderer(AudioRendererImpl* audio_renderer) {
FXL_DCHECK(audio_renderer != nullptr);
FXL_DCHECK(audio_renderer->InContainer());
audio_renderers_.erase(*audio_renderer);
}
// Select the initial set of outputs for a newly-configured AudioRenderer.
void SelectOutputsForAudioRenderer(AudioRendererImpl* audio_renderer);
// Link an output to an AudioRenderer.
void LinkOutputToAudioRenderer(AudioOutput* output,
AudioRendererImpl* audio_renderer);
// Add/remove an AudioCapturer to/from the set of active AudioCapturers.
void AddAudioCapturer(fbl::RefPtr<AudioCapturerImpl> audio_capturer);
void RemoveAudioCapturer(AudioCapturerImpl* audio_capturer);
// Schedule a closure to run on our encapsulating service's main message loop.
void ScheduleMainThreadTask(fit::closure task);
// Begin the process of initializing a device and add it to the set of device
// which are waiting to be initialized.
//
// Called from the plug detector when a new stream device first shows up.
zx_status_t AddDevice(const fbl::RefPtr<AudioDevice>& device);
// Move a device from the pending init list to the active device's list.
// Notify users of this new device, and re-evaluate policy decisions.
void ActivateDevice(const fbl::RefPtr<AudioDevice>& device);
// Shutdown this device; remove it from the appropriate set of active devices.
void RemoveDevice(const fbl::RefPtr<AudioDevice>& device);
// Handles a plugged/unplugged state change for the supplied audio device.
void HandlePlugStateChange(const fbl::RefPtr<AudioDevice>& device,
bool plugged, zx_time_t plug_time);
void SetRoutingPolicy(fuchsia::media::AudioOutputRoutingPolicy policy);
static inline bool ValidateRoutingPolicy(
fuchsia::media::AudioOutputRoutingPolicy policy) {
switch (policy) {
case fuchsia::media::AudioOutputRoutingPolicy::LAST_PLUGGED_OUTPUT:
case fuchsia::media::AudioOutputRoutingPolicy::ALL_PLUGGED_OUTPUTS:
return true;
// Note: no default: handler here. If someone adds a new policy to the
// enum but forgets to update this code, we want a Build Break, to
// notify us that we need to handle the new policy.
}
return false;
}
// SetSystemGain/Mute has been called. 'changed' tells us whether System Gain
// or Mute values actually changed. If not, only update devices that (because
// of calls to SetDeviceGain) have diverged from System settings.
void OnSystemGain(bool changed);
// Implementation of the AudioDeviceEnumerator FIDL interface.
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,
uint32_t set_flags) final;
void GetDefaultInputDevice(GetDefaultInputDeviceCallback cbk) final;
void GetDefaultOutputDevice(GetDefaultOutputDeviceCallback cbk) final;
private:
// KeyTraits we use to sort our AudioDeviceSettings set to ensure uniqueness.
struct AudioDeviceSettingsKeyTraits {
static const AudioDeviceSettings* GetKey(const AudioDeviceSettings& obj) {
return &obj;
}
static bool LessThan(const AudioDeviceSettings* k1,
const AudioDeviceSettings* k2) {
return (k1->is_input() && !k2->is_input()) ||
((k1->is_input() == k2->is_input()) &&
(::memcmp(&k1->uid(), &k2->uid(), sizeof(k1->uid())) < 0));
}
static bool EqualTo(const AudioDeviceSettings* k1,
const AudioDeviceSettings* k2) {
return (k1->is_input() == k2->is_input()) &&
(::memcmp(&k1->uid(), &k2->uid(), sizeof(k1->uid())) == 0);
}
};
using DeviceSettingsSet = fbl::WAVLTree<const AudioDeviceSettings*,
fbl::RefPtr<AudioDeviceSettings>,
AudioDeviceSettingsKeyTraits>;
// 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.
fbl::RefPtr<AudioDevice> FindLastPlugged(AudioObject::Type type,
bool allow_unplugged = false);
fbl::RefPtr<AudioOutput> FindLastPluggedOutput(bool allow_unplugged = false) {
auto dev = FindLastPlugged(AudioObject::Type::Output, allow_unplugged);
FXL_DCHECK(!dev || (dev->type() == AudioObject::Type::Output));
return fbl::RefPtr<AudioOutput>::Downcast(std::move(dev));
}
fbl::RefPtr<AudioInput> FindLastPluggedInput(bool allow_unplugged = false) {
auto dev = FindLastPlugged(AudioObject::Type::Input, allow_unplugged);
FXL_DCHECK(!dev || (dev->type() == AudioObject::Type::Input));
return fbl::RefPtr<AudioInput>::Downcast(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 fbl::RefPtr<AudioDevice>& device,
zx_time_t plug_time);
void OnDevicePlugged(const fbl::RefPtr<AudioDevice>& device,
zx_time_t plug_time);
void LinkToAudioCapturers(const fbl::RefPtr<AudioDevice>& device);
// Commit any pending device-settings changes to disk (if settings are disk-
// backed), then remove the settings from our persisted_device_settings_ map.
void FinalizeDeviceSettings(const AudioDevice& device);
// 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);
// Update a device gain to the "system" gain exposed by the top-level service.
//
// TODO(johngro): Remove this when we remove system gain entirely.
void UpdateDeviceToSystemGain(AudioDevice* device);
// Commit any dirty settings to storage, (re)scheduling the timer as needed.
void CommitDirtySettings();
void CommitDirtySettingsThunk(async_dispatcher_t*, async::TaskBase*,
zx_status_t) {
CommitDirtySettings();
}
// A pointer to the service which encapsulates us. It is not possible for
// this pointer to be bad while we still exist.
AudioCoreImpl* service_;
// 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.
//
// Contents of these collections must only be manipulated on the main message
// loop thread, so no synchronization should be needed.
fbl::WAVLTree<uint64_t, fbl::RefPtr<AudioDevice>> devices_pending_init_;
fbl::WAVLTree<uint64_t, fbl::RefPtr<AudioDevice>> devices_;
fbl::DoublyLinkedList<fbl::RefPtr<AudioCapturerImpl>> audio_capturers_;
fbl::DoublyLinkedList<fbl::RefPtr<AudioRendererImpl>> audio_renderers_;
// The special throttle output always exists and is used by every renderer.
fbl::RefPtr<AudioOutput> throttle_output_;
// A helper class we will use to detect plug/unplug events for audio devices
AudioPlugDetector plug_detector_;
// State which affects routing policy.
fuchsia::media::AudioOutputRoutingPolicy routing_policy_ =
fuchsia::media::AudioOutputRoutingPolicy::LAST_PLUGGED_OUTPUT;
uint64_t default_output_token_ = ZX_KOID_INVALID;
uint64_t default_input_token_ = ZX_KOID_INVALID;
// The unique AudioDeviceSettings subset we track that needs disk-persistence.
DeviceSettingsSet persisted_device_settings_;
async::TaskMethod<AudioDeviceManager,
&AudioDeviceManager::CommitDirtySettingsThunk>
commit_settings_task_{this};
FxLoader fx_loader_;
};
} // namespace audio
} // namespace media
#endif // GARNET_BIN_MEDIA_AUDIO_CORE_AUDIO_DEVICE_MANAGER_H_