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.
5 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/threading/thread_checker.h"
14 #include "content/browser/media/capture/audio_mirroring_manager.h"
15 #include "content/browser/media/capture/web_contents_capture_util.h"
16 #include "content/browser/media/capture/web_contents_tracker.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"
20 #include "media/audio/virtual_audio_input_stream.h"
21 #include "media/audio/virtual_audio_output_stream.h"
22 #include "media/base/bind_to_current_loop.h"
26 class WebContentsAudioInputStream::Impl
27 : public base::RefCountedThreadSafe
<WebContentsAudioInputStream::Impl
>,
28 public AudioMirroringManager::MirroringDestination
{
30 // Takes ownership of |mixer_stream|. The rest outlive this instance.
31 Impl(int render_process_id
, int main_render_frame_id
,
32 AudioMirroringManager
* mirroring_manager
,
33 const scoped_refptr
<WebContentsTracker
>& tracker
,
34 media::VirtualAudioInputStream
* mixer_stream
);
36 // Open underlying VirtualAudioInputStream and start tracker.
39 // Start the underlying VirtualAudioInputStream and instruct
40 // AudioMirroringManager to begin a mirroring session.
41 void Start(AudioInputCallback
* callback
);
43 // Stop the underlying VirtualAudioInputStream and instruct
44 // AudioMirroringManager to shutdown a mirroring session.
47 // Close the underlying VirtualAudioInputStream and stop the tracker.
50 // Accessor to underlying VirtualAudioInputStream.
51 media::VirtualAudioInputStream
* mixer_stream() const {
52 return mixer_stream_
.get();
56 friend class base::RefCountedThreadSafe
<WebContentsAudioInputStream::Impl
>;
58 typedef AudioMirroringManager::SourceFrameRef SourceFrameRef
;
69 // Notifies the consumer callback that the stream is now dead.
72 // (Re-)Start/Stop mirroring by posting a call to AudioMirroringManager on the
74 void StartMirroring();
77 // Invoked on the UI thread to make sure WebContents muting is turned off for
78 // successful audio capture.
79 void UnmuteWebContentsAudio();
81 // AudioMirroringManager::MirroringDestination implementation
82 void QueryForMatches(const std::set
<SourceFrameRef
>& candidates
,
83 const MatchesCallback
& results_callback
) override
;
84 void QueryForMatchesOnUIThread(const std::set
<SourceFrameRef
>& candidates
,
85 const MatchesCallback
& results_callback
);
86 media::AudioOutputStream
* AddInput(
87 const media::AudioParameters
& params
) override
;
89 // Callback which is run when |stream| is closed. Deletes |stream|.
90 void ReleaseInput(media::VirtualAudioOutputStream
* stream
);
92 // Called by WebContentsTracker when the target of the audio mirroring has
94 void OnTargetChanged(bool had_target
);
96 // Injected dependencies.
97 const int initial_render_process_id_
;
98 const int initial_main_render_frame_id_
;
99 AudioMirroringManager
* const mirroring_manager_
;
100 const scoped_refptr
<WebContentsTracker
> tracker_
;
101 // The AudioInputStream implementation that handles the audio conversion and
103 const scoped_ptr
<media::VirtualAudioInputStream
> mixer_stream_
;
107 // Set to true if |tracker_| reports a NULL target, which indicates the target
108 // is permanently lost.
109 bool is_target_lost_
;
111 // Current callback used to consume the resulting mixed audio data.
112 AudioInputCallback
* callback_
;
114 base::ThreadChecker thread_checker_
;
116 DISALLOW_COPY_AND_ASSIGN(Impl
);
119 WebContentsAudioInputStream::Impl::Impl(
120 int render_process_id
, int main_render_frame_id
,
121 AudioMirroringManager
* mirroring_manager
,
122 const scoped_refptr
<WebContentsTracker
>& tracker
,
123 media::VirtualAudioInputStream
* mixer_stream
)
124 : initial_render_process_id_(render_process_id
),
125 initial_main_render_frame_id_(main_render_frame_id
),
126 mirroring_manager_(mirroring_manager
),
128 mixer_stream_(mixer_stream
),
130 is_target_lost_(false),
132 DCHECK(mirroring_manager_
);
133 DCHECK(tracker_
.get());
134 DCHECK(mixer_stream_
.get());
136 // WAIS::Impl can be constructed on any thread, but will DCHECK that all
137 // its methods from here on are called from the same thread.
138 thread_checker_
.DetachFromThread();
141 WebContentsAudioInputStream::Impl::~Impl() {
142 DCHECK(state_
== CONSTRUCTED
|| state_
== CLOSED
);
145 bool WebContentsAudioInputStream::Impl::Open() {
146 DCHECK(thread_checker_
.CalledOnValidThread());
148 DCHECK_EQ(CONSTRUCTED
, state_
) << "Illegal to Open more than once.";
150 if (!mixer_stream_
->Open())
156 initial_render_process_id_
, initial_main_render_frame_id_
,
157 base::Bind(&Impl::OnTargetChanged
, this));
162 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback
* callback
) {
163 DCHECK(thread_checker_
.CalledOnValidThread());
166 if (state_
!= OPENED
)
169 callback_
= callback
;
170 if (is_target_lost_
) {
177 mixer_stream_
->Start(callback
);
181 // WebContents audio muting is implemented as audio capture to nowhere.
182 // Unmuting will stop that audio capture, allowing AudioMirroringManager to
183 // divert audio capture to here.
184 BrowserThread::PostTask(
187 base::Bind(&Impl::UnmuteWebContentsAudio
, this));
190 void WebContentsAudioInputStream::Impl::Stop() {
191 DCHECK(thread_checker_
.CalledOnValidThread());
193 if (state_
!= MIRRORING
)
198 mixer_stream_
->Stop();
204 void WebContentsAudioInputStream::Impl::Close() {
205 DCHECK(thread_checker_
.CalledOnValidThread());
209 if (state_
== OPENED
) {
210 state_
= CONSTRUCTED
;
212 mixer_stream_
->Close();
215 DCHECK_EQ(CONSTRUCTED
, state_
);
219 void WebContentsAudioInputStream::Impl::ReportError() {
220 DCHECK(thread_checker_
.CalledOnValidThread());
222 // TODO(miu): Need clean-up of AudioInputCallback interface in a future
223 // change, since its only implementation ignores the first argument entirely
224 callback_
->OnError(NULL
);
227 void WebContentsAudioInputStream::Impl::StartMirroring() {
228 DCHECK(thread_checker_
.CalledOnValidThread());
230 BrowserThread::PostTask(
233 base::Bind(&AudioMirroringManager::StartMirroring
,
234 base::Unretained(mirroring_manager_
),
235 make_scoped_refptr(this)));
238 void WebContentsAudioInputStream::Impl::StopMirroring() {
239 DCHECK(thread_checker_
.CalledOnValidThread());
241 BrowserThread::PostTask(
244 base::Bind(&AudioMirroringManager::StopMirroring
,
245 base::Unretained(mirroring_manager_
),
246 make_scoped_refptr(this)));
249 void WebContentsAudioInputStream::Impl::UnmuteWebContentsAudio() {
250 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
252 WebContents
* const contents
= tracker_
->web_contents();
254 contents
->SetAudioMuted(false);
257 void WebContentsAudioInputStream::Impl::QueryForMatches(
258 const std::set
<SourceFrameRef
>& candidates
,
259 const MatchesCallback
& results_callback
) {
260 BrowserThread::PostTask(
263 base::Bind(&Impl::QueryForMatchesOnUIThread
,
266 media::BindToCurrentLoop(results_callback
)));
269 void WebContentsAudioInputStream::Impl::QueryForMatchesOnUIThread(
270 const std::set
<SourceFrameRef
>& candidates
,
271 const MatchesCallback
& results_callback
) {
272 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
274 std::set
<SourceFrameRef
> matches
;
275 WebContents
* const contents
= tracker_
->web_contents();
277 // Add each ID to |matches| if it maps to a RenderFrameHost that maps to the
278 // currently-tracked WebContents.
279 for (std::set
<SourceFrameRef
>::const_iterator i
= candidates
.begin();
280 i
!= candidates
.end(); ++i
) {
281 WebContents
* const contents_containing_frame
=
282 WebContents::FromRenderFrameHost(
283 RenderFrameHost::FromID(i
->first
, i
->second
));
284 if (contents_containing_frame
== contents
)
289 results_callback
.Run(matches
);
292 media::AudioOutputStream
* WebContentsAudioInputStream::Impl::AddInput(
293 const media::AudioParameters
& params
) {
294 // Note: The closure created here holds a reference to "this," which will
295 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
296 // VirtualAudioOutputStream.
297 return new media::VirtualAudioOutputStream(
300 base::Bind(&Impl::ReleaseInput
, this));
303 void WebContentsAudioInputStream::Impl::ReleaseInput(
304 media::VirtualAudioOutputStream
* stream
) {
308 void WebContentsAudioInputStream::Impl::OnTargetChanged(bool had_target
) {
309 DCHECK(thread_checker_
.CalledOnValidThread());
311 is_target_lost_
= !had_target
;
313 if (state_
== MIRRORING
) {
314 if (is_target_lost_
) {
324 WebContentsAudioInputStream
* WebContentsAudioInputStream::Create(
325 const std::string
& device_id
,
326 const media::AudioParameters
& params
,
327 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner
,
328 AudioMirroringManager
* audio_mirroring_manager
) {
329 int render_process_id
;
330 int main_render_frame_id
;
331 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
332 device_id
, &render_process_id
, &main_render_frame_id
)) {
336 return new WebContentsAudioInputStream(
337 render_process_id
, main_render_frame_id
,
338 audio_mirroring_manager
,
339 new WebContentsTracker(false),
340 new media::VirtualAudioInputStream(
341 params
, worker_task_runner
,
342 media::VirtualAudioInputStream::AfterCloseCallback()));
345 WebContentsAudioInputStream::WebContentsAudioInputStream(
346 int render_process_id
, int main_render_frame_id
,
347 AudioMirroringManager
* mirroring_manager
,
348 const scoped_refptr
<WebContentsTracker
>& tracker
,
349 media::VirtualAudioInputStream
* mixer_stream
)
350 : impl_(new Impl(render_process_id
, main_render_frame_id
,
351 mirroring_manager
, tracker
, mixer_stream
)) {}
353 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
355 bool WebContentsAudioInputStream::Open() {
356 return impl_
->Open();
359 void WebContentsAudioInputStream::Start(AudioInputCallback
* callback
) {
360 impl_
->Start(callback
);
363 void WebContentsAudioInputStream::Stop() {
367 void WebContentsAudioInputStream::Close() {
372 double WebContentsAudioInputStream::GetMaxVolume() {
373 return impl_
->mixer_stream()->GetMaxVolume();
376 void WebContentsAudioInputStream::SetVolume(double volume
) {
377 impl_
->mixer_stream()->SetVolume(volume
);
380 double WebContentsAudioInputStream::GetVolume() {
381 return impl_
->mixer_stream()->GetVolume();
384 bool WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled
) {
385 return impl_
->mixer_stream()->SetAutomaticGainControl(enabled
);
388 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
389 return impl_
->mixer_stream()->GetAutomaticGainControl();
392 bool WebContentsAudioInputStream::IsMuted() {
396 } // namespace content