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 24namespace content { 25 26class 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 120WebContentsAudioInputStream::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 142WebContentsAudioInputStream::Impl::~Impl() { 143 DCHECK(state_ == CONSTRUCTED || state_ == CLOSED); 144} 145 146bool 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 163void 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 191void 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 205void 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 220void 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 228void 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 239void 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 250void 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 258void 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 270void 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 293media::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 304void WebContentsAudioInputStream::Impl::ReleaseInput( 305 media::VirtualAudioOutputStream* stream) { 306 delete stream; 307} 308 309void 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 326WebContentsAudioInputStream* 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 347WebContentsAudioInputStream::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 355WebContentsAudioInputStream::~WebContentsAudioInputStream() {} 356 357bool WebContentsAudioInputStream::Open() { 358 return impl_->Open(); 359} 360 361void WebContentsAudioInputStream::Start(AudioInputCallback* callback) { 362 impl_->Start(callback); 363} 364 365void WebContentsAudioInputStream::Stop() { 366 impl_->Stop(); 367} 368 369void WebContentsAudioInputStream::Close() { 370 impl_->Close(); 371 delete this; 372} 373 374double WebContentsAudioInputStream::GetMaxVolume() { 375 return impl_->mixer_stream()->GetMaxVolume(); 376} 377 378void WebContentsAudioInputStream::SetVolume(double volume) { 379 impl_->mixer_stream()->SetVolume(volume); 380} 381 382double WebContentsAudioInputStream::GetVolume() { 383 return impl_->mixer_stream()->GetVolume(); 384} 385 386void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) { 387 impl_->mixer_stream()->SetAutomaticGainControl(enabled); 388} 389 390bool WebContentsAudioInputStream::GetAutomaticGainControl() { 391 return impl_->mixer_stream()->GetAutomaticGainControl(); 392} 393 394bool WebContentsAudioInputStream::IsMuted() { 395 return false; 396} 397 398} // namespace content 399