OLD | NEW |
---|---|
(Empty) | |
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/renderer_host/media/web_contents_audio_input_stream.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/logging.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/message_loop_proxy.h" | |
14 #include "content/browser/browser_main_loop.h" | |
15 #include "content/browser/renderer_host/media/audio_mirroring_manager.h" | |
16 #include "content/browser/renderer_host/media/web_contents_capture_util.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "media/audio/virtual_audio_input_stream.h" | |
19 #include "media/audio/virtual_audio_output_stream.h" | |
20 | |
21 namespace content { | |
22 | |
23 class WebContentsAudioInputStream::Impl | |
24 : public base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>, | |
25 public AudioMirroringManager::MirroringDestination { | |
26 public: | |
27 // Takes ownership of |mixer_stream|. The rest outlive this instance. | |
28 Impl(int render_process_id, int render_view_id, | |
29 base::MessageLoopProxy* message_loop, | |
30 AudioMirroringManager* mirroring_manager, | |
31 const scoped_refptr<WebContentsTracker>& tracker, | |
32 media::VirtualAudioInputStream* mixer_stream); | |
33 | |
34 // Open underlying VirtualAudioInputStream and start tracker. | |
35 bool Open(); | |
36 | |
37 // Start the underlying VirtualAudioInputStream and instruct | |
38 // AudioMirroringManager to begin a mirroring session. | |
39 void Start(AudioInputCallback* callback); | |
40 | |
41 // Stop the underlying VirtualAudioInputStream and instruct | |
42 // AudioMirroringManager to shutdown a mirroring session. | |
43 void Stop(); | |
44 | |
45 // Close the underlying VirtualAudioInputStream and stop the tracker. | |
46 void Close(); | |
47 | |
48 // Accessor to underlying VirtualAudioInputStream. | |
49 media::VirtualAudioInputStream* mixer_stream() const { | |
50 return mixer_stream_.get(); | |
51 } | |
52 | |
53 private: | |
54 friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>; | |
55 | |
56 enum State { | |
57 CONSTRUCTED, | |
58 OPENED, | |
59 MIRRORING, | |
60 CLOSED | |
61 }; | |
62 | |
63 virtual ~Impl(); | |
64 | |
65 // Returns true if the mirroring target has been permanently lost. | |
66 bool IsTargetLost() const; | |
67 | |
68 // Notifies the consumer callback that the stream is now dead. | |
69 void ReportError(); | |
70 | |
71 // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO | |
72 // BrowserThread. | |
73 void StartMirroring(); | |
74 void StopMirroring(); | |
75 | |
76 // AudioMirroringManager::MirroringDestination implementation | |
77 virtual media::AudioOutputStream* AddInput( | |
78 const media::AudioParameters& params) OVERRIDE; | |
79 | |
80 // Callback which is run when |stream| is closed. Deletes |stream|. | |
81 void ReleaseInput(media::VirtualAudioOutputStream* stream); | |
82 | |
83 // Called by WebContentsTracker when the target of the audio mirroring has | |
84 // changed. | |
85 void OnTargetChanged(int render_process_id, int render_view_id); | |
86 | |
87 // Injected dependencies. | |
88 base::MessageLoopProxy* const message_loop_; | |
89 AudioMirroringManager* const mirroring_manager_; | |
90 const scoped_refptr<WebContentsTracker> tracker_; | |
91 // The AudioInputStream implementation that handles the audio conversion and | |
92 // mixing details. | |
93 const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_; | |
94 | |
95 State state_; | |
96 | |
97 // Current audio mirroring target. | |
98 int target_render_process_id_; | |
99 int target_render_view_id_; | |
100 | |
101 // Current callback used to consume the resulting mixed audio data. | |
102 AudioInputCallback* callback_; | |
103 | |
104 DISALLOW_COPY_AND_ASSIGN(Impl); | |
105 }; | |
106 | |
107 WebContentsAudioInputStream::Impl::Impl( | |
108 int render_process_id, int render_view_id, | |
109 base::MessageLoopProxy* message_loop, | |
110 AudioMirroringManager* mirroring_manager, | |
111 const scoped_refptr<WebContentsTracker>& tracker, | |
112 media::VirtualAudioInputStream* mixer_stream) | |
113 : message_loop_(message_loop), mirroring_manager_(mirroring_manager), | |
114 tracker_(tracker), mixer_stream_(mixer_stream), state_(CONSTRUCTED), | |
115 target_render_process_id_(render_process_id), | |
116 target_render_view_id_(render_view_id), | |
117 callback_(NULL) { | |
118 DCHECK(message_loop_); | |
119 DCHECK(mirroring_manager_); | |
120 DCHECK(tracker_); | |
121 DCHECK(mixer_stream_.get()); | |
122 } | |
123 | |
124 WebContentsAudioInputStream::Impl::~Impl() { | |
125 DCHECK(state_ == CONSTRUCTED || state_ == CLOSED); | |
126 } | |
127 | |
128 bool WebContentsAudioInputStream::Impl::Open() { | |
129 DCHECK(message_loop_->BelongsToCurrentThread()); | |
130 | |
131 DCHECK_EQ(CONSTRUCTED, state_) << "Illegal to Open more than once."; | |
132 | |
133 if (!mixer_stream_->Open()) | |
134 return false; | |
135 | |
136 state_ = OPENED; | |
137 | |
138 tracker_->Start( | |
139 target_render_process_id_, target_render_view_id_, | |
140 base::Bind(&Impl::OnTargetChanged, this)); | |
141 | |
142 return true; | |
143 } | |
144 | |
145 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) { | |
146 DCHECK(message_loop_->BelongsToCurrentThread()); | |
147 DCHECK(callback); | |
148 | |
149 if (state_ != OPENED) | |
150 return; | |
151 | |
152 if (IsTargetLost()) { | |
153 ReportError(); | |
154 return; | |
155 } | |
156 | |
157 state_ = MIRRORING; | |
158 | |
159 callback_ = callback; | |
160 mixer_stream_->Start(callback); | |
161 | |
162 StartMirroring(); | |
163 } | |
164 | |
165 void WebContentsAudioInputStream::Impl::Stop() { | |
166 DCHECK(message_loop_->BelongsToCurrentThread()); | |
167 | |
168 if (state_ != MIRRORING) | |
169 return; | |
170 | |
171 state_ = OPENED; | |
172 | |
173 mixer_stream_->Stop(); | |
174 callback_ = NULL; | |
175 | |
176 if (!IsTargetLost()) | |
177 StopMirroring(); | |
178 } | |
179 | |
180 void WebContentsAudioInputStream::Impl::Close() { | |
181 DCHECK(message_loop_->BelongsToCurrentThread()); | |
182 | |
183 Stop(); | |
184 | |
185 if (state_ == OPENED) { | |
186 state_ = CONSTRUCTED; | |
187 tracker_->Stop(); | |
188 mixer_stream_->Close(); | |
189 } | |
190 | |
191 DCHECK_EQ(CONSTRUCTED, state_); | |
192 state_ = CLOSED; | |
193 } | |
194 | |
195 bool WebContentsAudioInputStream::Impl::IsTargetLost() const { | |
196 DCHECK(message_loop_->BelongsToCurrentThread()); | |
197 | |
198 return target_render_process_id_ <= 0 || target_render_view_id_ <= 0; | |
199 } | |
200 | |
201 void WebContentsAudioInputStream::Impl::ReportError() { | |
202 DCHECK(message_loop_->BelongsToCurrentThread()); | |
203 | |
204 // TODO(miu): Need clean-up of AudioInputCallback interface in a future | |
205 // change, since its only implementation ignores the first argument entirely | |
206 // and the values for the second argument are undefined. | |
207 callback_->OnError(NULL, 0); | |
208 } | |
209 | |
210 void WebContentsAudioInputStream::Impl::StartMirroring() { | |
211 DCHECK(message_loop_->BelongsToCurrentThread()); | |
212 | |
213 BrowserThread::PostTask( | |
214 BrowserThread::IO, | |
215 FROM_HERE, | |
216 base::Bind(&AudioMirroringManager::StartMirroring, | |
217 base::Unretained(mirroring_manager_), | |
218 target_render_process_id_, target_render_view_id_, | |
219 make_scoped_refptr(this))); | |
220 } | |
221 | |
222 void WebContentsAudioInputStream::Impl::StopMirroring() { | |
223 DCHECK(message_loop_->BelongsToCurrentThread()); | |
224 | |
225 BrowserThread::PostTask( | |
226 BrowserThread::IO, | |
227 FROM_HERE, | |
228 base::Bind(&AudioMirroringManager::StopMirroring, | |
229 base::Unretained(mirroring_manager_), | |
230 target_render_process_id_, target_render_view_id_, | |
231 make_scoped_refptr(this))); | |
232 } | |
233 | |
234 media::AudioOutputStream* WebContentsAudioInputStream::Impl::AddInput( | |
235 const media::AudioParameters& params) { | |
236 // Note: The closure created here holds a reference to "this," which will | |
237 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the | |
238 // VirtualAudioOutputStream. | |
239 return new media::VirtualAudioOutputStream( | |
240 params, message_loop_, mixer_stream_.get(), | |
241 base::Bind(&Impl::ReleaseInput, this)); | |
242 } | |
243 | |
244 void WebContentsAudioInputStream::Impl::ReleaseInput( | |
245 media::VirtualAudioOutputStream* stream) { | |
246 delete stream; | |
247 } | |
248 | |
249 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id, | |
250 int render_view_id) { | |
251 DCHECK(message_loop_->BelongsToCurrentThread()); | |
252 | |
253 if (target_render_process_id_ == render_process_id && | |
254 target_render_view_id_ == render_view_id) { | |
255 return; | |
256 } | |
257 | |
258 DVLOG(1) << "Target RenderView has changed from " | |
259 << target_render_process_id_ << ':' << target_render_view_id_ | |
260 << " to " << render_process_id << ':' << render_view_id; | |
261 | |
262 if (state_ == MIRRORING) | |
263 StopMirroring(); | |
264 | |
265 target_render_process_id_ = render_process_id; | |
266 target_render_view_id_ = render_view_id; | |
267 | |
268 if (state_ == MIRRORING) { | |
269 if (IsTargetLost()) { | |
270 ReportError(); | |
271 Stop(); | |
272 } else { | |
273 StartMirroring(); | |
274 } | |
275 } | |
276 } | |
277 | |
278 // static | |
279 WebContentsAudioInputStream* WebContentsAudioInputStream::Create( | |
280 const std::string& device_id, | |
281 const media::AudioParameters& params, | |
282 base::MessageLoopProxy* message_loop) { | |
283 int render_process_id; | |
284 int render_view_id; | |
285 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget( | |
286 device_id, &render_process_id, &render_view_id)) { | |
287 return NULL; | |
288 } | |
289 | |
290 struct Helper { | |
DaleCurtis
2013/01/17 01:15:52
bind_helpers exposes a DoNothing you can use inste
miu
2013/01/17 05:33:55
Fixed. bind_helpers doesn't provide a DoNothing()
| |
291 static void DoNothingAfterClose(media::VirtualAudioInputStream* stream) {} | |
292 }; | |
293 return new WebContentsAudioInputStream( | |
294 render_process_id, render_view_id, message_loop, | |
295 BrowserMainLoop::GetAudioMirroringManager(), | |
296 new WebContentsTracker(), | |
297 new media::VirtualAudioInputStream( | |
298 params, message_loop, base::Bind(&Helper::DoNothingAfterClose))); | |
299 } | |
300 | |
301 WebContentsAudioInputStream::WebContentsAudioInputStream( | |
302 int render_process_id, int render_view_id, | |
303 base::MessageLoopProxy* message_loop, | |
304 AudioMirroringManager* mirroring_manager, | |
305 const scoped_refptr<WebContentsTracker>& tracker, | |
306 media::VirtualAudioInputStream* mixer_stream) | |
307 : impl_(new Impl(render_process_id, render_view_id, message_loop, | |
308 mirroring_manager, tracker, mixer_stream)) {} | |
309 | |
310 WebContentsAudioInputStream::~WebContentsAudioInputStream() {} | |
311 | |
312 bool WebContentsAudioInputStream::Open() { | |
313 return impl_->Open(); | |
314 } | |
315 | |
316 void WebContentsAudioInputStream::Start(AudioInputCallback* callback) { | |
317 impl_->Start(callback); | |
318 } | |
319 | |
320 void WebContentsAudioInputStream::Stop() { | |
321 impl_->Stop(); | |
322 } | |
323 | |
324 void WebContentsAudioInputStream::Close() { | |
325 impl_->Close(); | |
326 delete this; | |
327 } | |
328 | |
329 double WebContentsAudioInputStream::GetMaxVolume() { | |
330 return impl_->mixer_stream()->GetMaxVolume(); | |
331 } | |
332 | |
333 void WebContentsAudioInputStream::SetVolume(double volume) { | |
334 impl_->mixer_stream()->SetVolume(volume); | |
335 } | |
336 | |
337 double WebContentsAudioInputStream::GetVolume() { | |
338 return impl_->mixer_stream()->GetVolume(); | |
339 } | |
340 | |
341 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) { | |
342 impl_->mixer_stream()->SetAutomaticGainControl(enabled); | |
343 } | |
344 | |
345 bool WebContentsAudioInputStream::GetAutomaticGainControl() { | |
346 return impl_->mixer_stream()->GetAutomaticGainControl(); | |
347 } | |
348 | |
349 } // namespace content | |
OLD | NEW |