Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1221)

Unified Diff: content/browser/renderer_host/media/web_contents_audio_input_stream.cc

Issue 11413078: Tab Audio Capture: Browser-side connect/disconnect functionality. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/media/web_contents_audio_input_stream.cc
diff --git a/content/browser/renderer_host/media/web_contents_audio_input_stream.cc b/content/browser/renderer_host/media/web_contents_audio_input_stream.cc
new file mode 100644
index 0000000000000000000000000000000000000000..105f1efa37de1da564737dd9302ae43ace8e57a3
--- /dev/null
+++ b/content/browser/renderer_host/media/web_contents_audio_input_stream.cc
@@ -0,0 +1,280 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "content/browser/renderer_host/media/audio_renderer_host.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "media/base/bind_to_loop.h"
+
+namespace content {
+
+// TODO(miu): Fix lots of code duplication, especially around the monitoring of
+// WebContents and the parsing of device_ids. See
+// web_contents_video_capture_device.cc.
+
+//static
+WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
+ const std::string& device_id) {
+ // Parse device_id into render_process_id and render_view_id.
+ const size_t sep_pos = device_id.find(':');
+ if (sep_pos == std::string::npos) {
+ return NULL;
+ }
+ const base::StringPiece component1(device_id.data(), sep_pos);
+ const base::StringPiece component2(device_id.data() + sep_pos + 1,
+ device_id.length() - sep_pos - 1);
+ int render_process_id = -1;
+ int render_view_id = -1;
+ if (!base::StringToInt(component1, &render_process_id) ||
+ !base::StringToInt(component2, &render_view_id)) {
+ return NULL;
+ }
+
+ return new WebContentsAudioInputStream(render_process_id, render_view_id);
+}
+
+class WebContentsAudioInputStream::AudioRendererHostTracker
Alpha Left Google 2012/11/20 21:49:43 I suggest we split this class out to a separate fi
miu 2012/11/21 08:27:48 Absolutely agree! :-) We think alike. I had alr
+ : public base::RefCountedThreadSafe<AudioRendererHostTracker>,
+ public WebContentsObserver {
+ public:
+ typedef base::Callback<void(int render_process_id, int render_view_id)>
+ ChangeCallback;
+
+ void Start(int render_process_id, int render_view_id,
+ const ChangeCallback& callback) {
+ {
+ base::AutoLock guard(lock_);
Alpha Left Google 2012/11/20 21:49:43 There's no need to use a lock if you post tasks ba
miu 2012/11/21 08:27:48 The lock is needed for two reasons: 1. callback_.
+ callback_ = callback;
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&AudioRendererHostTracker::LookUpWebAndObserveWebContents,
+ this,
+ render_process_id, render_view_id));
+ }
+
+ void Stop() {
+ {
+ base::AutoLock guard(lock_);
+ callback_.Reset();
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&AudioRendererHostTracker::Observe, this,
+ static_cast<WebContents*>(NULL)));
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<AudioRendererHostTracker>;
+
+ virtual ~AudioRendererHostTracker() {
+ DCHECK(!web_contents()) << "BUG: Still observering!";
+ }
+
+ void LookUpWebAndObserveWebContents(int render_process_id,
+ int render_view_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ RenderViewHost* const rvh =
+ RenderViewHost::FromID(render_process_id, render_view_id);
+ DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
+ << render_process_id << ", " << render_view_id
+ << ") returned NULL.";
+ Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
+ DVLOG_IF(1, !web_contents())
+ << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
+
+ OnWebContentsChangeEvent();
+ }
+
+ void OnWebContentsChangeEvent() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ WebContents* const wc = web_contents();
+ RenderViewHost* const rvh = wc ? wc->GetRenderViewHost() : NULL;
+ RenderProcessHost* const rph = rvh ? rvh->GetProcess() : NULL;
+
+ const int render_process_id = rph ? rph->GetID() : MSG_ROUTING_NONE;
+ const int render_view_id = rvh ? rvh->GetRoutingID() : MSG_ROUTING_NONE;
+
+ base::AutoLock guard(lock_);
+ if (!callback_.is_null()) {
+ callback_.Run(render_process_id, render_view_id);
Alpha Left Google 2012/11/20 21:49:43 WeakPtr doesn't work cross-threads. It has a DCHEC
miu 2012/11/21 08:27:48 callback_ is a base::Callback<...>, not a weak poi
+ }
+ }
+
+ // How WebContents notifies us of a new RenderView.
+ virtual void RenderViewReady() OVERRIDE {
+ OnWebContentsChangeEvent();
+ }
+ // When a WebContents is destroyed.
+ virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
+ OnWebContentsChangeEvent();
+ }
+
+ base::Lock lock_;
Alpha Left Google 2012/11/20 21:49:43 No need to use lock.
+ ChangeCallback callback_;
+};
+
+
+WebContentsAudioInputStream::WebContentsAudioInputStream(int render_process_id,
+ int render_view_id)
+ : state_(kIdle),
+ target_render_process_id_(render_process_id),
+ target_render_view_id_(render_view_id) {
+ target_host_ =
+ AudioRendererHost::FromRenderProcessID(target_render_process_id_);
+}
+
+WebContentsAudioInputStream::~WebContentsAudioInputStream() {
+ DCHECK(!tracker_) << "BUG: Close() not called after Open().";
+}
+
+bool WebContentsAudioInputStream::Open() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (state_ != kIdle) {
+ return false;
+ }
+
+ DCHECK(!tracker_);
+ tracker_ = new AudioRendererHostTracker();
+ const AudioRendererHostTracker::ChangeCallback& callback =
+ media::BindToLoop(
+ base::MessageLoopProxy::current(),
+ base::Bind(&WebContentsAudioInputStream::OnTargetChanged,
+ AsWeakPtr()));
+ tracker_->Start(target_render_process_id_, target_render_view_id_, callback);
+
+ return true;
+}
+
+void WebContentsAudioInputStream::Start(AudioInputCallback* callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (state_ != kIdle) {
+ return;
+ }
+
+ state_ = kStarting;
+
+ DCHECK(streams_.empty());
+ if (target_host_) {
+ target_host_->StartMirroring(target_render_view_id_, this);
+ }
+
+ state_ = kRecording;
+}
+
+void WebContentsAudioInputStream::Stop() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (state_ != kStarting && state_ != kRecording) {
+ return;
+ }
+
+ state_ = kStopping;
+
+ if (target_host_) {
+ target_host_->StopMirroring(target_render_view_id_, this);
Alpha Left Google 2012/11/20 21:49:43 Does this need |target_render_view_id_|? It seems
miu 2012/11/21 08:27:48 Answered in one of my comment responses in audio_r
+ }
+ DCHECK(streams_.empty());
+
+ state_ = kIdle;
+}
+
+void WebContentsAudioInputStream::Close() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (state_ != kIdle) {
+ Stop();
+ }
+
+ tracker_->Stop();
+ tracker_ = NULL;
+
+ state_ = kClosed;
+}
+
+void WebContentsAudioInputStream::OnTargetChanged(int render_process_id,
+ int render_view_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (target_render_process_id_ == render_process_id &&
+ target_render_view_id_ == render_view_id) {
+ return;
+ }
+
+ if (state_ == kRecording) {
+ if (target_host_) {
+ target_host_->StopMirroring(target_render_view_id_, this);
+ }
+ DCHECK(streams_.empty());
+ state_ = kStarting;
+ }
+
+ target_render_process_id_ = render_process_id;
+ target_render_view_id_ = render_view_id;
+ target_host_ = AudioRendererHost::FromRenderProcessID(render_process_id);
+
+ if (state_ == kStarting || state_ == kRecording) {
+ DCHECK(streams_.empty());
+ if (target_host_) {
+ target_host_->StartMirroring(target_render_view_id_, this);
+ }
+ state_ = kRecording;
+ }
+}
+
+double WebContentsAudioInputStream::GetMaxVolume() {
+ return 1.0;
+}
+
+void WebContentsAudioInputStream::SetVolume(double volume) {
+ // no-op
+}
+
+double WebContentsAudioInputStream::GetVolume() {
+ return 1.0;
+}
+
+void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) {
+ // no-op
+}
+
+bool WebContentsAudioInputStream::GetAutomaticGainControl() {
+ return false;
+}
+
+void WebContentsAudioInputStream::AddAudioOutputStream(
+ media::DivertedAudioOutputStream* aos) {
+ DCHECK(!streams_.count(aos)) << "BUG: Adding same stream twice.";
+ streams_.insert(aos);
+
+ DVLOG(1) << "STUB: WebContentsAudioInputStream@" << this
+ << "->AddAudioOutputStream(" << aos << ')';
+}
+
+void WebContentsAudioInputStream::RemoveAudioOutputStream(
+ media::DivertedAudioOutputStream* aos) {
+ DCHECK(streams_.count(aos)) << "BUG: Removing unknown stream.";
+ streams_.erase(aos);
+
+ DVLOG(1) << "STUB: WebContentsAudioInputStream@" << this
+ << "->RemoveAudioOutputStream(" << aos << ')';
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698