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 "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 main_render_frame_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 const int initial_render_process_id_
;
88 const int initial_main_render_frame_id_
;
89 AudioMirroringManager
* const mirroring_manager_
;
90 const scoped_refptr
<WebContentsTracker
> tracker_
;
91 // The AudioInputStream implementation that handles the audio conversion and
93 const scoped_ptr
<media::VirtualAudioInputStream
> mixer_stream_
;
97 // Current audio mirroring target.
98 bool target_identified_
;
99 int target_render_process_id_
;
100 int target_render_view_id_
;
102 // Current callback used to consume the resulting mixed audio data.
103 AudioInputCallback
* callback_
;
105 base::ThreadChecker thread_checker_
;
107 DISALLOW_COPY_AND_ASSIGN(Impl
);
110 WebContentsAudioInputStream::Impl::Impl(
111 int render_process_id
, int main_render_frame_id
,
112 AudioMirroringManager
* mirroring_manager
,
113 const scoped_refptr
<WebContentsTracker
>& tracker
,
114 media::VirtualAudioInputStream
* mixer_stream
)
115 : initial_render_process_id_(render_process_id
),
116 initial_main_render_frame_id_(main_render_frame_id
),
117 mirroring_manager_(mirroring_manager
),
119 mixer_stream_(mixer_stream
),
121 target_identified_(false),
122 target_render_process_id_(-1),
123 target_render_view_id_(-1),
125 DCHECK(mirroring_manager_
);
126 DCHECK(tracker_
.get());
127 DCHECK(mixer_stream_
.get());
129 // 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.
131 thread_checker_
.DetachFromThread();
134 WebContentsAudioInputStream::Impl::~Impl() {
135 DCHECK(state_
== CONSTRUCTED
|| state_
== CLOSED
);
138 bool WebContentsAudioInputStream::Impl::Open() {
139 DCHECK(thread_checker_
.CalledOnValidThread());
141 DCHECK_EQ(CONSTRUCTED
, state_
) << "Illegal to Open more than once.";
143 if (!mixer_stream_
->Open())
149 initial_render_process_id_
, initial_main_render_frame_id_
,
150 base::Bind(&Impl::OnTargetChanged
, this));
155 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback
* callback
) {
156 DCHECK(thread_checker_
.CalledOnValidThread());
159 if (state_
!= OPENED
)
162 callback_
= callback
;
163 if (IsTargetLost()) {
170 mixer_stream_
->Start(callback
);
175 void WebContentsAudioInputStream::Impl::Stop() {
176 DCHECK(thread_checker_
.CalledOnValidThread());
178 if (state_
!= MIRRORING
)
183 mixer_stream_
->Stop();
190 void WebContentsAudioInputStream::Impl::Close() {
191 DCHECK(thread_checker_
.CalledOnValidThread());
195 if (state_
== OPENED
) {
196 state_
= CONSTRUCTED
;
198 mixer_stream_
->Close();
201 DCHECK_EQ(CONSTRUCTED
, state_
);
205 bool WebContentsAudioInputStream::Impl::IsTargetLost() const {
206 DCHECK(thread_checker_
.CalledOnValidThread());
207 if (!target_identified_
)
209 return target_render_process_id_
<= 0 || target_render_view_id_
<= 0;
212 void WebContentsAudioInputStream::Impl::ReportError() {
213 DCHECK(thread_checker_
.CalledOnValidThread());
215 // TODO(miu): Need clean-up of AudioInputCallback interface in a future
216 // change, since its only implementation ignores the first argument entirely
217 callback_
->OnError(NULL
);
220 void WebContentsAudioInputStream::Impl::StartMirroring() {
221 DCHECK(thread_checker_
.CalledOnValidThread());
223 BrowserThread::PostTask(
226 base::Bind(&AudioMirroringManager::StartMirroring
,
227 base::Unretained(mirroring_manager_
),
228 target_render_process_id_
, target_render_view_id_
,
229 make_scoped_refptr(this)));
232 void WebContentsAudioInputStream::Impl::StopMirroring() {
233 DCHECK(thread_checker_
.CalledOnValidThread());
235 BrowserThread::PostTask(
238 base::Bind(&AudioMirroringManager::StopMirroring
,
239 base::Unretained(mirroring_manager_
),
240 target_render_process_id_
, target_render_view_id_
,
241 make_scoped_refptr(this)));
244 media::AudioOutputStream
* WebContentsAudioInputStream::Impl::AddInput(
245 const media::AudioParameters
& params
) {
246 // Note: The closure created here holds a reference to "this," which will
247 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
248 // VirtualAudioOutputStream.
249 return new media::VirtualAudioOutputStream(
252 base::Bind(&Impl::ReleaseInput
, this));
255 void WebContentsAudioInputStream::Impl::ReleaseInput(
256 media::VirtualAudioOutputStream
* stream
) {
260 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id
,
261 int render_view_id
) {
262 DCHECK(thread_checker_
.CalledOnValidThread());
264 if (target_identified_
&&
265 target_render_process_id_
== render_process_id
&&
266 target_render_view_id_
== render_view_id
) {
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
;
274 if (state_
== MIRRORING
)
277 target_identified_
= true;
278 target_render_process_id_
= render_process_id
;
279 target_render_view_id_
= render_view_id
;
281 if (state_
== MIRRORING
) {
282 if (IsTargetLost()) {
292 WebContentsAudioInputStream
* WebContentsAudioInputStream::Create(
293 const std::string
& device_id
,
294 const media::AudioParameters
& params
,
295 const scoped_refptr
<base::SingleThreadTaskRunner
>& worker_task_runner
,
296 AudioMirroringManager
* audio_mirroring_manager
) {
297 int render_process_id
;
298 int main_render_frame_id
;
299 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
300 device_id
, &render_process_id
, &main_render_frame_id
)) {
304 return new WebContentsAudioInputStream(
305 render_process_id
, main_render_frame_id
,
306 audio_mirroring_manager
,
307 new WebContentsTracker(),
308 new media::VirtualAudioInputStream(
309 params
, worker_task_runner
,
310 media::VirtualAudioInputStream::AfterCloseCallback()));
313 WebContentsAudioInputStream::WebContentsAudioInputStream(
314 int render_process_id
, int main_render_frame_id
,
315 AudioMirroringManager
* mirroring_manager
,
316 const scoped_refptr
<WebContentsTracker
>& tracker
,
317 media::VirtualAudioInputStream
* mixer_stream
)
318 : impl_(new Impl(render_process_id
, main_render_frame_id
,
319 mirroring_manager
, tracker
, mixer_stream
)) {}
321 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
323 bool WebContentsAudioInputStream::Open() {
324 return impl_
->Open();
327 void WebContentsAudioInputStream::Start(AudioInputCallback
* callback
) {
328 impl_
->Start(callback
);
331 void WebContentsAudioInputStream::Stop() {
335 void WebContentsAudioInputStream::Close() {
340 double WebContentsAudioInputStream::GetMaxVolume() {
341 return impl_
->mixer_stream()->GetMaxVolume();
344 void WebContentsAudioInputStream::SetVolume(double volume
) {
345 impl_
->mixer_stream()->SetVolume(volume
);
348 double WebContentsAudioInputStream::GetVolume() {
349 return impl_
->mixer_stream()->GetVolume();
352 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled
) {
353 impl_
->mixer_stream()->SetAutomaticGainControl(enabled
);
356 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
357 return impl_
->mixer_stream()->GetAutomaticGainControl();
360 } // namespace content