1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/media/capture/audio_mirroring_manager.h" 6 7#include <algorithm> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/lazy_instance.h" 12 13namespace content { 14 15namespace { 16 17base::LazyInstance<AudioMirroringManager>::Leaky g_audio_mirroring_manager = 18 LAZY_INSTANCE_INITIALIZER; 19 20} // namespace 21 22// static 23AudioMirroringManager* AudioMirroringManager::GetInstance() { 24 return g_audio_mirroring_manager.Pointer(); 25} 26 27AudioMirroringManager::AudioMirroringManager() { 28 // Only *after* construction, check that AudioMirroringManager is being 29 // invoked on the same single thread. 30 thread_checker_.DetachFromThread(); 31} 32 33AudioMirroringManager::~AudioMirroringManager() {} 34 35void AudioMirroringManager::AddDiverter( 36 int render_process_id, int render_frame_id, Diverter* diverter) { 37 DCHECK(thread_checker_.CalledOnValidThread()); 38 DCHECK(diverter); 39 40 // DCHECK(diverter not already in routes_) 41#ifndef NDEBUG 42 for (StreamRoutes::const_iterator it = routes_.begin(); 43 it != routes_.end(); ++it) { 44 DCHECK_NE(diverter, it->diverter); 45 } 46#endif 47 routes_.push_back(StreamRoutingState( 48 SourceFrameRef(render_process_id, render_frame_id), 49 diverter)); 50 51 // Query existing destinations to see whether to immediately start diverting 52 // the stream. 53 std::set<SourceFrameRef> candidates; 54 candidates.insert(routes_.back().source_render_frame); 55 InitiateQueriesToFindNewDestination(NULL, candidates); 56} 57 58void AudioMirroringManager::RemoveDiverter(Diverter* diverter) { 59 DCHECK(thread_checker_.CalledOnValidThread()); 60 61 // Find and remove the entry from the routing table. If the stream is being 62 // diverted, it is stopped. 63 for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) { 64 if (it->diverter == diverter) { 65 ChangeRoute(&(*it), NULL); 66 routes_.erase(it); 67 return; 68 } 69 } 70 NOTREACHED(); 71} 72 73void AudioMirroringManager::StartMirroring(MirroringDestination* destination) { 74 DCHECK(thread_checker_.CalledOnValidThread()); 75 DCHECK(destination); 76 77 // Insert an entry into the set of active mirroring sessions, if this is a 78 // previously-unknown destination. 79 if (std::find(sessions_.begin(), sessions_.end(), destination) == 80 sessions_.end()) { 81 sessions_.push_back(destination); 82 } 83 84 // Query the MirroringDestination to see which of the audio streams should be 85 // diverted. 86 std::set<SourceFrameRef> candidates; 87 for (StreamRoutes::const_iterator it = routes_.begin(); it != routes_.end(); 88 ++it) { 89 if (!it->destination || it->destination == destination) 90 candidates.insert(it->source_render_frame); 91 } 92 if (!candidates.empty()) { 93 destination->QueryForMatches( 94 candidates, 95 base::Bind(&AudioMirroringManager::UpdateRoutesToDestination, 96 base::Unretained(this), 97 destination, 98 false)); 99 } 100} 101 102void AudioMirroringManager::StopMirroring(MirroringDestination* destination) { 103 DCHECK(thread_checker_.CalledOnValidThread()); 104 105 // Stop diverting each audio stream in the mirroring session being stopped. 106 // Each stopped stream becomes a candidate to be diverted to another 107 // destination. 108 std::set<SourceFrameRef> redivert_candidates; 109 for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) { 110 if (it->destination == destination) { 111 ChangeRoute(&(*it), NULL); 112 redivert_candidates.insert(it->source_render_frame); 113 } 114 } 115 if (!redivert_candidates.empty()) 116 InitiateQueriesToFindNewDestination(destination, redivert_candidates); 117 118 // Remove the entry from the set of active mirroring sessions. 119 const Destinations::iterator dest_it = 120 std::find(sessions_.begin(), sessions_.end(), destination); 121 if (dest_it == sessions_.end()) { 122 NOTREACHED(); 123 return; 124 } 125 sessions_.erase(dest_it); 126} 127 128void AudioMirroringManager::InitiateQueriesToFindNewDestination( 129 MirroringDestination* old_destination, 130 const std::set<SourceFrameRef>& candidates) { 131 DCHECK(thread_checker_.CalledOnValidThread()); 132 133 for (Destinations::const_iterator it = sessions_.begin(); 134 it != sessions_.end(); ++it) { 135 if (*it != old_destination) { 136 (*it)->QueryForMatches( 137 candidates, 138 base::Bind(&AudioMirroringManager::UpdateRoutesToDestination, 139 base::Unretained(this), 140 *it, 141 true)); 142 } 143 } 144} 145 146void AudioMirroringManager::UpdateRoutesToDestination( 147 MirroringDestination* destination, 148 bool add_only, 149 const std::set<SourceFrameRef>& matches) { 150 DCHECK(thread_checker_.CalledOnValidThread()); 151 152 if (std::find(sessions_.begin(), sessions_.end(), destination) == 153 sessions_.end()) { 154 return; // Query result callback invoked after StopMirroring(). 155 } 156 157 DVLOG(1) << (add_only ? "Add " : "Replace with ") << matches.size() 158 << " routes to MirroringDestination@" << destination; 159 160 // Start/stop diverting based on |matches|. Any stopped stream becomes a 161 // candidate to be diverted to another destination. 162 std::set<SourceFrameRef> redivert_candidates; 163 for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) { 164 if (matches.find(it->source_render_frame) != matches.end()) { 165 // Only change the route if the stream is not already being diverted. 166 if (!it->destination) 167 ChangeRoute(&(*it), destination); 168 } else if (!add_only) { 169 // Only stop diverting if the stream is currently routed to |destination|. 170 if (it->destination == destination) { 171 ChangeRoute(&(*it), NULL); 172 redivert_candidates.insert(it->source_render_frame); 173 } 174 } 175 } 176 if (!redivert_candidates.empty()) 177 InitiateQueriesToFindNewDestination(destination, redivert_candidates); 178} 179 180// static 181void AudioMirroringManager::ChangeRoute( 182 StreamRoutingState* route, MirroringDestination* new_destination) { 183 if (route->destination == new_destination) 184 return; // No change. 185 186 if (route->destination) { 187 DVLOG(1) << "Stop diverting render_process_id:render_frame_id=" 188 << route->source_render_frame.first << ':' 189 << route->source_render_frame.second 190 << " --> MirroringDestination@" << route->destination; 191 route->diverter->StopDiverting(); 192 route->destination = NULL; 193 } 194 195 if (new_destination) { 196 DVLOG(1) << "Start diverting of render_process_id:render_frame_id=" 197 << route->source_render_frame.first << ':' 198 << route->source_render_frame.second 199 << " --> MirroringDestination@" << new_destination; 200 route->diverter->StartDiverting( 201 new_destination->AddInput(route->diverter->GetAudioParameters())); 202 route->destination = new_destination; 203 } 204} 205 206AudioMirroringManager::StreamRoutingState::StreamRoutingState( 207 const SourceFrameRef& source_frame, Diverter* stream_diverter) 208 : source_render_frame(source_frame), 209 diverter(stream_diverter), 210 destination(NULL) {} 211 212AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {} 213 214} // namespace content 215