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/renderer_host/media/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/renderer_host/media/audio_mirroring_manager.h"
15 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
16 #include "content/browser/renderer_host/media/web_contents_tracker.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"
23 class WebContentsAudioInputStream::Impl
24 : public base::RefCountedThreadSafe
<WebContentsAudioInputStream::Impl
>,
25 public AudioMirroringManager::MirroringDestination
{
27 // Takes ownership of |mixer_stream|. The rest outlive this instance.
28 Impl(int render_process_id
, int render_view_id
,
29 AudioMirroringManager
* mirroring_manager
,
30 const scoped_refptr
<WebContentsTracker
>& tracker
,
31 media::VirtualAudioInputStream
* mixer_stream
);
33 // Open underlying VirtualAudioInputStream and start tracker.
36 // Start the underlying VirtualAudioInputStream and instruct
37 // AudioMirroringManager to begin a mirroring session.
38 void Start(AudioInputCallback
* callback
);
40 // Stop the underlying VirtualAudioInputStream and instruct
41 // AudioMirroringManager to shutdown a mirroring session.
44 // Close the underlying VirtualAudioInputStream and stop the tracker.
47 // Accessor to underlying VirtualAudioInputStream.
48 media::VirtualAudioInputStream
* mixer_stream() const {
49 return mixer_stream_
.get();
53 friend class base::RefCountedThreadSafe
<WebContentsAudioInputStream::Impl
>;
64 // Returns true if the mirroring target has been permanently lost.
65 bool IsTargetLost() const;
67 // Notifies the consumer callback that the stream is now dead.
70 // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO
72 void StartMirroring();
75 // AudioMirroringManager::MirroringDestination implementation
76 virtual media::AudioOutputStream
* AddInput(
77 const media::AudioParameters
& params
) OVERRIDE
;
79 // Callback which is run when |stream| is closed. Deletes |stream|.
80 void ReleaseInput(media::VirtualAudioOutputStream
* stream
);
82 // Called by WebContentsTracker when the target of the audio mirroring has
84 void OnTargetChanged(int render_process_id
, int render_view_id
);
86 // Injected dependencies.
87 AudioMirroringManager
* const mirroring_manager_
;
88 const scoped_refptr
<WebContentsTracker
> tracker_
;
89 // The AudioInputStream implementation that handles the audio conversion and
91 const scoped_ptr
<media::VirtualAudioInputStream
> mixer_stream_
;
95 // Current audio mirroring target.
96 int target_render_process_id_
;
97 int target_render_view_id_
;
99 // Current callback used to consume the resulting mixed audio data.
100 AudioInputCallback
* callback_
;
102 base::ThreadChecker thread_checker_
;
104 DISALLOW_COPY_AND_ASSIGN(Impl
);
107 WebContentsAudioInputStream::Impl::Impl(
108 int render_process_id
, int render_view_id
,
109 AudioMirroringManager
* mirroring_manager
,
110 const scoped_refptr
<WebContentsTracker
>& tracker
,
111 media::VirtualAudioInputStream
* mixer_stream
)
112 : mirroring_manager_(mirroring_manager
),
113 tracker_(tracker
), mixer_stream_(mixer_stream
), state_(CONSTRUCTED
),
114 target_render_process_id_(render_process_id
),
115 target_render_view_id_(render_view_id
),
117 DCHECK(mirroring_manager_
);
118 DCHECK(tracker_
.get());
119 DCHECK(mixer_stream_
.get());
121 // WAIS::Impl can be constructed on any thread, but will DCHECK that all
122 // its methods from here on are called from the same thread.
123 thread_checker_
.DetachFromThread();
126 WebContentsAudioInputStream::Impl::~Impl() {
127 DCHECK(state_
== CONSTRUCTED
|| state_
== CLOSED
);
130 bool WebContentsAudioInputStream::Impl::Open() {
131 DCHECK(thread_checker_
.CalledOnValidThread());
133 DCHECK_EQ(CONSTRUCTED
, state_
) << "Illegal to Open more than once.";
135 if (!mixer_stream_
->Open())
141 target_render_process_id_
, target_render_view_id_
,
142 base::Bind(&Impl::OnTargetChanged
, this));
147 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback
* callback
) {
148 DCHECK(thread_checker_
.CalledOnValidThread());
151 if (state_
!= OPENED
)
154 callback_
= callback
;
155 if (IsTargetLost()) {
162 mixer_stream_
->Start(callback
);
167 void WebContentsAudioInputStream::Impl::Stop() {
168 DCHECK(thread_checker_
.CalledOnValidThread());
170 if (state_
!= MIRRORING
)
175 mixer_stream_
->Stop();
182 void WebContentsAudioInputStream::Impl::Close() {
183 DCHECK(thread_checker_
.CalledOnValidThread());
187 if (state_
== OPENED
) {
188 state_
= CONSTRUCTED
;
190 mixer_stream_
->Close();
193 DCHECK_EQ(CONSTRUCTED
, state_
);
197 bool WebContentsAudioInputStream::Impl::IsTargetLost() const {
198 DCHECK(thread_checker_
.CalledOnValidThread());
200 return target_render_process_id_
<= 0 || target_render_view_id_
<= 0;
203 void WebContentsAudioInputStream::Impl::ReportError() {
204 DCHECK(thread_checker_
.CalledOnValidThread());
206 // TODO(miu): Need clean-up of AudioInputCallback interface in a future
207 // change, since its only implementation ignores the first argument entirely
208 callback_
->OnError(NULL
);
211 void WebContentsAudioInputStream::Impl::StartMirroring() {
212 DCHECK(thread_checker_
.CalledOnValidThread());
214 BrowserThread::PostTask(
217 base::Bind(&AudioMirroringManager::StartMirroring
,
218 base::Unretained(mirroring_manager_
),
219 target_render_process_id_
, target_render_view_id_
,
220 make_scoped_refptr(this)));
223 void WebContentsAudioInputStream::Impl::StopMirroring() {
224 DCHECK(thread_checker_
.CalledOnValidThread());
226 BrowserThread::PostTask(
229 base::Bind(&AudioMirroringManager::StopMirroring
,
230 base::Unretained(mirroring_manager_
),
231 target_render_process_id_
, target_render_view_id_
,
232 make_scoped_refptr(this)));
235 media::AudioOutputStream
* WebContentsAudioInputStream::Impl::AddInput(
236 const media::AudioParameters
& params
) {
237 // Note: The closure created here holds a reference to "this," which will
238 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
239 // VirtualAudioOutputStream.
240 return new media::VirtualAudioOutputStream(
243 base::Bind(&Impl::ReleaseInput
, this));
246 void WebContentsAudioInputStream::Impl::ReleaseInput(
247 media::VirtualAudioOutputStream
* stream
) {
251 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id
,
252 int render_view_id
) {
253 DCHECK(thread_checker_
.CalledOnValidThread());
255 if (target_render_process_id_
== render_process_id
&&
256 target_render_view_id_
== render_view_id
) {
260 DVLOG(1) << "Target RenderView has changed from "
261 << target_render_process_id_
<< ':' << target_render_view_id_
262 << " to " << render_process_id
<< ':' << render_view_id
;
264 if (state_
== MIRRORING
)
267 target_render_process_id_
= render_process_id
;
268 target_render_view_id_
= render_view_id
;
270 if (state_
== MIRRORING
) {
271 if (IsTargetLost()) {
281 WebContentsAudioInputStream
* WebContentsAudioInputStream::Create(
282 const std::string
& device_id
,
283 const media::AudioParameters
& params
,
284 const scoped_refptr
<base::MessageLoopProxy
>& worker_loop
,
285 AudioMirroringManager
* audio_mirroring_manager
) {
286 int render_process_id
;
288 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
289 device_id
, &render_process_id
, &render_view_id
)) {
293 return new WebContentsAudioInputStream(
294 render_process_id
, render_view_id
,
295 audio_mirroring_manager
,
296 new WebContentsTracker(),
297 new media::VirtualAudioInputStream(
299 media::VirtualAudioInputStream::AfterCloseCallback()));
302 WebContentsAudioInputStream::WebContentsAudioInputStream(
303 int render_process_id
, int render_view_id
,
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
,
308 mirroring_manager
, tracker
, mixer_stream
)) {}
310 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
312 bool WebContentsAudioInputStream::Open() {
313 return impl_
->Open();
316 void WebContentsAudioInputStream::Start(AudioInputCallback
* callback
) {
317 impl_
->Start(callback
);
320 void WebContentsAudioInputStream::Stop() {
324 void WebContentsAudioInputStream::Close() {
329 double WebContentsAudioInputStream::GetMaxVolume() {
330 return impl_
->mixer_stream()->GetMaxVolume();
333 void WebContentsAudioInputStream::SetVolume(double volume
) {
334 impl_
->mixer_stream()->SetVolume(volume
);
337 double WebContentsAudioInputStream::GetVolume() {
338 return impl_
->mixer_stream()->GetVolume();
341 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled
) {
342 impl_
->mixer_stream()->SetAutomaticGainControl(enabled
);
345 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
346 return impl_
->mixer_stream()->GetAutomaticGainControl();
349 } // namespace content