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/web_contents_audio_input_stream.h" | 5 #include "content/browser/media/capture/web_contents_audio_input_stream.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "base/threading/thread_checker.h" | 13 #include "base/threading/thread_checker.h" |
14 #include "content/browser/media/capture/audio_mirroring_manager.h" | 14 #include "content/browser/media/capture/audio_mirroring_manager.h" |
15 #include "content/browser/media/capture/web_contents_capture_util.h" | 15 #include "content/browser/media/capture/web_contents_capture_util.h" |
16 #include "content/browser/media/capture/web_contents_tracker.h" | 16 #include "content/browser/media/capture/web_contents_tracker.h" |
17 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
| 18 #include "content/public/browser/render_frame_host.h" |
| 19 #include "content/public/browser/web_contents.h" |
18 #include "media/audio/virtual_audio_input_stream.h" | 20 #include "media/audio/virtual_audio_input_stream.h" |
19 #include "media/audio/virtual_audio_output_stream.h" | 21 #include "media/audio/virtual_audio_output_stream.h" |
| 22 #include "media/base/bind_to_current_loop.h" |
20 | 23 |
21 namespace content { | 24 namespace content { |
22 | 25 |
23 class WebContentsAudioInputStream::Impl | 26 class WebContentsAudioInputStream::Impl |
24 : public base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>, | 27 : public base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>, |
25 public AudioMirroringManager::MirroringDestination { | 28 public AudioMirroringManager::MirroringDestination { |
26 public: | 29 public: |
27 // Takes ownership of |mixer_stream|. The rest outlive this instance. | 30 // Takes ownership of |mixer_stream|. The rest outlive this instance. |
28 Impl(int render_process_id, int main_render_frame_id, | 31 Impl(int render_process_id, int main_render_frame_id, |
29 AudioMirroringManager* mirroring_manager, | 32 AudioMirroringManager* mirroring_manager, |
(...skipping 15 matching lines...) Expand all Loading... |
45 void Close(); | 48 void Close(); |
46 | 49 |
47 // Accessor to underlying VirtualAudioInputStream. | 50 // Accessor to underlying VirtualAudioInputStream. |
48 media::VirtualAudioInputStream* mixer_stream() const { | 51 media::VirtualAudioInputStream* mixer_stream() const { |
49 return mixer_stream_.get(); | 52 return mixer_stream_.get(); |
50 } | 53 } |
51 | 54 |
52 private: | 55 private: |
53 friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>; | 56 friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>; |
54 | 57 |
| 58 typedef AudioMirroringManager::SourceFrameRef SourceFrameRef; |
| 59 |
55 enum State { | 60 enum State { |
56 CONSTRUCTED, | 61 CONSTRUCTED, |
57 OPENED, | 62 OPENED, |
58 MIRRORING, | 63 MIRRORING, |
59 CLOSED | 64 CLOSED |
60 }; | 65 }; |
61 | 66 |
62 virtual ~Impl(); | 67 virtual ~Impl(); |
63 | 68 |
64 // Returns true if the mirroring target has been permanently lost. | |
65 bool IsTargetLost() const; | |
66 | |
67 // Notifies the consumer callback that the stream is now dead. | 69 // Notifies the consumer callback that the stream is now dead. |
68 void ReportError(); | 70 void ReportError(); |
69 | 71 |
70 // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO | 72 // (Re-)Start/Stop mirroring by posting a call to AudioMirroringManager on the |
71 // BrowserThread. | 73 // IO BrowserThread. |
72 void StartMirroring(); | 74 void StartMirroring(); |
73 void StopMirroring(); | 75 void StopMirroring(); |
74 | 76 |
75 // AudioMirroringManager::MirroringDestination implementation | 77 // AudioMirroringManager::MirroringDestination implementation |
| 78 virtual void QueryForMatches( |
| 79 const std::set<SourceFrameRef>& candidates, |
| 80 const MatchesCallback& results_callback) OVERRIDE; |
| 81 void QueryForMatchesOnUIThread(const std::set<SourceFrameRef>& candidates, |
| 82 const MatchesCallback& results_callback); |
76 virtual media::AudioOutputStream* AddInput( | 83 virtual media::AudioOutputStream* AddInput( |
77 const media::AudioParameters& params) OVERRIDE; | 84 const media::AudioParameters& params) OVERRIDE; |
78 | 85 |
79 // Callback which is run when |stream| is closed. Deletes |stream|. | 86 // Callback which is run when |stream| is closed. Deletes |stream|. |
80 void ReleaseInput(media::VirtualAudioOutputStream* stream); | 87 void ReleaseInput(media::VirtualAudioOutputStream* stream); |
81 | 88 |
82 // Called by WebContentsTracker when the target of the audio mirroring has | 89 // Called by WebContentsTracker when the target of the audio mirroring has |
83 // changed. | 90 // changed. |
84 void OnTargetChanged(int render_process_id, int render_view_id); | 91 void OnTargetChanged(RenderWidgetHost* target); |
85 | 92 |
86 // Injected dependencies. | 93 // Injected dependencies. |
87 const int initial_render_process_id_; | 94 const int initial_render_process_id_; |
88 const int initial_main_render_frame_id_; | 95 const int initial_main_render_frame_id_; |
89 AudioMirroringManager* const mirroring_manager_; | 96 AudioMirroringManager* const mirroring_manager_; |
90 const scoped_refptr<WebContentsTracker> tracker_; | 97 const scoped_refptr<WebContentsTracker> tracker_; |
91 // The AudioInputStream implementation that handles the audio conversion and | 98 // The AudioInputStream implementation that handles the audio conversion and |
92 // mixing details. | 99 // mixing details. |
93 const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_; | 100 const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_; |
94 | 101 |
95 State state_; | 102 State state_; |
96 | 103 |
97 // Current audio mirroring target. | 104 // Set to true if |tracker_| reports a NULL target, which indicates the target |
98 bool target_identified_; | 105 // is permanently lost. |
99 int target_render_process_id_; | 106 bool is_target_lost_; |
100 int target_render_view_id_; | |
101 | 107 |
102 // Current callback used to consume the resulting mixed audio data. | 108 // Current callback used to consume the resulting mixed audio data. |
103 AudioInputCallback* callback_; | 109 AudioInputCallback* callback_; |
104 | 110 |
105 base::ThreadChecker thread_checker_; | 111 base::ThreadChecker thread_checker_; |
106 | 112 |
107 DISALLOW_COPY_AND_ASSIGN(Impl); | 113 DISALLOW_COPY_AND_ASSIGN(Impl); |
108 }; | 114 }; |
109 | 115 |
110 WebContentsAudioInputStream::Impl::Impl( | 116 WebContentsAudioInputStream::Impl::Impl( |
111 int render_process_id, int main_render_frame_id, | 117 int render_process_id, int main_render_frame_id, |
112 AudioMirroringManager* mirroring_manager, | 118 AudioMirroringManager* mirroring_manager, |
113 const scoped_refptr<WebContentsTracker>& tracker, | 119 const scoped_refptr<WebContentsTracker>& tracker, |
114 media::VirtualAudioInputStream* mixer_stream) | 120 media::VirtualAudioInputStream* mixer_stream) |
115 : initial_render_process_id_(render_process_id), | 121 : initial_render_process_id_(render_process_id), |
116 initial_main_render_frame_id_(main_render_frame_id), | 122 initial_main_render_frame_id_(main_render_frame_id), |
117 mirroring_manager_(mirroring_manager), | 123 mirroring_manager_(mirroring_manager), |
118 tracker_(tracker), | 124 tracker_(tracker), |
119 mixer_stream_(mixer_stream), | 125 mixer_stream_(mixer_stream), |
120 state_(CONSTRUCTED), | 126 state_(CONSTRUCTED), |
121 target_identified_(false), | 127 is_target_lost_(false), |
122 target_render_process_id_(-1), | |
123 target_render_view_id_(-1), | |
124 callback_(NULL) { | 128 callback_(NULL) { |
125 DCHECK(mirroring_manager_); | 129 DCHECK(mirroring_manager_); |
126 DCHECK(tracker_.get()); | 130 DCHECK(tracker_.get()); |
127 DCHECK(mixer_stream_.get()); | 131 DCHECK(mixer_stream_.get()); |
128 | 132 |
129 // WAIS::Impl can be constructed on any thread, but will DCHECK that all | 133 // WAIS::Impl can be constructed on any thread, but will DCHECK that all |
130 // its methods from here on are called from the same thread. | 134 // its methods from here on are called from the same thread. |
131 thread_checker_.DetachFromThread(); | 135 thread_checker_.DetachFromThread(); |
132 } | 136 } |
133 | 137 |
(...skipping 19 matching lines...) Expand all Loading... |
153 } | 157 } |
154 | 158 |
155 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) { | 159 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) { |
156 DCHECK(thread_checker_.CalledOnValidThread()); | 160 DCHECK(thread_checker_.CalledOnValidThread()); |
157 DCHECK(callback); | 161 DCHECK(callback); |
158 | 162 |
159 if (state_ != OPENED) | 163 if (state_ != OPENED) |
160 return; | 164 return; |
161 | 165 |
162 callback_ = callback; | 166 callback_ = callback; |
163 if (IsTargetLost()) { | 167 if (is_target_lost_) { |
164 ReportError(); | 168 ReportError(); |
165 callback_ = NULL; | 169 callback_ = NULL; |
166 return; | 170 return; |
167 } | 171 } |
168 | 172 |
169 state_ = MIRRORING; | 173 state_ = MIRRORING; |
170 mixer_stream_->Start(callback); | 174 mixer_stream_->Start(callback); |
171 | 175 |
172 StartMirroring(); | 176 StartMirroring(); |
173 } | 177 } |
174 | 178 |
175 void WebContentsAudioInputStream::Impl::Stop() { | 179 void WebContentsAudioInputStream::Impl::Stop() { |
176 DCHECK(thread_checker_.CalledOnValidThread()); | 180 DCHECK(thread_checker_.CalledOnValidThread()); |
177 | 181 |
178 if (state_ != MIRRORING) | 182 if (state_ != MIRRORING) |
179 return; | 183 return; |
180 | 184 |
181 state_ = OPENED; | 185 state_ = OPENED; |
182 | 186 |
183 mixer_stream_->Stop(); | 187 mixer_stream_->Stop(); |
184 callback_ = NULL; | 188 callback_ = NULL; |
185 | 189 |
186 if (!IsTargetLost()) | 190 StopMirroring(); |
187 StopMirroring(); | |
188 } | 191 } |
189 | 192 |
190 void WebContentsAudioInputStream::Impl::Close() { | 193 void WebContentsAudioInputStream::Impl::Close() { |
191 DCHECK(thread_checker_.CalledOnValidThread()); | 194 DCHECK(thread_checker_.CalledOnValidThread()); |
192 | 195 |
193 Stop(); | 196 Stop(); |
194 | 197 |
195 if (state_ == OPENED) { | 198 if (state_ == OPENED) { |
196 state_ = CONSTRUCTED; | 199 state_ = CONSTRUCTED; |
197 tracker_->Stop(); | 200 tracker_->Stop(); |
198 mixer_stream_->Close(); | 201 mixer_stream_->Close(); |
199 } | 202 } |
200 | 203 |
201 DCHECK_EQ(CONSTRUCTED, state_); | 204 DCHECK_EQ(CONSTRUCTED, state_); |
202 state_ = CLOSED; | 205 state_ = CLOSED; |
203 } | 206 } |
204 | 207 |
205 bool WebContentsAudioInputStream::Impl::IsTargetLost() const { | |
206 DCHECK(thread_checker_.CalledOnValidThread()); | |
207 if (!target_identified_) | |
208 return false; | |
209 return target_render_process_id_ <= 0 || target_render_view_id_ <= 0; | |
210 } | |
211 | |
212 void WebContentsAudioInputStream::Impl::ReportError() { | 208 void WebContentsAudioInputStream::Impl::ReportError() { |
213 DCHECK(thread_checker_.CalledOnValidThread()); | 209 DCHECK(thread_checker_.CalledOnValidThread()); |
214 | 210 |
215 // TODO(miu): Need clean-up of AudioInputCallback interface in a future | 211 // TODO(miu): Need clean-up of AudioInputCallback interface in a future |
216 // change, since its only implementation ignores the first argument entirely | 212 // change, since its only implementation ignores the first argument entirely |
217 callback_->OnError(NULL); | 213 callback_->OnError(NULL); |
218 } | 214 } |
219 | 215 |
220 void WebContentsAudioInputStream::Impl::StartMirroring() { | 216 void WebContentsAudioInputStream::Impl::StartMirroring() { |
221 DCHECK(thread_checker_.CalledOnValidThread()); | 217 DCHECK(thread_checker_.CalledOnValidThread()); |
222 | 218 |
223 BrowserThread::PostTask( | 219 BrowserThread::PostTask( |
224 BrowserThread::IO, | 220 BrowserThread::IO, |
225 FROM_HERE, | 221 FROM_HERE, |
226 base::Bind(&AudioMirroringManager::StartMirroring, | 222 base::Bind(&AudioMirroringManager::StartMirroring, |
227 base::Unretained(mirroring_manager_), | 223 base::Unretained(mirroring_manager_), |
228 target_render_process_id_, target_render_view_id_, | |
229 make_scoped_refptr(this))); | 224 make_scoped_refptr(this))); |
230 } | 225 } |
231 | 226 |
232 void WebContentsAudioInputStream::Impl::StopMirroring() { | 227 void WebContentsAudioInputStream::Impl::StopMirroring() { |
233 DCHECK(thread_checker_.CalledOnValidThread()); | 228 DCHECK(thread_checker_.CalledOnValidThread()); |
234 | 229 |
235 BrowserThread::PostTask( | 230 BrowserThread::PostTask( |
236 BrowserThread::IO, | 231 BrowserThread::IO, |
237 FROM_HERE, | 232 FROM_HERE, |
238 base::Bind(&AudioMirroringManager::StopMirroring, | 233 base::Bind(&AudioMirroringManager::StopMirroring, |
239 base::Unretained(mirroring_manager_), | 234 base::Unretained(mirroring_manager_), |
240 target_render_process_id_, target_render_view_id_, | |
241 make_scoped_refptr(this))); | 235 make_scoped_refptr(this))); |
242 } | 236 } |
243 | 237 |
| 238 void WebContentsAudioInputStream::Impl::QueryForMatches( |
| 239 const std::set<SourceFrameRef>& candidates, |
| 240 const MatchesCallback& results_callback) { |
| 241 BrowserThread::PostTask( |
| 242 BrowserThread::UI, |
| 243 FROM_HERE, |
| 244 base::Bind(&Impl::QueryForMatchesOnUIThread, |
| 245 this, |
| 246 candidates, |
| 247 media::BindToCurrentLoop(results_callback))); |
| 248 } |
| 249 |
| 250 void WebContentsAudioInputStream::Impl::QueryForMatchesOnUIThread( |
| 251 const std::set<SourceFrameRef>& candidates, |
| 252 const MatchesCallback& results_callback) { |
| 253 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 254 |
| 255 std::set<SourceFrameRef> matches; |
| 256 WebContents* const contents = tracker_->web_contents(); |
| 257 if (contents) { |
| 258 // Add each ID to |matches| if it maps to a RenderFrameHost that maps to the |
| 259 // currently-tracked WebContents. |
| 260 for (std::set<SourceFrameRef>::const_iterator i = candidates.begin(); |
| 261 i != candidates.end(); ++i) { |
| 262 WebContents* const contents_containing_frame = |
| 263 WebContents::FromRenderFrameHost( |
| 264 RenderFrameHost::FromID(i->first, i->second)); |
| 265 if (contents_containing_frame == contents) |
| 266 matches.insert(*i); |
| 267 } |
| 268 } |
| 269 |
| 270 results_callback.Run(matches); |
| 271 } |
| 272 |
244 media::AudioOutputStream* WebContentsAudioInputStream::Impl::AddInput( | 273 media::AudioOutputStream* WebContentsAudioInputStream::Impl::AddInput( |
245 const media::AudioParameters& params) { | 274 const media::AudioParameters& params) { |
246 // Note: The closure created here holds a reference to "this," which will | 275 // Note: The closure created here holds a reference to "this," which will |
247 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the | 276 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the |
248 // VirtualAudioOutputStream. | 277 // VirtualAudioOutputStream. |
249 return new media::VirtualAudioOutputStream( | 278 return new media::VirtualAudioOutputStream( |
250 params, | 279 params, |
251 mixer_stream_.get(), | 280 mixer_stream_.get(), |
252 base::Bind(&Impl::ReleaseInput, this)); | 281 base::Bind(&Impl::ReleaseInput, this)); |
253 } | 282 } |
254 | 283 |
255 void WebContentsAudioInputStream::Impl::ReleaseInput( | 284 void WebContentsAudioInputStream::Impl::ReleaseInput( |
256 media::VirtualAudioOutputStream* stream) { | 285 media::VirtualAudioOutputStream* stream) { |
257 delete stream; | 286 delete stream; |
258 } | 287 } |
259 | 288 |
260 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id, | 289 void WebContentsAudioInputStream::Impl::OnTargetChanged( |
261 int render_view_id) { | 290 RenderWidgetHost* target) { |
262 DCHECK(thread_checker_.CalledOnValidThread()); | 291 DCHECK(thread_checker_.CalledOnValidThread()); |
263 | 292 |
264 if (target_identified_ && | 293 is_target_lost_ = !target; |
265 target_render_process_id_ == render_process_id && | |
266 target_render_view_id_ == render_view_id) { | |
267 return; | |
268 } | |
269 | |
270 DVLOG(1) << "Target RenderView has changed from " | |
271 << target_render_process_id_ << ':' << target_render_view_id_ | |
272 << " to " << render_process_id << ':' << render_view_id; | |
273 | |
274 if (state_ == MIRRORING) | |
275 StopMirroring(); | |
276 | |
277 target_identified_ = true; | |
278 target_render_process_id_ = render_process_id; | |
279 target_render_view_id_ = render_view_id; | |
280 | 294 |
281 if (state_ == MIRRORING) { | 295 if (state_ == MIRRORING) { |
282 if (IsTargetLost()) { | 296 if (is_target_lost_) { |
283 ReportError(); | 297 ReportError(); |
284 Stop(); | 298 Stop(); |
285 } else { | 299 } else { |
286 StartMirroring(); | 300 StartMirroring(); |
287 } | 301 } |
288 } | 302 } |
289 } | 303 } |
290 | 304 |
291 // static | 305 // static |
292 WebContentsAudioInputStream* WebContentsAudioInputStream::Create( | 306 WebContentsAudioInputStream* WebContentsAudioInputStream::Create( |
293 const std::string& device_id, | 307 const std::string& device_id, |
294 const media::AudioParameters& params, | 308 const media::AudioParameters& params, |
295 const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, | 309 const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, |
296 AudioMirroringManager* audio_mirroring_manager) { | 310 AudioMirroringManager* audio_mirroring_manager) { |
297 int render_process_id; | 311 int render_process_id; |
298 int main_render_frame_id; | 312 int main_render_frame_id; |
299 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget( | 313 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget( |
300 device_id, &render_process_id, &main_render_frame_id)) { | 314 device_id, &render_process_id, &main_render_frame_id)) { |
301 return NULL; | 315 return NULL; |
302 } | 316 } |
303 | 317 |
304 return new WebContentsAudioInputStream( | 318 return new WebContentsAudioInputStream( |
305 render_process_id, main_render_frame_id, | 319 render_process_id, main_render_frame_id, |
306 audio_mirroring_manager, | 320 audio_mirroring_manager, |
307 new WebContentsTracker(), | 321 new WebContentsTracker(false), |
308 new media::VirtualAudioInputStream( | 322 new media::VirtualAudioInputStream( |
309 params, worker_task_runner, | 323 params, worker_task_runner, |
310 media::VirtualAudioInputStream::AfterCloseCallback())); | 324 media::VirtualAudioInputStream::AfterCloseCallback())); |
311 } | 325 } |
312 | 326 |
313 WebContentsAudioInputStream::WebContentsAudioInputStream( | 327 WebContentsAudioInputStream::WebContentsAudioInputStream( |
314 int render_process_id, int main_render_frame_id, | 328 int render_process_id, int main_render_frame_id, |
315 AudioMirroringManager* mirroring_manager, | 329 AudioMirroringManager* mirroring_manager, |
316 const scoped_refptr<WebContentsTracker>& tracker, | 330 const scoped_refptr<WebContentsTracker>& tracker, |
317 media::VirtualAudioInputStream* mixer_stream) | 331 media::VirtualAudioInputStream* mixer_stream) |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
351 | 365 |
352 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) { | 366 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) { |
353 impl_->mixer_stream()->SetAutomaticGainControl(enabled); | 367 impl_->mixer_stream()->SetAutomaticGainControl(enabled); |
354 } | 368 } |
355 | 369 |
356 bool WebContentsAudioInputStream::GetAutomaticGainControl() { | 370 bool WebContentsAudioInputStream::GetAutomaticGainControl() { |
357 return impl_->mixer_stream()->GetAutomaticGainControl(); | 371 return impl_->mixer_stream()->GetAutomaticGainControl(); |
358 } | 372 } |
359 | 373 |
360 } // namespace content | 374 } // namespace content |
OLD | NEW |