blob: 8cd36bf60b19b23e4b38ffc8c4f40135f20b70ec [file] [log] [blame]
// Copyright 2017 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 <fuchsia/media/cpp/fidl.h>
#include <lib/media/cpp/timeline_function.h>
#include <zircon/device/audio.h>
#include <memory>
#include <dispatcher-pool/dispatcher-execution-domain.h>
#include <dispatcher-pool/dispatcher-wakeup-event.h>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include "src/lib/fxl/synchronization/thread_annotations.h"
#include "src/media/audio/audio_core/audio_device_settings.h"
#include "src/media/audio/audio_core/audio_object.h"
#include "src/media/audio/audio_core/fwd_decls.h"
namespace media::audio {
class AudioDriver;
class DriverRingBuffer;
class AudioDevice : public AudioObject, public fbl::WAVLTreeContainable<fbl::RefPtr<AudioDevice>> {
// Wakeup
// Called from outside the mixing ExecutionDomain to cause an AudioDevice's::OnWakeup handler to
// run from within the context of the mixing execution domain.
void Wakeup();
// Accessors for the current plug state of the device.
// In addition to publishing and unpublishing streams when codecs are attached to or removed from
// hot-pluggable buses (such as USB), some codecs have the ability to detect the plugged/unplugged
// state of external connectors (such as a 3.5mm audio jack). Drivers can report this state as
// well as the time of the last state change. Currently this information is used in the Audio
// Service to implement simple routing policies for AudioRenderers and AudioCapturers.
// plugged : true when an audio output/input stream is either hardwired, or
// believes that it has something connected to its plug.
// plug_time : The time (per zx::clock::get_monotonic() at which the plugged/unplugged state of
// this output or input last changed.
bool plugged() const { return plugged_; }
zx_time_t plug_time() const { return plug_time_; }
const std::unique_ptr<AudioDriver>& driver() const { return driver_; }
uint64_t token() const;
uint64_t GetKey() const { return token(); }
bool activated() const { return activated_; }
// NotifyDestFormatPreference
// Called by clients who are destinations of ours, to inform us of their preferred format.
// TODO(johngro): Remove, when driver format selection is controlled by a policy manager layer.
virtual void NotifyDestFormatPreference(const fuchsia::media::AudioStreamTypePtr& fmt)
FXL_LOCKS_EXCLUDED(mix_domain_->token()) {}
// GetSourceFormatPreference
// Returns the format that this AudioDevice prefers to use when acting as a
// source of audio (either an input, or an output being looped back).
// TODO(johngro): Remove this once we have audio policy. Users should talk to
// the policy manager to know what inputs and outputs exist, what formats they
// support, and to express a desired device routing. "Preference" of an audio
// device is not a concept which belongs in the mixer.
virtual fuchsia::media::AudioStreamTypePtr GetSourceFormatPreference() { return nullptr; }
// Accessor set gain. Limits the gain command to what the hardware allows, and
// wakes up the device in the event of a meaningful change in gain settings.
// Only called by AudioDeviceManager, and only after the device is activated.
void SetGainInfo(const fuchsia::media::AudioGainInfo& info, uint32_t set_flags);
// Device info used during device enumeration and add-notifications.
void GetDeviceInfo(fuchsia::media::AudioDeviceInfo* out_info) const;
friend class fbl::RefPtr<AudioDevice>;
~AudioDevice() override;
AudioDevice(Type type, AudioDeviceManager* manager);
// Methods which may be implemented by derived classes to customize behavior.
// Init
// Called during startup on the AudioCore's main message loop thread. No
// locks are being held at this point. Derived classes should begin the
// process of driver initialization at this point. Return ZX_OK if things
// have started and we are waiting for driver init.
virtual zx_status_t Init();
// Cleanup
// Called at shutdown on AudioCore's main message loop thread to allow derived
// classes to clean up any allocated resources. All pending processing
// callbacks have either been nerfed or run to completion. All other audio
// objects have been disconnected/unlinked. No locks are being held. The
// analogous AudioDriver::Cleanup is only ever called by this function.
virtual void Cleanup();
// ApplyGainLimits
// Modify the contents of a user request to change the gain state to reflect
// the actual gain that we are going to end up setting. This may differ from
// the requested gain due to hardware limitations or general policy.
virtual void ApplyGainLimits(fuchsia::media::AudioGainInfo* in_out_info, uint32_t set_flags) = 0;
// Methods which may used by derived classes from within the context of a
// mix_domain_ ExecutionDomain. Note; since these methods are intended to be
// called from the within the mix_domain_, callers must be annotated properly
// to demonstrate that they are executing from within that domain.
// OnWakeup
// Called in response to someone from outside the domain poking the
// mix_wakeup_ WakeupEvent. At a minimum, the framework will call this once
// at startup to get the output running.
virtual void OnWakeup() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token()) = 0;
// ActivateSelf
// Send a message to the audio device manager to let it know that we are ready
// to be added to the set of active devices.
void ActivateSelf() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token());
// ShutdownSelf
// Kick off the process of shooting ourselves in the head. Note: after this
// method is called, no new callbacks may be scheduled. When the main message
// loop gets our shutdown request, it completes the process by unlinking us
// from our AudioRenderers/AudioCapturers and calling our Cleanup.
void ShutdownSelf() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token());
// Check the shutting down flag. We are in the process of shutting down when
// we have become deactivated at the dispatcher framework level.
inline bool is_shutting_down() const { return (!mix_domain_ || mix_domain_->deactivated()); }
// AudioDriver hooks.
// Hooks used by encapsulated AudioDriver instances to notify AudioDevices
// about internal state machine changes.
virtual void OnDriverInfoFetched() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token()){};
virtual void OnDriverConfigComplete() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token()){};
virtual void OnDriverStartComplete() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token()){};
virtual void OnDriverStopComplete() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token()){};
virtual void OnDriverPlugStateChange(bool plugged, zx_time_t plug_time)
// Other methods.
// UpdatePlugState
// Called by the audio device manager on the main message loop when it is
// notified of a plug state change for a device. Used to update the internal
// bookkeeping about the current plugged/unplugged state. This method may also
// be used by derived classes during Init to set an initial plug state.
// Returns true if the plug state has changed, or false otherwise.
bool UpdatePlugState(bool plugged, zx_time_t plug_time);
// AudioDriver accessors.
const fbl::RefPtr<DriverRingBuffer>& driver_ring_buffer() const
const TimelineFunction& driver_clock_mono_to_ring_pos_bytes() const
AudioDeviceManager* manager_;
// State used to manage asynchronous processing using the dispatcher
// framework.
fbl::RefPtr<dispatcher::ExecutionDomain> mix_domain_;
fbl::RefPtr<dispatcher::WakeupEvent> mix_wakeup_;
// This object manages most interactions with the low-level driver for us.
std::unique_ptr<AudioDriver> driver_;
// Persistable settings. Note, this is instantiated by the audio device
// itself during activate self so that it may be pre-populated with the
// current hardware state, and so the presence/absence of this pointer is
// always coherent from the view of the mix_domain. Once instantiated, this
// class lives for as long as the AudioDevice does.
fbl::RefPtr<AudioDeviceSettings> device_settings_;
// It's always nice when you manager is also your friend. Seriously though,
// the AudioDeviceManager gets to call Startup and Shutdown, no one else
// (including derived classes) should be able to.
friend class AudioDeviceManager;
friend class AudioDriver;
friend struct PendingInitListTraits;
// DeactivateDomain
// Deactivate our execution domain (if it exists) and synchronize with any
// operations taking place in the domain.
void DeactivateDomain() FXL_LOCKS_EXCLUDED(mix_domain_->token());
// Called from the AudioDeviceManager after an output has been created.
// Gives derived classes a chance to set up hardware, then sets up the
// machinery needed for scheduling processing tasks and schedules the first
// processing callback immediately in order to get the process running.
zx_status_t Startup();
// Called from the AudioDeviceManager on the main message loop thread. Makes
// certain that the shutdown process has started, synchronizes with processing
// tasks which were executing at the time, then finishes the shutdown by
// unlinking from all renderers/capturers and cleaning up all resources.
void Shutdown();
// Called from the AudioDeviceManager when it moves an audio device from its
// "pending init" set over to its "active" set .
void SetActivated() {
activated_ = true;
// Accessor used by AudioDeviceManager to access the device_settings object.
// Note: it is certainly possible for AudioDeviceManager to simply access the
// device_settings_ pointer directly. Code should use this accessor instead,
// however, to avoid accidentally releasing the device_settings object.
const fbl::RefPtr<AudioDeviceSettings>& device_settings() const { return device_settings_; }
bool system_gain_dirty = true;
// Plug state is protected by the fact that it is only ever accessed on the
// main message loop thread.
bool plugged_ = false;
zx_time_t plug_time_ = 0;
volatile bool shut_down_ = false;
volatile bool activated_ = false;
} // namespace media::audio