Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / media / capture / web_contents_audio_input_stream.cc
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/media/capture/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/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"
23
24 namespace content {
25
26 class WebContentsAudioInputStream::Impl
27     : public base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>,
28       public AudioMirroringManager::MirroringDestination {
29  public:
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);
35
36   // Open underlying VirtualAudioInputStream and start tracker.
37   bool Open();
38
39   // Start the underlying VirtualAudioInputStream and instruct
40   // AudioMirroringManager to begin a mirroring session.
41   void Start(AudioInputCallback* callback);
42
43   // Stop the underlying VirtualAudioInputStream and instruct
44   // AudioMirroringManager to shutdown a mirroring session.
45   void Stop();
46
47   // Close the underlying VirtualAudioInputStream and stop the tracker.
48   void Close();
49
50   // Accessor to underlying VirtualAudioInputStream.
51   media::VirtualAudioInputStream* mixer_stream() const {
52     return mixer_stream_.get();
53   }
54
55  private:
56   friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>;
57
58   typedef AudioMirroringManager::SourceFrameRef SourceFrameRef;
59
60   enum State {
61     CONSTRUCTED,
62     OPENED,
63     MIRRORING,
64     CLOSED
65   };
66
67   virtual ~Impl();
68
69   // Notifies the consumer callback that the stream is now dead.
70   void ReportError();
71
72   // (Re-)Start/Stop mirroring by posting a call to AudioMirroringManager on the
73   // IO BrowserThread.
74   void StartMirroring();
75   void StopMirroring();
76
77   // Invoked on the UI thread to make sure WebContents muting is turned off for
78   // successful audio capture.
79   void UnmuteWebContentsAudio();
80
81   // AudioMirroringManager::MirroringDestination implementation
82   virtual void QueryForMatches(
83       const std::set<SourceFrameRef>& candidates,
84       const MatchesCallback& results_callback) OVERRIDE;
85   void QueryForMatchesOnUIThread(const std::set<SourceFrameRef>& candidates,
86                                  const MatchesCallback& results_callback);
87   virtual media::AudioOutputStream* AddInput(
88       const media::AudioParameters& params) OVERRIDE;
89
90   // Callback which is run when |stream| is closed.  Deletes |stream|.
91   void ReleaseInput(media::VirtualAudioOutputStream* stream);
92
93   // Called by WebContentsTracker when the target of the audio mirroring has
94   // changed.
95   void OnTargetChanged(RenderWidgetHost* target);
96
97   // Injected dependencies.
98   const int initial_render_process_id_;
99   const int initial_main_render_frame_id_;
100   AudioMirroringManager* const mirroring_manager_;
101   const scoped_refptr<WebContentsTracker> tracker_;
102   // The AudioInputStream implementation that handles the audio conversion and
103   // mixing details.
104   const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_;
105
106   State state_;
107
108   // Set to true if |tracker_| reports a NULL target, which indicates the target
109   // is permanently lost.
110   bool is_target_lost_;
111
112   // Current callback used to consume the resulting mixed audio data.
113   AudioInputCallback* callback_;
114
115   base::ThreadChecker thread_checker_;
116
117   DISALLOW_COPY_AND_ASSIGN(Impl);
118 };
119
120 WebContentsAudioInputStream::Impl::Impl(
121     int render_process_id, int main_render_frame_id,
122     AudioMirroringManager* mirroring_manager,
123     const scoped_refptr<WebContentsTracker>& tracker,
124     media::VirtualAudioInputStream* mixer_stream)
125     : initial_render_process_id_(render_process_id),
126       initial_main_render_frame_id_(main_render_frame_id),
127       mirroring_manager_(mirroring_manager),
128       tracker_(tracker),
129       mixer_stream_(mixer_stream),
130       state_(CONSTRUCTED),
131       is_target_lost_(false),
132       callback_(NULL) {
133   DCHECK(mirroring_manager_);
134   DCHECK(tracker_.get());
135   DCHECK(mixer_stream_.get());
136
137   // WAIS::Impl can be constructed on any thread, but will DCHECK that all
138   // its methods from here on are called from the same thread.
139   thread_checker_.DetachFromThread();
140 }
141
142 WebContentsAudioInputStream::Impl::~Impl() {
143   DCHECK(state_ == CONSTRUCTED || state_ == CLOSED);
144 }
145
146 bool WebContentsAudioInputStream::Impl::Open() {
147   DCHECK(thread_checker_.CalledOnValidThread());
148
149   DCHECK_EQ(CONSTRUCTED, state_) << "Illegal to Open more than once.";
150
151   if (!mixer_stream_->Open())
152     return false;
153
154   state_ = OPENED;
155
156   tracker_->Start(
157       initial_render_process_id_, initial_main_render_frame_id_,
158       base::Bind(&Impl::OnTargetChanged, this));
159
160   return true;
161 }
162
163 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) {
164   DCHECK(thread_checker_.CalledOnValidThread());
165   DCHECK(callback);
166
167   if (state_ != OPENED)
168     return;
169
170   callback_ = callback;
171   if (is_target_lost_) {
172     ReportError();
173     callback_ = NULL;
174     return;
175   }
176
177   state_ = MIRRORING;
178   mixer_stream_->Start(callback);
179
180   StartMirroring();
181
182   // WebContents audio muting is implemented as audio capture to nowhere.
183   // Unmuting will stop that audio capture, allowing AudioMirroringManager to
184   // divert audio capture to here.
185   BrowserThread::PostTask(
186       BrowserThread::UI,
187       FROM_HERE,
188       base::Bind(&Impl::UnmuteWebContentsAudio, this));
189 }
190
191 void WebContentsAudioInputStream::Impl::Stop() {
192   DCHECK(thread_checker_.CalledOnValidThread());
193
194   if (state_ != MIRRORING)
195     return;
196
197   state_ = OPENED;
198
199   mixer_stream_->Stop();
200   callback_ = NULL;
201
202   StopMirroring();
203 }
204
205 void WebContentsAudioInputStream::Impl::Close() {
206   DCHECK(thread_checker_.CalledOnValidThread());
207
208   Stop();
209
210   if (state_ == OPENED) {
211     state_ = CONSTRUCTED;
212     tracker_->Stop();
213     mixer_stream_->Close();
214   }
215
216   DCHECK_EQ(CONSTRUCTED, state_);
217   state_ = CLOSED;
218 }
219
220 void WebContentsAudioInputStream::Impl::ReportError() {
221   DCHECK(thread_checker_.CalledOnValidThread());
222
223   // TODO(miu): Need clean-up of AudioInputCallback interface in a future
224   // change, since its only implementation ignores the first argument entirely
225   callback_->OnError(NULL);
226 }
227
228 void WebContentsAudioInputStream::Impl::StartMirroring() {
229   DCHECK(thread_checker_.CalledOnValidThread());
230
231   BrowserThread::PostTask(
232       BrowserThread::IO,
233       FROM_HERE,
234       base::Bind(&AudioMirroringManager::StartMirroring,
235                  base::Unretained(mirroring_manager_),
236                  make_scoped_refptr(this)));
237 }
238
239 void WebContentsAudioInputStream::Impl::StopMirroring() {
240   DCHECK(thread_checker_.CalledOnValidThread());
241
242   BrowserThread::PostTask(
243       BrowserThread::IO,
244       FROM_HERE,
245       base::Bind(&AudioMirroringManager::StopMirroring,
246                  base::Unretained(mirroring_manager_),
247                  make_scoped_refptr(this)));
248 }
249
250 void WebContentsAudioInputStream::Impl::UnmuteWebContentsAudio() {
251   DCHECK_CURRENTLY_ON(BrowserThread::UI);
252
253   WebContents* const contents = tracker_->web_contents();
254   if (contents)
255     contents->SetAudioMuted(false);
256 }
257
258 void WebContentsAudioInputStream::Impl::QueryForMatches(
259     const std::set<SourceFrameRef>& candidates,
260     const MatchesCallback& results_callback) {
261   BrowserThread::PostTask(
262       BrowserThread::UI,
263       FROM_HERE,
264       base::Bind(&Impl::QueryForMatchesOnUIThread,
265                  this,
266                  candidates,
267                  media::BindToCurrentLoop(results_callback)));
268 }
269
270 void WebContentsAudioInputStream::Impl::QueryForMatchesOnUIThread(
271     const std::set<SourceFrameRef>& candidates,
272     const MatchesCallback& results_callback) {
273   DCHECK_CURRENTLY_ON(BrowserThread::UI);
274
275   std::set<SourceFrameRef> matches;
276   WebContents* const contents = tracker_->web_contents();
277   if (contents) {
278     // Add each ID to |matches| if it maps to a RenderFrameHost that maps to the
279     // currently-tracked WebContents.
280     for (std::set<SourceFrameRef>::const_iterator i = candidates.begin();
281          i != candidates.end(); ++i) {
282       WebContents* const contents_containing_frame =
283           WebContents::FromRenderFrameHost(
284               RenderFrameHost::FromID(i->first, i->second));
285       if (contents_containing_frame == contents)
286         matches.insert(*i);
287     }
288   }
289
290   results_callback.Run(matches);
291 }
292
293 media::AudioOutputStream* WebContentsAudioInputStream::Impl::AddInput(
294     const media::AudioParameters& params) {
295   // Note: The closure created here holds a reference to "this," which will
296   // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
297   // VirtualAudioOutputStream.
298   return new media::VirtualAudioOutputStream(
299       params,
300       mixer_stream_.get(),
301       base::Bind(&Impl::ReleaseInput, this));
302 }
303
304 void WebContentsAudioInputStream::Impl::ReleaseInput(
305     media::VirtualAudioOutputStream* stream) {
306   delete stream;
307 }
308
309 void WebContentsAudioInputStream::Impl::OnTargetChanged(
310     RenderWidgetHost* target) {
311   DCHECK(thread_checker_.CalledOnValidThread());
312
313   is_target_lost_ = !target;
314
315   if (state_ == MIRRORING) {
316     if (is_target_lost_) {
317       ReportError();
318       Stop();
319     } else {
320       StartMirroring();
321     }
322   }
323 }
324
325 // static
326 WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
327     const std::string& device_id,
328     const media::AudioParameters& params,
329     const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
330     AudioMirroringManager* audio_mirroring_manager) {
331   int render_process_id;
332   int main_render_frame_id;
333   if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
334           device_id, &render_process_id, &main_render_frame_id)) {
335     return NULL;
336   }
337
338   return new WebContentsAudioInputStream(
339       render_process_id, main_render_frame_id,
340       audio_mirroring_manager,
341       new WebContentsTracker(false),
342       new media::VirtualAudioInputStream(
343           params, worker_task_runner,
344           media::VirtualAudioInputStream::AfterCloseCallback()));
345 }
346
347 WebContentsAudioInputStream::WebContentsAudioInputStream(
348     int render_process_id, int main_render_frame_id,
349     AudioMirroringManager* mirroring_manager,
350     const scoped_refptr<WebContentsTracker>& tracker,
351     media::VirtualAudioInputStream* mixer_stream)
352     : impl_(new Impl(render_process_id, main_render_frame_id,
353                      mirroring_manager, tracker, mixer_stream)) {}
354
355 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
356
357 bool WebContentsAudioInputStream::Open() {
358   return impl_->Open();
359 }
360
361 void WebContentsAudioInputStream::Start(AudioInputCallback* callback) {
362   impl_->Start(callback);
363 }
364
365 void WebContentsAudioInputStream::Stop() {
366   impl_->Stop();
367 }
368
369 void WebContentsAudioInputStream::Close() {
370   impl_->Close();
371   delete this;
372 }
373
374 double WebContentsAudioInputStream::GetMaxVolume() {
375   return impl_->mixer_stream()->GetMaxVolume();
376 }
377
378 void WebContentsAudioInputStream::SetVolume(double volume) {
379   impl_->mixer_stream()->SetVolume(volume);
380 }
381
382 double WebContentsAudioInputStream::GetVolume() {
383   return impl_->mixer_stream()->GetVolume();
384 }
385
386 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) {
387   impl_->mixer_stream()->SetAutomaticGainControl(enabled);
388 }
389
390 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
391   return impl_->mixer_stream()->GetAutomaticGainControl();
392 }
393
394 }  // namespace content