Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/media/capture/audio_mirroring_manager.h" | 5 #include "content/browser/media/capture/audio_mirroring_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/bind_helpers.h" | |
| 7 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 8 | 12 |
| 9 namespace content { | 13 namespace content { |
| 10 | 14 |
| 11 namespace { | 15 namespace { |
| 12 | 16 |
| 13 base::LazyInstance<AudioMirroringManager>::Leaky g_audio_mirroring_manager = | 17 base::LazyInstance<AudioMirroringManager>::Leaky g_audio_mirroring_manager = |
| 14 LAZY_INSTANCE_INITIALIZER; | 18 LAZY_INSTANCE_INITIALIZER; |
| 15 | 19 |
| 16 } // namespace | 20 } // namespace |
| 17 | 21 |
| 18 // static | 22 // static |
| 19 AudioMirroringManager* AudioMirroringManager::GetInstance() { | 23 AudioMirroringManager* AudioMirroringManager::GetInstance() { |
| 20 return g_audio_mirroring_manager.Pointer(); | 24 return g_audio_mirroring_manager.Pointer(); |
| 21 } | 25 } |
| 22 | 26 |
| 23 AudioMirroringManager::AudioMirroringManager() { | 27 AudioMirroringManager::AudioMirroringManager() { |
| 24 // Only *after* construction, check that AudioMirroringManager is being | 28 // Only *after* construction, check that AudioMirroringManager is being |
| 25 // invoked on the same single thread. | 29 // invoked on the same single thread. |
| 26 thread_checker_.DetachFromThread(); | 30 thread_checker_.DetachFromThread(); |
| 27 } | 31 } |
| 28 | 32 |
| 29 AudioMirroringManager::~AudioMirroringManager() {} | 33 AudioMirroringManager::~AudioMirroringManager() {} |
| 30 | 34 |
| 31 void AudioMirroringManager::AddDiverter( | 35 void AudioMirroringManager::AddDiverter( |
| 32 int render_process_id, int render_view_id, Diverter* diverter) { | 36 int render_process_id, int render_frame_id, Diverter* diverter) { |
| 33 DCHECK(thread_checker_.CalledOnValidThread()); | 37 DCHECK(thread_checker_.CalledOnValidThread()); |
| 34 DCHECK(diverter); | 38 DCHECK(diverter); |
| 35 | 39 |
| 36 // DCHECK(diverter not already in diverters_ under any key) | 40 // DCHECK(diverter not already in routes_) |
| 37 #ifndef NDEBUG | 41 #ifndef NDEBUG |
| 38 for (DiverterMap::const_iterator it = diverters_.begin(); | 42 for (StreamRoutes::const_iterator it = routes_.begin(); |
| 39 it != diverters_.end(); ++it) { | 43 it != routes_.end(); ++it) { |
| 40 DCHECK_NE(diverter, it->second); | 44 DCHECK_NE(diverter, it->diverter); |
| 41 } | 45 } |
| 42 #endif | 46 #endif |
| 47 routes_.push_back(StreamRoutingState( | |
| 48 SourceFrameRef(render_process_id, render_frame_id), | |
| 49 diverter)); | |
| 43 | 50 |
| 44 // Add the diverter to the set of active diverters. | 51 // Query existing destinations to see whether to immediately start diverting |
| 45 const Target target(render_process_id, render_view_id); | 52 // the stream. |
| 46 diverters_.insert(std::make_pair(target, diverter)); | 53 std::set<SourceFrameRef> candidates; |
| 54 candidates.insert(routes_.back().source_render_frame); | |
| 55 AttemptNewMatches(NULL, candidates); | |
| 56 } | |
| 47 | 57 |
| 48 // If a mirroring session is active, start diverting the audio stream | 58 void AudioMirroringManager::RemoveDiverter(Diverter* diverter) { |
| 49 // immediately. | 59 DCHECK(thread_checker_.CalledOnValidThread()); |
| 50 SessionMap::iterator session_it = sessions_.find(target); | 60 |
| 51 if (session_it != sessions_.end()) { | 61 // Find and remove the entry from the routing table. If the stream is being |
| 52 diverter->StartDiverting( | 62 // diverted, it is stopped. |
| 53 session_it->second->AddInput(diverter->GetAudioParameters())); | 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 | |
| 73 void 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)); | |
| 54 } | 99 } |
| 55 } | 100 } |
| 56 | 101 |
| 57 void AudioMirroringManager::RemoveDiverter( | 102 void AudioMirroringManager::StopMirroring(MirroringDestination* destination) { |
| 58 int render_process_id, int render_view_id, Diverter* diverter) { | |
| 59 DCHECK(thread_checker_.CalledOnValidThread()); | 103 DCHECK(thread_checker_.CalledOnValidThread()); |
| 60 | 104 |
| 61 // Stop diverting the audio stream if a mirroring session is active. | 105 // Stop diverting each audio stream in the mirroring session being stopped. |
| 62 const Target target(render_process_id, render_view_id); | 106 // Each stopped stream becomes a candidate to be diverted to another |
| 63 SessionMap::iterator session_it = sessions_.find(target); | 107 // destination. |
| 64 if (session_it != sessions_.end()) | 108 std::set<SourceFrameRef> redivert_candidates; |
| 65 diverter->StopDiverting(); | 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 AttemptNewMatches(destination, redivert_candidates); | |
| 66 | 117 |
| 67 // Remove the diverter from the set of active diverters. | 118 // Remove the entry from the set of active mirroring sessions. |
| 68 for (DiverterMap::iterator it = diverters_.lower_bound(target); | 119 const Destinations::iterator dest_it = |
| 69 it != diverters_.end() && it->first == target; ++it) { | 120 std::find(sessions_.begin(), sessions_.end(), destination); |
| 70 if (it->second == diverter) { | 121 if (dest_it == sessions_.end()) { |
| 71 diverters_.erase(it); | 122 NOTREACHED(); |
| 72 break; | 123 return; |
| 124 } | |
| 125 sessions_.erase(dest_it); | |
| 126 } | |
| 127 | |
| 128 void AudioMirroringManager::AttemptNewMatches( | |
| 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)); | |
| 73 } | 142 } |
| 74 } | 143 } |
| 75 } | 144 } |
| 76 | 145 |
| 77 void AudioMirroringManager::StartMirroring( | 146 void AudioMirroringManager::UpdateRoutesToDestination( |
| 78 int render_process_id, int render_view_id, | 147 MirroringDestination* destination, |
| 79 MirroringDestination* destination) { | 148 bool add_only, |
| 149 const std::set<SourceFrameRef>& matches) { | |
| 80 DCHECK(thread_checker_.CalledOnValidThread()); | 150 DCHECK(thread_checker_.CalledOnValidThread()); |
| 81 DCHECK(destination); | |
| 82 | 151 |
| 83 // Insert an entry into the set of active mirroring sessions. If a mirroring | 152 if (std::find(sessions_.begin(), sessions_.end(), destination) == |
| 84 // session is already active for |render_process_id| + |render_view_id|, | 153 sessions_.end()) { |
| 85 // replace the entry. | 154 return; // Query result callback invoked after StopMirroring(). |
| 86 const Target target(render_process_id, render_view_id); | |
| 87 SessionMap::iterator session_it = sessions_.find(target); | |
| 88 MirroringDestination* old_destination; | |
| 89 if (session_it == sessions_.end()) { | |
| 90 old_destination = NULL; | |
| 91 sessions_.insert(std::make_pair(target, destination)); | |
| 92 | |
| 93 DVLOG(1) << "Start mirroring render_process_id:render_view_id=" | |
| 94 << render_process_id << ':' << render_view_id | |
| 95 << " --> MirroringDestination@" << destination; | |
| 96 } else { | |
| 97 old_destination = session_it->second; | |
| 98 session_it->second = destination; | |
| 99 | |
| 100 DVLOG(1) << "Switch mirroring of render_process_id:render_view_id=" | |
| 101 << render_process_id << ':' << render_view_id | |
| 102 << " MirroringDestination@" << old_destination | |
| 103 << " --> MirroringDestination@" << destination; | |
| 104 } | 155 } |
| 105 | 156 |
| 106 // Divert audio streams coming from |target| to |destination|. If streams | 157 DVLOG(1) << (add_only ? "Add " : "Replace with ") << matches.size() |
| 107 // were already diverted to the |old_destination|, remove them. | 158 << " routes to MirroringDestination@" << destination; |
| 108 for (DiverterMap::iterator it = diverters_.lower_bound(target); | 159 |
| 109 it != diverters_.end() && it->first == target; ++it) { | 160 // Start/stop diverting based on |matches|. Any stopped stream becomes a |
| 110 Diverter* const diverter = it->second; | 161 // candidate to be diverted to another destination. |
|
DaleCurtis
2014/09/05 22:41:41
It seems that the list of candidates can change be
miu
2014/09/08 22:50:54
I do these in the loop. Line 166 checks that the
| |
| 111 if (old_destination) | 162 std::set<SourceFrameRef> redivert_candidates; |
| 112 diverter->StopDiverting(); | 163 for (StreamRoutes::iterator it = routes_.begin(); it != routes_.end(); ++it) { |
| 113 diverter->StartDiverting( | 164 if (matches.find(it->source_render_frame) != matches.end()) { |
| 114 destination->AddInput(diverter->GetAudioParameters())); | 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 AttemptNewMatches(destination, redivert_candidates); | |
| 178 } | |
| 179 | |
| 180 // static | |
| 181 void 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; | |
| 115 } | 203 } |
| 116 } | 204 } |
| 117 | 205 |
| 118 void AudioMirroringManager::StopMirroring( | 206 AudioMirroringManager::StreamRoutingState::StreamRoutingState( |
| 119 int render_process_id, int render_view_id, | 207 const SourceFrameRef& source_frame, Diverter* stream_diverter) |
| 120 MirroringDestination* destination) { | 208 : source_render_frame(source_frame), |
| 121 DCHECK(thread_checker_.CalledOnValidThread()); | 209 diverter(stream_diverter), |
| 210 destination(NULL) {} | |
| 122 | 211 |
| 123 // Stop mirroring if there is an active session *and* the destination | 212 AudioMirroringManager::StreamRoutingState::~StreamRoutingState() {} |
| 124 // matches. | |
| 125 const Target target(render_process_id, render_view_id); | |
| 126 SessionMap::iterator session_it = sessions_.find(target); | |
| 127 if (session_it == sessions_.end() || destination != session_it->second) | |
| 128 return; | |
| 129 | |
| 130 DVLOG(1) << "Stop mirroring render_process_id:render_view_id=" | |
| 131 << render_process_id << ':' << render_view_id | |
| 132 << " --> MirroringDestination@" << destination; | |
| 133 | |
| 134 // Stop diverting each audio stream in the mirroring session being stopped. | |
| 135 for (DiverterMap::iterator it = diverters_.lower_bound(target); | |
| 136 it != diverters_.end() && it->first == target; ++it) { | |
| 137 it->second->StopDiverting(); | |
| 138 } | |
| 139 | |
| 140 // Remove the entry from the set of active mirroring sessions. | |
| 141 sessions_.erase(session_it); | |
| 142 } | |
| 143 | 213 |
| 144 } // namespace content | 214 } // namespace content |
| OLD | NEW |