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 21namespace content { 22 23class 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 107WebContentsAudioInputStream::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 126WebContentsAudioInputStream::Impl::~Impl() { 127 DCHECK(state_ == CONSTRUCTED || state_ == CLOSED); 128} 129 130bool 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 147void 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 167void 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 182void 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 197bool WebContentsAudioInputStream::Impl::IsTargetLost() const { 198 DCHECK(thread_checker_.CalledOnValidThread()); 199 200 return target_render_process_id_ <= 0 || target_render_view_id_ <= 0; 201} 202 203void 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 211void 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 223void 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 235media::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 246void WebContentsAudioInputStream::Impl::ReleaseInput( 247 media::VirtualAudioOutputStream* stream) { 248 delete stream; 249} 250 251void 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 281WebContentsAudioInputStream* 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 302WebContentsAudioInputStream::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 310WebContentsAudioInputStream::~WebContentsAudioInputStream() {} 311 312bool WebContentsAudioInputStream::Open() { 313 return impl_->Open(); 314} 315 316void WebContentsAudioInputStream::Start(AudioInputCallback* callback) { 317 impl_->Start(callback); 318} 319 320void WebContentsAudioInputStream::Stop() { 321 impl_->Stop(); 322} 323 324void WebContentsAudioInputStream::Close() { 325 impl_->Close(); 326 delete this; 327} 328 329double WebContentsAudioInputStream::GetMaxVolume() { 330 return impl_->mixer_stream()->GetMaxVolume(); 331} 332 333void WebContentsAudioInputStream::SetVolume(double volume) { 334 impl_->mixer_stream()->SetVolume(volume); 335} 336 337double WebContentsAudioInputStream::GetVolume() { 338 return impl_->mixer_stream()->GetVolume(); 339} 340 341void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) { 342 impl_->mixer_stream()->SetAutomaticGainControl(enabled); 343} 344 345bool WebContentsAudioInputStream::GetAutomaticGainControl() { 346 return impl_->mixer_stream()->GetAutomaticGainControl(); 347} 348 349} // namespace content 350