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

Side by Side Diff: content/browser/renderer_host/media/web_contents_audio_input_stream.cc

Issue 11416350: Tab Audio Mirroring: WebContentsAudioInputStream is a new implementation which represents the lifet… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Replace RunAfterClosed() scheme with 'after close callback' passed to ctor. Created 7 years, 11 months 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) 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/message_loop_proxy.h"
14 #include "content/browser/browser_main_loop.h"
15 #include "content/browser/renderer_host/media/audio_mirroring_manager.h"
16 #include "content/browser/renderer_host/media/web_contents_capture_util.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 base::MessageLoopProxy* message_loop,
30 AudioMirroringManager* mirroring_manager,
31 const scoped_refptr<WebContentsTracker>& tracker,
32 media::VirtualAudioInputStream* mixer_stream);
33
34 // Open underlying VirtualAudioInputStream and start tracker.
35 bool Open();
36
37 // Start the underlying VirtualAudioInputStream and instruct
38 // AudioMirroringManager to begin a mirroring session.
39 void Start(AudioInputCallback* callback);
40
41 // Stop the underlying VirtualAudioInputStream and instruct
42 // AudioMirroringManager to shutdown a mirroring session.
43 void Stop();
44
45 // Close the underlying VirtualAudioInputStream and stop the tracker.
46 void Close();
47
48 // Accessor to underlying VirtualAudioInputStream.
49 media::VirtualAudioInputStream* mixer_stream() const {
50 return mixer_stream_.get();
51 }
52
53 private:
54 friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>;
55
56 enum State {
57 CONSTRUCTED,
58 OPENED,
59 MIRRORING,
60 CLOSED
61 };
62
63 virtual ~Impl();
64
65 // Returns true if the mirroring target has been permanently lost.
66 bool IsTargetLost() const;
67
68 // Notifies the consumer callback that the stream is now dead.
69 void ReportError();
70
71 // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO
72 // BrowserThread.
73 void StartMirroring();
74 void StopMirroring();
75
76 // AudioMirroringManager::MirroringDestination implementation
77 virtual media::AudioOutputStream* AddInput(
78 const media::AudioParameters& params) OVERRIDE;
79
80 // Callback which is run when |stream| is closed. Deletes |stream|.
81 void ReleaseInput(media::VirtualAudioOutputStream* stream);
82
83 // Called by WebContentsTracker when the target of the audio mirroring has
84 // changed.
85 void OnTargetChanged(int render_process_id, int render_view_id);
86
87 // Injected dependencies.
88 base::MessageLoopProxy* const message_loop_;
89 AudioMirroringManager* const mirroring_manager_;
90 const scoped_refptr<WebContentsTracker> tracker_;
91 // The AudioInputStream implementation that handles the audio conversion and
92 // mixing details.
93 const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_;
94
95 State state_;
96
97 // Current audio mirroring target.
98 int target_render_process_id_;
99 int target_render_view_id_;
100
101 // Current callback used to consume the resulting mixed audio data.
102 AudioInputCallback* callback_;
103
104 DISALLOW_COPY_AND_ASSIGN(Impl);
105 };
106
107 WebContentsAudioInputStream::Impl::Impl(
108 int render_process_id, int render_view_id,
109 base::MessageLoopProxy* message_loop,
110 AudioMirroringManager* mirroring_manager,
111 const scoped_refptr<WebContentsTracker>& tracker,
112 media::VirtualAudioInputStream* mixer_stream)
113 : message_loop_(message_loop), mirroring_manager_(mirroring_manager),
114 tracker_(tracker), mixer_stream_(mixer_stream), state_(CONSTRUCTED),
115 target_render_process_id_(render_process_id),
116 target_render_view_id_(render_view_id),
117 callback_(NULL) {
118 DCHECK(message_loop_);
119 DCHECK(mirroring_manager_);
120 DCHECK(tracker_);
121 DCHECK(mixer_stream_.get());
122 }
123
124 WebContentsAudioInputStream::Impl::~Impl() {
125 DCHECK(state_ == CONSTRUCTED || state_ == CLOSED);
126 }
127
128 bool WebContentsAudioInputStream::Impl::Open() {
129 DCHECK(message_loop_->BelongsToCurrentThread());
130
131 DCHECK_EQ(CONSTRUCTED, state_) << "Illegal to Open more than once.";
132
133 if (!mixer_stream_->Open())
134 return false;
135
136 state_ = OPENED;
137
138 tracker_->Start(
139 target_render_process_id_, target_render_view_id_,
140 base::Bind(&Impl::OnTargetChanged, this));
141
142 return true;
143 }
144
145 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) {
146 DCHECK(message_loop_->BelongsToCurrentThread());
147 DCHECK(callback);
148
149 if (state_ != OPENED)
150 return;
151
152 if (IsTargetLost()) {
153 ReportError();
154 return;
155 }
156
157 state_ = MIRRORING;
158
159 callback_ = callback;
160 mixer_stream_->Start(callback);
161
162 StartMirroring();
163 }
164
165 void WebContentsAudioInputStream::Impl::Stop() {
166 DCHECK(message_loop_->BelongsToCurrentThread());
167
168 if (state_ != MIRRORING)
169 return;
170
171 state_ = OPENED;
172
173 mixer_stream_->Stop();
174 callback_ = NULL;
175
176 if (!IsTargetLost())
177 StopMirroring();
178 }
179
180 void WebContentsAudioInputStream::Impl::Close() {
181 DCHECK(message_loop_->BelongsToCurrentThread());
182
183 Stop();
184
185 if (state_ == OPENED) {
186 state_ = CONSTRUCTED;
187 tracker_->Stop();
188 mixer_stream_->Close();
189 }
190
191 DCHECK_EQ(CONSTRUCTED, state_);
192 state_ = CLOSED;
193 }
194
195 bool WebContentsAudioInputStream::Impl::IsTargetLost() const {
196 DCHECK(message_loop_->BelongsToCurrentThread());
197
198 return target_render_process_id_ <= 0 || target_render_view_id_ <= 0;
199 }
200
201 void WebContentsAudioInputStream::Impl::ReportError() {
202 DCHECK(message_loop_->BelongsToCurrentThread());
203
204 // TODO(miu): Need clean-up of AudioInputCallback interface in a future
205 // change, since its only implementation ignores the first argument entirely
206 // and the values for the second argument are undefined.
207 callback_->OnError(NULL, 0);
208 }
209
210 void WebContentsAudioInputStream::Impl::StartMirroring() {
211 DCHECK(message_loop_->BelongsToCurrentThread());
212
213 BrowserThread::PostTask(
214 BrowserThread::IO,
215 FROM_HERE,
216 base::Bind(&AudioMirroringManager::StartMirroring,
217 base::Unretained(mirroring_manager_),
218 target_render_process_id_, target_render_view_id_,
219 make_scoped_refptr(this)));
220 }
221
222 void WebContentsAudioInputStream::Impl::StopMirroring() {
223 DCHECK(message_loop_->BelongsToCurrentThread());
224
225 BrowserThread::PostTask(
226 BrowserThread::IO,
227 FROM_HERE,
228 base::Bind(&AudioMirroringManager::StopMirroring,
229 base::Unretained(mirroring_manager_),
230 target_render_process_id_, target_render_view_id_,
231 make_scoped_refptr(this)));
232 }
233
234 media::AudioOutputStream* WebContentsAudioInputStream::Impl::AddInput(
235 const media::AudioParameters& params) {
236 // Note: The closure created here holds a reference to "this," which will
237 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
238 // VirtualAudioOutputStream.
239 return new media::VirtualAudioOutputStream(
240 params, message_loop_, mixer_stream_.get(),
241 base::Bind(&Impl::ReleaseInput, this));
242 }
243
244 void WebContentsAudioInputStream::Impl::ReleaseInput(
245 media::VirtualAudioOutputStream* stream) {
246 delete stream;
247 }
248
249 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
250 int render_view_id) {
251 DCHECK(message_loop_->BelongsToCurrentThread());
252
253 if (target_render_process_id_ == render_process_id &&
254 target_render_view_id_ == render_view_id) {
255 return;
256 }
257
258 DVLOG(1) << "Target RenderView has changed from "
259 << target_render_process_id_ << ':' << target_render_view_id_
260 << " to " << render_process_id << ':' << render_view_id;
261
262 if (state_ == MIRRORING)
263 StopMirroring();
264
265 target_render_process_id_ = render_process_id;
266 target_render_view_id_ = render_view_id;
267
268 if (state_ == MIRRORING) {
269 if (IsTargetLost()) {
270 ReportError();
271 Stop();
272 } else {
273 StartMirroring();
274 }
275 }
276 }
277
278 // static
279 WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
280 const std::string& device_id,
281 const media::AudioParameters& params,
282 base::MessageLoopProxy* message_loop) {
283 int render_process_id;
284 int render_view_id;
285 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
286 device_id, &render_process_id, &render_view_id)) {
287 return NULL;
288 }
289
290 struct Helper {
DaleCurtis 2013/01/17 01:15:52 bind_helpers exposes a DoNothing you can use inste
miu 2013/01/17 05:33:55 Fixed. bind_helpers doesn't provide a DoNothing()
291 static void DoNothingAfterClose(media::VirtualAudioInputStream* stream) {}
292 };
293 return new WebContentsAudioInputStream(
294 render_process_id, render_view_id, message_loop,
295 BrowserMainLoop::GetAudioMirroringManager(),
296 new WebContentsTracker(),
297 new media::VirtualAudioInputStream(
298 params, message_loop, base::Bind(&Helper::DoNothingAfterClose)));
299 }
300
301 WebContentsAudioInputStream::WebContentsAudioInputStream(
302 int render_process_id, int render_view_id,
303 base::MessageLoopProxy* message_loop,
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, message_loop,
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698