Home | History | Annotate | Download | only in media
      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/renderer_host/media/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/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"
     20 
     21 namespace content {
     22 
     23 class WebContentsAudioInputStream::Impl
     24     : public base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>,
     25       public AudioMirroringManager::MirroringDestination {
     26  public:
     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);
     32 
     33   // Open underlying VirtualAudioInputStream and start tracker.
     34   bool Open();
     35 
     36   // Start the underlying VirtualAudioInputStream and instruct
     37   // AudioMirroringManager to begin a mirroring session.
     38   void Start(AudioInputCallback* callback);
     39 
     40   // Stop the underlying VirtualAudioInputStream and instruct
     41   // AudioMirroringManager to shutdown a mirroring session.
     42   void Stop();
     43 
     44   // Close the underlying VirtualAudioInputStream and stop the tracker.
     45   void Close();
     46 
     47   // Accessor to underlying VirtualAudioInputStream.
     48   media::VirtualAudioInputStream* mixer_stream() const {
     49     return mixer_stream_.get();
     50   }
     51 
     52  private:
     53   friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>;
     54 
     55   enum State {
     56     CONSTRUCTED,
     57     OPENED,
     58     MIRRORING,
     59     CLOSED
     60   };
     61 
     62   virtual ~Impl();
     63 
     64   // Returns true if the mirroring target has been permanently lost.
     65   bool IsTargetLost() const;
     66 
     67   // Notifies the consumer callback that the stream is now dead.
     68   void ReportError();
     69 
     70   // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO
     71   // BrowserThread.
     72   void StartMirroring();
     73   void StopMirroring();
     74 
     75   // AudioMirroringManager::MirroringDestination implementation
     76   virtual media::AudioOutputStream* AddInput(
     77       const media::AudioParameters& params) OVERRIDE;
     78 
     79   // Callback which is run when |stream| is closed.  Deletes |stream|.
     80   void ReleaseInput(media::VirtualAudioOutputStream* stream);
     81 
     82   // Called by WebContentsTracker when the target of the audio mirroring has
     83   // changed.
     84   void OnTargetChanged(int render_process_id, int render_view_id);
     85 
     86   // Injected dependencies.
     87   AudioMirroringManager* const mirroring_manager_;
     88   const scoped_refptr<WebContentsTracker> tracker_;
     89   // The AudioInputStream implementation that handles the audio conversion and
     90   // mixing details.
     91   const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_;
     92 
     93   State state_;
     94 
     95   // Current audio mirroring target.
     96   int target_render_process_id_;
     97   int target_render_view_id_;
     98 
     99   // Current callback used to consume the resulting mixed audio data.
    100   AudioInputCallback* callback_;
    101 
    102   base::ThreadChecker thread_checker_;
    103 
    104   DISALLOW_COPY_AND_ASSIGN(Impl);
    105 };
    106 
    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),
    116       callback_(NULL) {
    117   DCHECK(mirroring_manager_);
    118   DCHECK(tracker_.get());
    119   DCHECK(mixer_stream_.get());
    120 
    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();
    124 }
    125 
    126 WebContentsAudioInputStream::Impl::~Impl() {
    127   DCHECK(state_ == CONSTRUCTED || state_ == CLOSED);
    128 }
    129 
    130 bool WebContentsAudioInputStream::Impl::Open() {
    131   DCHECK(thread_checker_.CalledOnValidThread());
    132 
    133   DCHECK_EQ(CONSTRUCTED, state_) << "Illegal to Open more than once.";
    134 
    135   if (!mixer_stream_->Open())
    136     return false;
    137 
    138   state_ = OPENED;
    139 
    140   tracker_->Start(
    141       target_render_process_id_, target_render_view_id_,
    142       base::Bind(&Impl::OnTargetChanged, this));
    143 
    144   return true;
    145 }
    146 
    147 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) {
    148   DCHECK(thread_checker_.CalledOnValidThread());
    149   DCHECK(callback);
    150 
    151   if (state_ != OPENED)
    152     return;
    153 
    154   callback_ = callback;
    155   if (IsTargetLost()) {
    156     ReportError();
    157     callback_ = NULL;
    158     return;
    159   }
    160 
    161   state_ = MIRRORING;
    162   mixer_stream_->Start(callback);
    163 
    164   StartMirroring();
    165 }
    166 
    167 void WebContentsAudioInputStream::Impl::Stop() {
    168   DCHECK(thread_checker_.CalledOnValidThread());
    169 
    170   if (state_ != MIRRORING)
    171     return;
    172 
    173   state_ = OPENED;
    174 
    175   mixer_stream_->Stop();
    176   callback_ = NULL;
    177 
    178   if (!IsTargetLost())
    179     StopMirroring();
    180 }
    181 
    182 void WebContentsAudioInputStream::Impl::Close() {
    183   DCHECK(thread_checker_.CalledOnValidThread());
    184 
    185   Stop();
    186 
    187   if (state_ == OPENED) {
    188     state_ = CONSTRUCTED;
    189     tracker_->Stop();
    190     mixer_stream_->Close();
    191   }
    192 
    193   DCHECK_EQ(CONSTRUCTED, state_);
    194   state_ = CLOSED;
    195 }
    196 
    197 bool WebContentsAudioInputStream::Impl::IsTargetLost() const {
    198   DCHECK(thread_checker_.CalledOnValidThread());
    199 
    200   return target_render_process_id_ <= 0 || target_render_view_id_ <= 0;
    201 }
    202 
    203 void WebContentsAudioInputStream::Impl::ReportError() {
    204   DCHECK(thread_checker_.CalledOnValidThread());
    205 
    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);
    209 }
    210 
    211 void WebContentsAudioInputStream::Impl::StartMirroring() {
    212   DCHECK(thread_checker_.CalledOnValidThread());
    213 
    214   BrowserThread::PostTask(
    215       BrowserThread::IO,
    216       FROM_HERE,
    217       base::Bind(&AudioMirroringManager::StartMirroring,
    218                  base::Unretained(mirroring_manager_),
    219                  target_render_process_id_, target_render_view_id_,
    220                  make_scoped_refptr(this)));
    221 }
    222 
    223 void WebContentsAudioInputStream::Impl::StopMirroring() {
    224   DCHECK(thread_checker_.CalledOnValidThread());
    225 
    226   BrowserThread::PostTask(
    227       BrowserThread::IO,
    228       FROM_HERE,
    229       base::Bind(&AudioMirroringManager::StopMirroring,
    230                  base::Unretained(mirroring_manager_),
    231                  target_render_process_id_, target_render_view_id_,
    232                  make_scoped_refptr(this)));
    233 }
    234 
    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(
    241       params,
    242       mixer_stream_.get(),
    243       base::Bind(&Impl::ReleaseInput, this));
    244 }
    245 
    246 void WebContentsAudioInputStream::Impl::ReleaseInput(
    247     media::VirtualAudioOutputStream* stream) {
    248   delete stream;
    249 }
    250 
    251 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
    252                                                         int render_view_id) {
    253   DCHECK(thread_checker_.CalledOnValidThread());
    254 
    255   if (target_render_process_id_ == render_process_id &&
    256       target_render_view_id_ == render_view_id) {
    257     return;
    258   }
    259 
    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;
    263 
    264   if (state_ == MIRRORING)
    265     StopMirroring();
    266 
    267   target_render_process_id_ = render_process_id;
    268   target_render_view_id_ = render_view_id;
    269 
    270   if (state_ == MIRRORING) {
    271     if (IsTargetLost()) {
    272       ReportError();
    273       Stop();
    274     } else {
    275       StartMirroring();
    276     }
    277   }
    278 }
    279 
    280 // static
    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;
    287   int render_view_id;
    288   if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
    289           device_id, &render_process_id, &render_view_id)) {
    290     return NULL;
    291   }
    292 
    293   return new WebContentsAudioInputStream(
    294       render_process_id, render_view_id,
    295       audio_mirroring_manager,
    296       new WebContentsTracker(),
    297       new media::VirtualAudioInputStream(
    298           params, worker_loop,
    299           media::VirtualAudioInputStream::AfterCloseCallback()));
    300 }
    301 
    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)) {}
    309 
    310 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
    311 
    312 bool WebContentsAudioInputStream::Open() {
    313   return impl_->Open();
    314 }
    315 
    316 void WebContentsAudioInputStream::Start(AudioInputCallback* callback) {
    317   impl_->Start(callback);
    318 }
    319 
    320 void WebContentsAudioInputStream::Stop() {
    321   impl_->Stop();
    322 }
    323 
    324 void WebContentsAudioInputStream::Close() {
    325   impl_->Close();
    326   delete this;
    327 }
    328 
    329 double WebContentsAudioInputStream::GetMaxVolume() {
    330   return impl_->mixer_stream()->GetMaxVolume();
    331 }
    332 
    333 void WebContentsAudioInputStream::SetVolume(double volume) {
    334   impl_->mixer_stream()->SetVolume(volume);
    335 }
    336 
    337 double WebContentsAudioInputStream::GetVolume() {
    338   return impl_->mixer_stream()->GetVolume();
    339 }
    340 
    341 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) {
    342   impl_->mixer_stream()->SetAutomaticGainControl(enabled);
    343 }
    344 
    345 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
    346   return impl_->mixer_stream()->GetAutomaticGainControl();
    347 }
    348 
    349 }  // namespace content
    350