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 |