// 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 #include #include "base/bind.h" #include "base/bind_helpers.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( SourceFrameRef(render_process_id, render_frame_id), diverter)); // Query existing destinations to see whether to immediately start diverting // the stream. std::set 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 (StreamRoutes::iterator 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 (std::find(sessions_.begin(), sessions_.end(), destination) == sessions_.end()) { sessions_.push_back(destination); } std::set 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::Bind(&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 redivert_candidates; for (StreamRoutes::iterator 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(guessable_token.GetHighForSerialization()); const int render_frame_id = static_cast(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(render_process_id), static_cast(render_frame_id)); } void AudioMirroringManager::InitiateQueriesToFindNewDestination( MirroringDestination* old_destination, const std::set& candidates) { lock_.AssertAcquired(); for (Destinations::const_iterator it = sessions_.begin(); it != sessions_.end(); ++it) { if (*it == old_destination) continue; (*it)->QueryForMatches( candidates, base::Bind(&AudioMirroringManager::UpdateRoutesToDestination, base::Unretained(this), *it, true)); } } void AudioMirroringManager::UpdateRoutesToDestination( MirroringDestination* destination, bool add_only, const std::set& 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& matches) { lock_.AssertAcquired(); if (std::find(sessions_.begin(), sessions_.end(), destination) == sessions_.end()) { 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 redivert_candidates; for (StreamRoutes::iterator 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& matches) { lock_.AssertAcquired(); if (std::find(sessions_.begin(), sessions_.end(), destination) == sessions_.end()) { return; // Query result callback invoked after StopMirroring(). } for (StreamRoutes::iterator 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.first << ':' << route->source_render_frame.second << " --> 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.first << ':' << route->source_render_frame.second << " --> MirroringDestination@" << new_destination; route->diverter->StartDiverting( new_destination->AddInput(route->diverter->GetAudioParameters())); route->destination = new_destination; } } AudioMirroringManager::StreamRoutingState::StreamRoutingState( const SourceFrameRef& 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