blob: eff29c39c7eacf62b11c5b27cd26a3db41e5dd7e [file] [log] [blame]
// Copyright (c) 2013 The Chromium 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 "content/browser/media/capture/audio_mirroring_manager.h"
#include <stdint.h>
#include <algorithm>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/stl_util.h"
namespace content {
// static
AudioMirroringManager* AudioMirroringManager::GetInstance() {
static AudioMirroringManager* manager = new AudioMirroringManager();
return manager;
}
AudioMirroringManager::AudioMirroringManager() {}
AudioMirroringManager::~AudioMirroringManager() {}
void AudioMirroringManager::AddDiverter(
int render_process_id, int render_frame_id, Diverter* diverter) {
DCHECK(diverter);
base::AutoLock scoped_lock(lock_);
// DCHECK(diverter not already in routes_)
#ifndef NDEBUG
for (StreamRoutes::const_iterator it = routes_.begin();
it != routes_.end(); ++it) {
DCHECK_NE(diverter, it->diverter);
}
#endif
routes_.push_back(StreamRoutingState(
GlobalFrameRoutingId(render_process_id, render_frame_id), diverter));
// Query existing destinations to see whether to immediately start diverting
// the stream.
std::set<GlobalFrameRoutingId> candidates;
candidates.insert(routes_.back().source_render_frame);
InitiateQueriesToFindNewDestination(nullptr, candidates);
}
void AudioMirroringManager::RemoveDiverter(Diverter* diverter) {
base::AutoLock scoped_lock(lock_);
// Find and remove the entry from the routing table. If the stream is being
// diverted, it is stopped.
for (auto it = routes_.begin(); it != routes_.end(); ++it) {
if (it->diverter == diverter) {
// Stop the diverted flow.
RouteDivertedFlow(&(*it), nullptr);
// Stop duplication flows.
for (auto& dup : it->duplications) {
diverter->StopDuplicating(dup.second);
}
routes_.erase(it);
return;
}
}
NOTREACHED();
}
void AudioMirroringManager::StartMirroring(MirroringDestination* destination) {
DCHECK(destination);
base::AutoLock scoped_lock(lock_);
// Insert an entry into the set of active mirroring sessions, if this is a
// previously-unknown destination.
if (!base::Contains(sessions_, destination))
sessions_.push_back(destination);
std::set<GlobalFrameRoutingId> candidates;
// Query the MirroringDestination to see which of the audio streams should be
// diverted.
for (StreamRoutes::const_iterator it = routes_.begin(); it != routes_.end();
++it) {
candidates.insert(it->source_render_frame);
}
if (!candidates.empty()) {
destination->QueryForMatches(
candidates,
base::BindOnce(&AudioMirroringManager::UpdateRoutesToDestination,
base::Unretained(this), destination, false));
}
}
void AudioMirroringManager::StopMirroring(MirroringDestination* destination) {
base::AutoLock scoped_lock(lock_);
// Stop diverting each audio stream in the mirroring session being stopped.
// Each stopped stream becomes a candidate to be diverted to another
// destination.
std::set<GlobalFrameRoutingId> redivert_candidates;
for (auto it = routes_.begin(); it != routes_.end(); ++it) {
if (it->destination == destination) {
RouteDivertedFlow(&(*it), nullptr);
redivert_candidates.insert(it->source_render_frame);
}
auto dup_it = it->duplications.find(destination);
if (dup_it != it->duplications.end()) {
it->diverter->StopDuplicating(dup_it->second);
it->duplications.erase(dup_it);
}
}
if (!redivert_candidates.empty())
InitiateQueriesToFindNewDestination(destination, redivert_candidates);
// Remove the entry from the set of active mirroring sessions.
const Destinations::iterator dest_it =
std::find(sessions_.begin(), sessions_.end(), destination);
if (dest_it == sessions_.end()) {
NOTREACHED();
return;
}
sessions_.erase(dest_it);
}
AudioMirroringManager::AddDiverterCallback
AudioMirroringManager::GetAddDiverterCallback() {
return base::BindRepeating(
[](AudioMirroringManager* self,
const base::UnguessableToken& guessable_token, Diverter* diverter) {
const int render_process_id =
static_cast<int>(guessable_token.GetHighForSerialization());
const int render_frame_id =
static_cast<int>(guessable_token.GetLowForSerialization());
self->AddDiverter(render_process_id, render_frame_id, diverter);
},
base::Unretained(this));
}
AudioMirroringManager::RemoveDiverterCallback
AudioMirroringManager::GetRemoveDiverterCallback() {
return base::BindRepeating(&AudioMirroringManager::RemoveDiverter,
base::Unretained(this));
}
// static
base::UnguessableToken AudioMirroringManager::ToGroupId(int render_process_id,
int render_frame_id) {
return base::UnguessableToken::Deserialize(
static_cast<uint64_t>(render_process_id),
static_cast<uint64_t>(render_frame_id));
}
void AudioMirroringManager::InitiateQueriesToFindNewDestination(
MirroringDestination* old_destination,
const std::set<GlobalFrameRoutingId>& candidates) {
lock_.AssertAcquired();
for (Destinations::const_iterator it = sessions_.begin();
it != sessions_.end(); ++it) {
if (*it == old_destination)
continue;
(*it)->QueryForMatches(
candidates,
base::BindOnce(&AudioMirroringManager::UpdateRoutesToDestination,
base::Unretained(this), *it, true));
}
}
void AudioMirroringManager::UpdateRoutesToDestination(
MirroringDestination* destination,
bool add_only,
const std::set<GlobalFrameRoutingId>& matches,
bool is_duplicate) {
base::AutoLock scoped_lock(lock_);
if (is_duplicate)
UpdateRoutesToDuplicateDestination(destination, add_only, matches);
else
UpdateRoutesToDivertDestination(destination, add_only, matches);
}
void AudioMirroringManager::UpdateRoutesToDivertDestination(
MirroringDestination* destination,
bool add_only,
const std::set<GlobalFrameRoutingId>& matches) {
lock_.AssertAcquired();
if (!base::Contains(sessions_, destination))
return; // Query result callback invoked after StopMirroring().
DVLOG(1) << (add_only ? "Add " : "Replace with ") << matches.size()
<< " routes to MirroringDestination@" << destination;
// Start/stop diverting based on |matches|. Any stopped stream becomes a
// candidate to be diverted to another destination.
std::set<GlobalFrameRoutingId> redivert_candidates;
for (auto it = routes_.begin(); it != routes_.end(); ++it) {
if (matches.find(it->source_render_frame) != matches.end()) {
// Only change the route if the stream is not already being diverted.
if (!it->destination)
RouteDivertedFlow(&(*it), destination);
} else if (!add_only) {
// Only stop diverting if the stream is currently routed to |destination|.
if (it->destination == destination) {
RouteDivertedFlow(&(*it), nullptr);
redivert_candidates.insert(it->source_render_frame);
}
}
}
if (!redivert_candidates.empty())
InitiateQueriesToFindNewDestination(destination, redivert_candidates);
}
void AudioMirroringManager::UpdateRoutesToDuplicateDestination(
MirroringDestination* destination,
bool add_only,
const std::set<GlobalFrameRoutingId>& matches) {
lock_.AssertAcquired();
if (!base::Contains(sessions_, destination))
return; // Query result callback invoked after StopMirroring().
for (auto it = routes_.begin(); it != routes_.end(); ++it) {
if (matches.find(it->source_render_frame) != matches.end()) {
// The same destination cannot have both a diverted audio flow and a
// duplicated flow from the same source.
DCHECK_NE(it->destination, destination);
media::AudioPushSink*& pusher = it->duplications[destination];
if (!pusher) {
pusher = destination->AddPushInput(it->diverter->GetAudioParameters());
DCHECK(pusher);
it->diverter->StartDuplicating(pusher);
}
} else if (!add_only) {
auto dup_it = it->duplications.find(destination);
if (dup_it != it->duplications.end()) {
it->diverter->StopDuplicating(dup_it->second);
it->duplications.erase(dup_it);
}
}
}
}
// static
void AudioMirroringManager::RouteDivertedFlow(
StreamRoutingState* route,
MirroringDestination* new_destination) {
// The same destination cannot have both a diverted audio flow and a
// duplicated flow from the same source.
DCHECK(route->duplications.find(new_destination) ==
route->duplications.end());
if (route->destination == new_destination)
return; // No change.
if (route->destination) {
DVLOG(1) << "Stop diverting render_process_id:render_frame_id="
<< route->source_render_frame.child_id << ':'
<< route->source_render_frame.frame_routing_id
<< " --> MirroringDestination@" << route->destination;
route->diverter->StopDiverting();
route->destination = nullptr;
}
if (new_destination) {
DVLOG(1) << "Start diverting of render_process_id:render_frame_id="
<< route->source_render_frame.child_id << ':'
<< route->source_render_frame.frame_routing_id
<< " --> MirroringDestination@" << new_destination;
route->diverter->StartDiverting(
new_destination->AddInput(route->diverter->GetAudioParameters()));
route->destination = new_destination;
}
}
AudioMirroringManager::StreamRoutingState::StreamRoutingState(
const GlobalFrameRoutingId& source_frame,
Diverter* stream_diverter)
: source_render_frame(source_frame),
diverter(stream_diverter),
destination(nullptr) {}
AudioMirroringManager::StreamRoutingState::StreamRoutingState(
const StreamRoutingState& other) = default;
AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {}
} // namespace content