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 InitiateQueriesToFindNewDestination(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 InitiateQueriesToFindNewDestination(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::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)); |
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. |
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 InitiateQueriesToFindNewDestination(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 |