Home | History | Annotate | Download | only in capture
      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 bool WebContentsAudioInputStream::IsMuted() {
    395   return false;
    396 }
    397 
    398 }  // namespace content
    399