// 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 "base/bind.h" #include "base/bind_helpers.h" #include "base/lazy_instance.h" namespace content { namespace { base::LazyInstance::Leaky g_audio_mirroring_manager = LAZY_INSTANCE_INITIALIZER; } // namespace // static AudioMirroringManager* AudioMirroringManager::GetInstance() { return g_audio_mirroring_manager.Pointer(); } AudioMirroringManager::AudioMirroringManager() { // Only *after* construction, check that AudioMirroringManager is being // invoked on the same single thread. thread_checker_.DetachFromThread(); } AudioMirroringManager::~AudioMirroringManager() {} void AudioMirroringManager::AddDiverter( int render_process_id, int render_frame_id, Diverter* diverter) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(diverter); // 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(NULL, candidates); } void AudioMirroringManager::RemoveDiverter(Diverter* diverter) { DCHECK(thread_checker_.CalledOnValidThread()); // 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) { ChangeRoute(&(*it), NULL); routes_.erase(it); return; } } NOTREACHED(); } void AudioMirroringManager::StartMirroring(MirroringDestination* destination) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(destination); // 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); } // Query the MirroringDestination to see which of the audio streams should be // diverted. std::set candidates; for (StreamRoutes::const_iterator it = routes_.begin(); it != routes_.end(); ++it) { if (!it->destination || it->destination == destination) 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) { DCHECK(thread_checker_.CalledOnValidThread()); // 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) { ChangeRoute(&(*it), NULL); redivert_candidates.insert(it->source_render_frame); } } 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); } void AudioMirroringManager::InitiateQueriesToFindNewDestination( MirroringDestination* old_destination, const std::set& candidates) { DCHECK(thread_checker_.CalledOnValidThread()); for (Destinations::const_iterator it = sessions_.begin(); it != sessions_.end(); ++it) { if (*it != old_destination) { (*it)->QueryForMatches( candidates, base::Bind(&AudioMirroringManager::UpdateRoutesToDestination, base::Unretained(this), *it, true)); } } } void AudioMirroringManager::UpdateRoutesToDestination( MirroringDestination* destination, bool add_only, const std::set& matches) { DCHECK(thread_checker_.CalledOnValidThread()); 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) ChangeRoute(&(*it), destination); } else if (!add_only) { // Only stop diverting if the stream is currently routed to |destination|. if (it->destination == destination) { ChangeRoute(&(*it), NULL); redivert_candidates.insert(it->source_render_frame); } } } if (!redivert_candidates.empty()) InitiateQueriesToFindNewDestination(destination, redivert_candidates); } // static void AudioMirroringManager::ChangeRoute( StreamRoutingState* route, MirroringDestination* new_destination) { 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 = NULL; } 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(NULL) {} AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {} } // namespace content