blob: 20c4622d7be67ce44e074c4faec583bc8f8dfcc9 [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 "src/media/audio/audio_core/audio_object.h"
#include "src/lib/fxl/logging.h"
#include "src/media/audio/audio_core/audio_device.h"
#include "src/media/audio/audio_core/audio_link.h"
#include "src/media/audio/audio_core/audio_link_packet_source.h"
#include "src/media/audio/audio_core/audio_link_ring_buffer_source.h"
#include "src/media/audio/audio_core/audio_renderer_impl.h"
namespace media::audio {
// static
fbl::RefPtr<AudioLink> AudioObject::LinkObjects(const fbl::RefPtr<AudioObject>& source,
const fbl::RefPtr<AudioObject>& dest) {
// Assert this source is valid (AudioCapturers are disallowed).
FXL_DCHECK(source != nullptr);
FXL_DCHECK((source->type() == AudioObject::Type::AudioRenderer) ||
(source->type() == AudioObject::Type::Output) ||
(source->type() == AudioObject::Type::Input));
// Assert this destination is valid (inputs and AudioRenderers disallowed).
FXL_DCHECK(dest != nullptr);
FXL_DCHECK((dest->type() == AudioObject::Type::Output) ||
(dest->type() == AudioObject::Type::AudioCapturer));
// Assert that we are not connecting looped-back-output to output.
FXL_DCHECK((source->type() != AudioObject::Type::Output) ||
(dest->type() != AudioObject::Type::Output));
// Create a link of the appropriate type based on our source.
fbl::RefPtr<AudioLink> link;
if (source->type() == AudioObject::Type::AudioRenderer) {
auto& audio_renderer = *fbl::RefPtr<AudioRendererImpl>::Downcast(source);
FXL_DCHECK(audio_renderer.format_info_valid());
link = AudioLinkPacketSource::Create(source, dest, audio_renderer.format_info());
} else {
link = AudioLinkRingBufferSource::Create(fbl::RefPtr<AudioDevice>::Downcast(source), dest);
}
// Give source and destination a chance to initialize (or reject) the link.
zx_status_t res;
res = source->InitializeDestLink(link);
if (res != ZX_OK) {
return nullptr;
}
res = dest->InitializeSourceLink(link);
if (res != ZX_OK) {
return nullptr;
}
// Now lock both objects, make sure both are still allowing new links, then add the link to the
// proper sets in both source and destination.
{
fbl::AutoLock slock(&source->links_lock_);
fbl::AutoLock dlock(&dest->links_lock_);
if (source->new_links_allowed_ && dest->new_links_allowed_) {
source->dest_links_.insert(link);
dest->source_links_.insert(link);
} else {
link.reset();
}
}
// TODO(johngro): if we must poke the destination, in case it needs to wake and do specific work
// because of this new source, this where to do it.
return link;
}
// static
void AudioObject::RemoveLink(const fbl::RefPtr<AudioLink>& link) {
FXL_DCHECK(link != nullptr);
link->Invalidate();
const fbl::RefPtr<AudioObject>& source = link->GetSource();
FXL_DCHECK(source != nullptr);
{
fbl::AutoLock slock(&source->links_lock_);
auto iter = source->dest_links_.find(link.get());
if (iter != source->dest_links_.end()) {
source->dest_links_.erase(iter);
}
}
const fbl::RefPtr<AudioObject>& dest = link->GetDest();
FXL_DCHECK(dest != nullptr);
{
fbl::AutoLock dlock(&dest->links_lock_);
auto iter = dest->source_links_.find(link.get());
if (iter != dest->source_links_.end()) {
dest->source_links_.erase(iter);
}
}
}
// Call the provided function for each source link (passing the link as param). This distributes
// calls such as SetGain to every AudioCapturer path.
void AudioObject::ForEachSourceLink(const LinkFunction& source_task) {
fbl::AutoLock links_lock(&links_lock_);
// Callers (generally AudioCapturers) should never be linked to destinations.
FXL_DCHECK(dest_links_.is_empty());
for (auto& link : source_links_) {
source_task(link);
}
}
// Call the provided function for each dest link (passing the link as a param). This distributes
// calls such as SetGain to every AudioRenderer output path.
void AudioObject::ForEachDestLink(const LinkFunction& dest_task) {
fbl::AutoLock links_lock(&links_lock_);
// Callers (generally AudioRenderers) should never be linked to sources.
FXL_DCHECK(source_links_.is_empty());
for (auto& link : dest_links_) {
dest_task(link);
}
}
// Call the provided function for each destination link, until one returns true.
bool AudioObject::ForAnyDestLink(const LinkBoolFunction& dest_task) {
fbl::AutoLock links_lock(&links_lock_);
FXL_DCHECK(source_links_.is_empty());
for (auto& link : dest_links_) {
if (dest_task(link)) {
return true; // This link satisfied the need; we are done.
}
// Else, continue inquiring with the remaining links.
}
return false; // No link satisfied the need.
}
void AudioObject::UnlinkSources() {
typename AudioLink::Set<AudioLink::Source> old_links;
{
fbl::AutoLock lock(&links_lock_);
old_links = std::move(source_links_);
}
UnlinkCleanup<AudioLink::Source>(&old_links);
}
void AudioObject::UnlinkDestinations() {
typename AudioLink::Set<AudioLink::Dest> old_links;
{
fbl::AutoLock lock(&links_lock_);
old_links = std::move(dest_links_);
}
UnlinkCleanup<AudioLink::Dest>(&old_links);
}
zx_status_t AudioObject::InitializeSourceLink(const fbl::RefPtr<AudioLink>&) { return ZX_OK; }
zx_status_t AudioObject::InitializeDestLink(const fbl::RefPtr<AudioLink>&) { return ZX_OK; }
template <typename TagType>
void AudioObject::UnlinkCleanup(typename AudioLink::Set<TagType>* links) {
FXL_DCHECK(links != nullptr);
// Note: we could just range-based for-loop over this set and call RemoveLink on each member.
// Instead, we remove each element from our local set before calling RemoveLinks. This will make a
// future transition to intrusive containers a bit easier. Explanations available on request.
while (!links->is_empty()) {
auto link = links->pop_front();
RemoveLink(link);
link.reset();
}
}
} // namespace media::audio