blob: 6f4014bc02aa46d390f697a29aedf71534dd5aba [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 <trace/event.h>
#include "src/lib/syslog/cpp/logger.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_renderer_impl.h"
namespace media::audio {
// static
fbl::RefPtr<AudioLink> AudioObject::LinkObjects(const fbl::RefPtr<AudioObject>& source,
const fbl::RefPtr<AudioObject>& dest) {
TRACE_DURATION("audio", "AudioObject::LinkObjects");
// Assert this source is valid (AudioCapturers are disallowed).
FX_DCHECK(source != nullptr);
FX_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).
FX_DCHECK(dest != nullptr);
FX_DCHECK((dest->type() == AudioObject::Type::Output) ||
(dest->type() == AudioObject::Type::AudioCapturer));
// Assert that we are not connecting looped-back-output to output.
FX_DCHECK((source->type() != AudioObject::Type::Output) ||
(dest->type() != AudioObject::Type::Output));
// Create the link.
fbl::RefPtr<AudioLink> link = fbl::MakeRefCounted<AudioLink>(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 then add the link to the proper sets in both source and destination.
{
std::lock_guard<std::mutex> slock(source->links_lock_);
std::lock_guard<std::mutex> dlock(dest->links_lock_);
source->dest_links_.insert(link);
dest->source_links_.insert(link);
}
source->OnLinkAdded();
dest->OnLinkAdded();
return link;
}
// static
void AudioObject::RemoveLink(const fbl::RefPtr<AudioLink>& link) {
TRACE_DURATION("audio", "AudioObject::RemoveLink");
FX_DCHECK(link != nullptr);
link->Invalidate();
const fbl::RefPtr<AudioObject>& source = link->GetSource();
FX_DCHECK(source != nullptr);
{
std::lock_guard<std::mutex> slock(source->links_lock_);
auto iter = source->dest_links_.find(link.get());
if (iter != source->dest_links_.end()) {
source->CleanupDestLink(link);
source->dest_links_.erase(iter);
}
}
const fbl::RefPtr<AudioObject>& dest = link->GetDest();
FX_DCHECK(dest != nullptr);
{
std::lock_guard<std::mutex> dlock(dest->links_lock_);
auto iter = dest->source_links_.find(link.get());
if (iter != dest->source_links_.end()) {
dest->CleanupSourceLink(link);
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) {
TRACE_DURATION("audio", "AudioObject::ForEachSourceLink");
std::lock_guard<std::mutex> links_lock(links_lock_);
// Callers (generally AudioCapturers) should never be linked to destinations.
FX_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) {
TRACE_DURATION("audio", "AudioObject::ForEachDestLink");
std::lock_guard<std::mutex> links_lock(links_lock_);
// Callers (generally AudioRenderers) should never be linked to sources.
FX_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) {
TRACE_DURATION("audio", "AudioObject::ForAnyDestLink");
std::lock_guard<std::mutex> links_lock(links_lock_);
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.
}
bool AudioObject::ForAnySourceLink(const LinkBoolFunction& source_task) {
TRACE_DURATION("audio", "AudioObject::ForAnySourceLink");
std::lock_guard<std::mutex> links_lock(links_lock_);
for (auto& link : dest_links_) {
if (source_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() {
TRACE_DURATION("audio", "AudioObject::UnlinkSources");
typename AudioLink::Set<AudioLink::Source> old_links;
{
std::lock_guard<std::mutex> lock(links_lock_);
old_links = std::move(source_links_);
}
UnlinkCleanup<AudioLink::Source>(&old_links);
}
void AudioObject::UnlinkDestinations() {
TRACE_DURATION("audio", "AudioObject::UnlinkDestinations");
typename AudioLink::Set<AudioLink::Dest> old_links;
{
std::lock_guard<std::mutex> lock(links_lock_);
old_links = std::move(dest_links_);
}
UnlinkCleanup<AudioLink::Dest>(&old_links);
}
template <typename TagType>
void AudioObject::UnlinkCleanup(typename AudioLink::Set<TagType>* links) {
TRACE_DURATION("audio", "AudioObject::UnlinkCleanup");
FX_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 = nullptr;
}
}
} // namespace media::audio