blob: c8b5f302127d0e33be4cbc45a83a85fdea3ca554 [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.
#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DEVICE_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DEVICE_H_
#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 <fuchsia/media/cpp/fidl.h>
#include <zircon/device/audio.h>
#include <deque>
#include <memory>
#include <set>
#include <thread>
#include "lib/media/timeline/timeline_function.h"
#include "src/lib/fxl/synchronization/thread_annotations.h"
#include "src/lib/fxl/time/time_point.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/audio_renderer_impl.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>> {
public:
// 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/removed to/from hot pluggable buses (such as USB), some codecs
// have the ability to detect the plugged or unplugged state of external
// connectors (such as a 3.5mm audio jack). Drivers can report this
// plugged/unplugged 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 this once device driver format selection is under
// control of the policy manager layer instead of here.
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;
protected:
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)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token()){};
//////////////////////////////////////////////////////////////////////////////
//
// 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
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token());
const TimelineFunction& driver_clock_mono_to_ring_pos_bytes() const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain_->token());
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_;
private:
// 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() {
FXL_DCHECK(!activated());
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
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_DEVICE_H_