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 "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::SingleThreadTaskRunner>& worker_task_runner, 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_task_runner, 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