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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/callback_forward.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_piece.h"
13 #include "base/synchronization/lock.h"
14 #include "content/browser/renderer_host/media/audio_renderer_host.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_contents_observer.h"
20 #include "media/base/bind_to_loop.h"
21
22 namespace content {
23
24 // TODO(miu): Fix lots of code duplication, especially around the monitoring of
25 // WebContents and the parsing of device_ids. See
26 // web_contents_video_capture_device.cc.
27
28 //static
29 WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
30 const std::string& device_id) {
31 // Parse device_id into render_process_id and render_view_id.
32 const size_t sep_pos = device_id.find(':');
33 if (sep_pos == std::string::npos) {
34 return NULL;
35 }
36 const base::StringPiece component1(device_id.data(), sep_pos);
37 const base::StringPiece component2(device_id.data() + sep_pos + 1,
38 device_id.length() - sep_pos - 1);
39 int render_process_id = -1;
40 int render_view_id = -1;
41 if (!base::StringToInt(component1, &render_process_id) ||
42 !base::StringToInt(component2, &render_view_id)) {
43 return NULL;
44 }
45
46 return new WebContentsAudioInputStream(render_process_id, render_view_id);
47 }
48
49 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
50 : public base::RefCountedThreadSafe<AudioRendererHostTracker>,
51 public WebContentsObserver {
52 public:
53 typedef base::Callback<void(int render_process_id, int render_view_id)>
54 ChangeCallback;
55
56 void Start(int render_process_id, int render_view_id,
57 const ChangeCallback& callback) {
58 {
59 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_.
60 callback_ = callback;
61 }
62 BrowserThread::PostTask(
63 BrowserThread::UI, FROM_HERE,
64 base::Bind(&AudioRendererHostTracker::LookUpWebAndObserveWebContents,
65 this,
66 render_process_id, render_view_id));
67 }
68
69 void Stop() {
70 {
71 base::AutoLock guard(lock_);
72 callback_.Reset();
73 }
74 BrowserThread::PostTask(
75 BrowserThread::UI, FROM_HERE,
76 base::Bind(&AudioRendererHostTracker::Observe, this,
77 static_cast<WebContents*>(NULL)));
78 }
79
80 private:
81 friend class base::RefCountedThreadSafe<AudioRendererHostTracker>;
82
83 virtual ~AudioRendererHostTracker() {
84 DCHECK(!web_contents()) << "BUG: Still observering!";
85 }
86
87 void LookUpWebAndObserveWebContents(int render_process_id,
88 int render_view_id) {
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90
91 RenderViewHost* const rvh =
92 RenderViewHost::FromID(render_process_id, render_view_id);
93 DVLOG_IF(1, !rvh) << "RenderViewHost::FromID("
94 << render_process_id << ", " << render_view_id
95 << ") returned NULL.";
96 Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL);
97 DVLOG_IF(1, !web_contents())
98 << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL.";
99
100 OnWebContentsChangeEvent();
101 }
102
103 void OnWebContentsChangeEvent() {
104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105
106 WebContents* const wc = web_contents();
107 RenderViewHost* const rvh = wc ? wc->GetRenderViewHost() : NULL;
108 RenderProcessHost* const rph = rvh ? rvh->GetProcess() : NULL;
109
110 const int render_process_id = rph ? rph->GetID() : MSG_ROUTING_NONE;
111 const int render_view_id = rvh ? rvh->GetRoutingID() : MSG_ROUTING_NONE;
112
113 base::AutoLock guard(lock_);
114 if (!callback_.is_null()) {
115 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
116 }
117 }
118
119 // How WebContents notifies us of a new RenderView.
120 virtual void RenderViewReady() OVERRIDE {
121 OnWebContentsChangeEvent();
122 }
123 // When a WebContents is destroyed.
124 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
125 OnWebContentsChangeEvent();
126 }
127
128 base::Lock lock_;
Alpha Left Google 2012/11/20 21:49:43 No need to use lock.
129 ChangeCallback callback_;
130 };
131
132
133 WebContentsAudioInputStream::WebContentsAudioInputStream(int render_process_id,
134 int render_view_id)
135 : state_(kIdle),
136 target_render_process_id_(render_process_id),
137 target_render_view_id_(render_view_id) {
138 target_host_ =
139 AudioRendererHost::FromRenderProcessID(target_render_process_id_);
140 }
141
142 WebContentsAudioInputStream::~WebContentsAudioInputStream() {
143 DCHECK(!tracker_) << "BUG: Close() not called after Open().";
144 }
145
146 bool WebContentsAudioInputStream::Open() {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
148
149 if (state_ != kIdle) {
150 return false;
151 }
152
153 DCHECK(!tracker_);
154 tracker_ = new AudioRendererHostTracker();
155 const AudioRendererHostTracker::ChangeCallback& callback =
156 media::BindToLoop(
157 base::MessageLoopProxy::current(),
158 base::Bind(&WebContentsAudioInputStream::OnTargetChanged,
159 AsWeakPtr()));
160 tracker_->Start(target_render_process_id_, target_render_view_id_, callback);
161
162 return true;
163 }
164
165 void WebContentsAudioInputStream::Start(AudioInputCallback* callback) {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
167
168 if (state_ != kIdle) {
169 return;
170 }
171
172 state_ = kStarting;
173
174 DCHECK(streams_.empty());
175 if (target_host_) {
176 target_host_->StartMirroring(target_render_view_id_, this);
177 }
178
179 state_ = kRecording;
180 }
181
182 void WebContentsAudioInputStream::Stop() {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
184
185 if (state_ != kStarting && state_ != kRecording) {
186 return;
187 }
188
189 state_ = kStopping;
190
191 if (target_host_) {
192 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
193 }
194 DCHECK(streams_.empty());
195
196 state_ = kIdle;
197 }
198
199 void WebContentsAudioInputStream::Close() {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201
202 if (state_ != kIdle) {
203 Stop();
204 }
205
206 tracker_->Stop();
207 tracker_ = NULL;
208
209 state_ = kClosed;
210 }
211
212 void WebContentsAudioInputStream::OnTargetChanged(int render_process_id,
213 int render_view_id) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
215
216 if (target_render_process_id_ == render_process_id &&
217 target_render_view_id_ == render_view_id) {
218 return;
219 }
220
221 if (state_ == kRecording) {
222 if (target_host_) {
223 target_host_->StopMirroring(target_render_view_id_, this);
224 }
225 DCHECK(streams_.empty());
226 state_ = kStarting;
227 }
228
229 target_render_process_id_ = render_process_id;
230 target_render_view_id_ = render_view_id;
231 target_host_ = AudioRendererHost::FromRenderProcessID(render_process_id);
232
233 if (state_ == kStarting || state_ == kRecording) {
234 DCHECK(streams_.empty());
235 if (target_host_) {
236 target_host_->StartMirroring(target_render_view_id_, this);
237 }
238 state_ = kRecording;
239 }
240 }
241
242 double WebContentsAudioInputStream::GetMaxVolume() {
243 return 1.0;
244 }
245
246 void WebContentsAudioInputStream::SetVolume(double volume) {
247 // no-op
248 }
249
250 double WebContentsAudioInputStream::GetVolume() {
251 return 1.0;
252 }
253
254 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) {
255 // no-op
256 }
257
258 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
259 return false;
260 }
261
262 void WebContentsAudioInputStream::AddAudioOutputStream(
263 media::DivertedAudioOutputStream* aos) {
264 DCHECK(!streams_.count(aos)) << "BUG: Adding same stream twice.";
265 streams_.insert(aos);
266
267 DVLOG(1) << "STUB: WebContentsAudioInputStream@" << this
268 << "->AddAudioOutputStream(" << aos << ')';
269 }
270
271 void WebContentsAudioInputStream::RemoveAudioOutputStream(
272 media::DivertedAudioOutputStream* aos) {
273 DCHECK(streams_.count(aos)) << "BUG: Removing unknown stream.";
274 streams_.erase(aos);
275
276 DVLOG(1) << "STUB: WebContentsAudioInputStream@" << this
277 << "->RemoveAudioOutputStream(" << aos << ')';
278 }
279
280 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698