| // 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_OBJECT_H_ |
| #define SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_OBJECT_H_ |
| |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <lib/fit/function.h> |
| |
| #include "src/lib/fxl/synchronization/thread_annotations.h" |
| #include "src/media/audio/audio_core/audio_link.h" |
| #include "src/media/audio/audio_core/fwd_decls.h" |
| |
| namespace media::audio { |
| |
| // An audio object is the simple base class for 4 major types of audio objects |
| // in the mixer; Outputs, Inputs, AudioRenderers and AudioCapturers. It ensures |
| // that each of these objects is intrusively ref-counted, and remembers its type |
| // so that it may be safely downcast from a generic audio object to something |
| // more specific. |
| class AudioObject : public fbl::RefCounted<AudioObject> { |
| public: |
| enum class Type { |
| Output, |
| Input, |
| AudioRenderer, |
| AudioCapturer, |
| }; |
| |
| static fbl::RefPtr<AudioLink> LinkObjects( |
| const fbl::RefPtr<AudioObject>& source, |
| const fbl::RefPtr<AudioObject>& dest); |
| static void RemoveLink(const fbl::RefPtr<AudioLink>& link); |
| |
| void UnlinkSources(); |
| void UnlinkDestinations(); |
| void Unlink() { |
| UnlinkSources(); |
| UnlinkDestinations(); |
| } |
| |
| // PreventNewLinks |
| // |
| // Clears the new_links_allowed flag from within the context of the |
| // links_lock. This ensures that no new links may be added to this object |
| // anymore. Calling PreventNewLinks is one of the first steps in the process |
| // of shutting down an AudioObject. |
| // |
| // TODO(johngro) : Consider eliminating this; given the way that links are |
| // created and destroyed, it is not clear if it is needed anymore. |
| void PreventNewLinks() { |
| fbl::AutoLock lock(&links_lock_); |
| new_links_allowed_ = false; |
| } |
| |
| Type type() const { return type_; } |
| bool is_output() const { return type() == Type::Output; } |
| bool is_input() const { return type() == Type::Input; } |
| bool is_audio_renderer() const { return type() == Type::AudioRenderer; } |
| bool is_audio_capturer() const { return type() == Type::AudioCapturer; } |
| |
| protected: |
| friend class fbl::RefPtr<AudioObject>; |
| explicit AudioObject(Type type) : type_(type) {} |
| virtual ~AudioObject() {} |
| |
| // Initialize(Source|Dest)Link |
| // |
| // Called on the AudioCore's main message loop any time a source and a |
| // destination are being linked via AudioObject::LinkObjects. By default, |
| // these hooks do nothing, but AudioObject subclasses may use them to set the |
| // properties of a link (or reject the link) before the link gets added to the |
| // source and destination link sets. |
| // |
| // For example, Sources like an AudioRenderer override InitializeDestLink in |
| // order to set the source gain and to make a copy of their pending packet |
| // queue. Destinations like an output override InitializeSourceLink in order |
| // to choose and initialize an appropriate resampling filter. |
| // |
| // @return MediaResult::OK if initialization succeeded, or an appropriate |
| // error code otherwise. |
| virtual zx_status_t InitializeSourceLink(const fbl::RefPtr<AudioLink>& link); |
| virtual zx_status_t InitializeDestLink(const fbl::RefPtr<AudioLink>& link); |
| |
| fbl::Mutex links_lock_; |
| |
| // The set of links which this audio device is acting as a source for (eg; the |
| // destinations that this object is sending to). The target of each of these |
| // links must be a either an Output or a AudioCapturer. |
| typename AudioLink::Set<AudioLink::Dest> dest_links_ |
| FXL_GUARDED_BY(links_lock_); |
| |
| // The set of links which this audio device is acting as a destination for |
| // (eg; the sources that that the object is receiving from). The target of |
| // each of these links must be a either an Output or a AudioCapturer. |
| // |
| // TODO(johngro): Order this by priority. Use a fbl::WAVLTree (or some other |
| // form of ordered intrusive container) so that we can easily remove and |
| // re-insert a link if/when priority changes. |
| // |
| // Right now, we have no priorities, so this is just a set of |
| // AudioRenderer/output links. |
| typename AudioLink::Set<AudioLink::Source> source_links_ |
| FXL_GUARDED_BY(links_lock_); |
| |
| // The following iterator functions accept a function (see below) and call it |
| // sequentially with each destination link as a parameter. As described below, |
| // depending on which iterator is used, either every link is guaranteed to be |
| // included, or iteration will terminate early as soon as a task returns true. |
| // |
| // This iterator approach reduces our ability to use static thread analysis |
| // effectively, so use with care. ForEachDestLink and ForAnyDestLink each |
| // obtain the links_lock_ and hold it while each LinkFunction or |
| // LinkBoolFunction is invoked. For this reason, |
| // 1) Callers into the ForEachSourceLink, ForEachDestLink or ForAnyDestLink |
| // functions must not already hold links_lock_; additionally, |
| // 2) A LinkFunction or LinkBoolFunction must not |
| // a) attempt to obtain links_lock_ directly, nor |
| // b) acquire any lock marked as acquired_before(links_lock_), nor |
| // c) call any function which excludes links_lock_. |
| // |
| |
| // The inline_functions below reserve stack space for up to four pointers. |
| // This can be increased as needed (but should NOT be needed any time soon). |
| // |
| // LinkFunction has no return value and is used with ForEach[Source|Dest]Link. |
| using LinkFunction = |
| fit::inline_function<void(AudioLink& link), sizeof(void*) * 4>; |
| // Same as LinkFunction, but returns bool for early termination. This |
| // return val is used by ForAnyDestLink (or a future ForAllDestLinks). |
| // Currently stack space for one ptr is provided (the one caller needs 0). |
| using LinkBoolFunction = |
| fit::inline_function<bool(AudioLink& link), sizeof(void*) * 1>; |
| |
| // Link Iterators - these functions iterate upon LinkPacketSource types only. |
| // |
| // Run this task on AudioLinks in source_links_. All links will be called. |
| void ForEachSourceLink(const LinkFunction& source_task) |
| FXL_LOCKS_EXCLUDED(links_lock_); |
| |
| // Run this task on every AudioLink in dest_links_. All links will be called. |
| void ForEachDestLink(const LinkFunction& dest_task) |
| FXL_LOCKS_EXCLUDED(links_lock_); |
| |
| // Run this task on each dest link. If any returns 'true', ForAnyDestLink |
| // immediately returns 'true' without calling the remaining links. If none |
| // returns 'true' or if link set is empty, ForAnyDestLink returns 'false'. |
| bool ForAnyDestLink(const LinkBoolFunction& dest_task) |
| FXL_LOCKS_EXCLUDED(links_lock_); |
| |
| // TODO(mpuryear): it might be a good idea to introduce an auto-lock like |
| // object to fbl::, to behave like a lock token for situations like this. With |
| // proper tweaks to fbl::Mutex, this could for static analysis purposes seem |
| // to obtain and release a mutex without actually doing so. In debug builds, |
| // it could also assert that the mutex was held at object construction time. |
| |
| // Pros: we regain much of the TA protection, if lambdas add one of these |
| // objects "holding" the proper lock at the start of their executions. |
| |
| // Cons: essentially all these lambdas must capture "this", to tell the TA |
| // which instance of links_lock was being held. This price would be paid in |
| // all builds, regardless of whether code gets generated as a result. |
| // |
| |
| private: |
| template <typename TagType> |
| void UnlinkCleanup(typename AudioLink::Set<TagType>* links); |
| |
| const Type type_; |
| bool new_links_allowed_ FXL_GUARDED_BY(links_lock_) = true; |
| }; |
| |
| } // namespace media::audio |
| |
| #endif // SRC_MEDIA_AUDIO_AUDIO_CORE_AUDIO_OBJECT_H_ |