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

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: Put WebContentsTracker in its own files. Clean-up 'do-nothing' callbacks. 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/browser/renderer_host/media/web_contents_tracker.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "media/audio/virtual_audio_input_stream.h"
20 #include "media/audio/virtual_audio_output_stream.h"
21
22 namespace content {
23
24 class WebContentsAudioInputStream::Impl
25 : public base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>,
26 public AudioMirroringManager::MirroringDestination {
27 public:
28 // Takes ownership of |mixer_stream|. The rest outlive this instance.
29 Impl(int render_process_id, int render_view_id,
30 base::MessageLoopProxy* message_loop,
31 AudioMirroringManager* mirroring_manager,
32 const scoped_refptr<WebContentsTracker>& tracker,
33 media::VirtualAudioInputStream* mixer_stream);
34
35 // Open underlying VirtualAudioInputStream and start tracker.
36 bool Open();
37
38 // Start the underlying VirtualAudioInputStream and instruct
39 // AudioMirroringManager to begin a mirroring session.
40 void Start(AudioInputCallback* callback);
41
42 // Stop the underlying VirtualAudioInputStream and instruct
43 // AudioMirroringManager to shutdown a mirroring session.
44 void Stop();
45
46 // Close the underlying VirtualAudioInputStream and stop the tracker.
47 void Close();
48
49 // Accessor to underlying VirtualAudioInputStream.
50 media::VirtualAudioInputStream* mixer_stream() const {
51 return mixer_stream_.get();
52 }
53
54 private:
55 friend class base::RefCountedThreadSafe<WebContentsAudioInputStream::Impl>;
56
57 enum State {
58 CONSTRUCTED,
59 OPENED,
60 MIRRORING,
61 CLOSED
62 };
63
64 virtual ~Impl();
65
66 // Returns true if the mirroring target has been permanently lost.
67 bool IsTargetLost() const;
68
69 // Notifies the consumer callback that the stream is now dead.
70 void ReportError();
71
72 // Start/Stop mirroring by posting a call to AudioMirroringManager on the IO
73 // BrowserThread.
74 void StartMirroring();
75 void StopMirroring();
76
77 // AudioMirroringManager::MirroringDestination implementation
78 virtual media::AudioOutputStream* AddInput(
79 const media::AudioParameters& params) OVERRIDE;
80
81 // Callback which is run when |stream| is closed. Deletes |stream|.
82 void ReleaseInput(media::VirtualAudioOutputStream* stream);
83
84 // Called by WebContentsTracker when the target of the audio mirroring has
85 // changed.
86 void OnTargetChanged(int render_process_id, int render_view_id);
87
88 // Injected dependencies.
89 base::MessageLoopProxy* const message_loop_;
90 AudioMirroringManager* const mirroring_manager_;
91 const scoped_refptr<WebContentsTracker> tracker_;
92 // The AudioInputStream implementation that handles the audio conversion and
93 // mixing details.
94 const scoped_ptr<media::VirtualAudioInputStream> mixer_stream_;
95
96 State state_;
97
98 // Current audio mirroring target.
99 int target_render_process_id_;
100 int target_render_view_id_;
101
102 // Current callback used to consume the resulting mixed audio data.
103 AudioInputCallback* callback_;
104
105 DISALLOW_COPY_AND_ASSIGN(Impl);
106 };
107
108 WebContentsAudioInputStream::Impl::Impl(
109 int render_process_id, int render_view_id,
110 base::MessageLoopProxy* message_loop,
111 AudioMirroringManager* mirroring_manager,
112 const scoped_refptr<WebContentsTracker>& tracker,
113 media::VirtualAudioInputStream* mixer_stream)
114 : message_loop_(message_loop), mirroring_manager_(mirroring_manager),
115 tracker_(tracker), mixer_stream_(mixer_stream), state_(CONSTRUCTED),
116 target_render_process_id_(render_process_id),
117 target_render_view_id_(render_view_id),
118 callback_(NULL) {
119 DCHECK(message_loop_);
120 DCHECK(mirroring_manager_);
121 DCHECK(tracker_);
122 DCHECK(mixer_stream_.get());
123 }
124
125 WebContentsAudioInputStream::Impl::~Impl() {
126 DCHECK(state_ == CONSTRUCTED || state_ == CLOSED);
127 }
128
129 bool WebContentsAudioInputStream::Impl::Open() {
130 DCHECK(message_loop_->BelongsToCurrentThread());
131
132 DCHECK_EQ(CONSTRUCTED, state_) << "Illegal to Open more than once.";
133
134 if (!mixer_stream_->Open())
135 return false;
136
137 state_ = OPENED;
138
139 tracker_->Start(
140 target_render_process_id_, target_render_view_id_,
141 base::Bind(&Impl::OnTargetChanged, this));
142
143 return true;
144 }
145
146 void WebContentsAudioInputStream::Impl::Start(AudioInputCallback* callback) {
147 DCHECK(message_loop_->BelongsToCurrentThread());
148 DCHECK(callback);
149
150 if (state_ != OPENED)
151 return;
152
153 if (IsTargetLost()) {
154 ReportError();
155 return;
156 }
157
158 state_ = MIRRORING;
159
160 callback_ = callback;
161 mixer_stream_->Start(callback);
162
163 StartMirroring();
164 }
165
166 void WebContentsAudioInputStream::Impl::Stop() {
167 DCHECK(message_loop_->BelongsToCurrentThread());
168
169 if (state_ != MIRRORING)
170 return;
171
172 state_ = OPENED;
173
174 mixer_stream_->Stop();
175 callback_ = NULL;
176
177 if (!IsTargetLost())
178 StopMirroring();
179 }
180
181 void WebContentsAudioInputStream::Impl::Close() {
182 DCHECK(message_loop_->BelongsToCurrentThread());
183
184 Stop();
185
186 if (state_ == OPENED) {
187 state_ = CONSTRUCTED;
188 tracker_->Stop();
189 mixer_stream_->Close();
190 }
191
192 DCHECK_EQ(CONSTRUCTED, state_);
193 state_ = CLOSED;
194 }
195
196 bool WebContentsAudioInputStream::Impl::IsTargetLost() const {
197 DCHECK(message_loop_->BelongsToCurrentThread());
198
199 return target_render_process_id_ <= 0 || target_render_view_id_ <= 0;
200 }
201
202 void WebContentsAudioInputStream::Impl::ReportError() {
203 DCHECK(message_loop_->BelongsToCurrentThread());
204
205 // TODO(miu): Need clean-up of AudioInputCallback interface in a future
206 // change, since its only implementation ignores the first argument entirely
207 // and the values for the second argument are undefined.
208 callback_->OnError(NULL, 0);
209 }
210
211 void WebContentsAudioInputStream::Impl::StartMirroring() {
212 DCHECK(message_loop_->BelongsToCurrentThread());
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(message_loop_->BelongsToCurrentThread());
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, message_loop_, mixer_stream_.get(),
242 base::Bind(&Impl::ReleaseInput, this));
243 }
244
245 void WebContentsAudioInputStream::Impl::ReleaseInput(
246 media::VirtualAudioOutputStream* stream) {
247 delete stream;
248 }
249
250 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
251 int render_view_id) {
252 DCHECK(message_loop_->BelongsToCurrentThread());
253
254 if (target_render_process_id_ == render_process_id &&
255 target_render_view_id_ == render_view_id) {
256 return;
257 }
258
259 DVLOG(1) << "Target RenderView has changed from "
260 << target_render_process_id_ << ':' << target_render_view_id_
261 << " to " << render_process_id << ':' << render_view_id;
262
263 if (state_ == MIRRORING)
264 StopMirroring();
265
266 target_render_process_id_ = render_process_id;
267 target_render_view_id_ = render_view_id;
268
269 if (state_ == MIRRORING) {
270 if (IsTargetLost()) {
271 ReportError();
272 Stop();
273 } else {
274 StartMirroring();
275 }
276 }
277 }
278
279 // static
280 WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
281 const std::string& device_id,
282 const media::AudioParameters& params,
283 base::MessageLoopProxy* message_loop) {
284 int render_process_id;
285 int render_view_id;
286 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
287 device_id, &render_process_id, &render_view_id)) {
288 return NULL;
289 }
290
291 return new WebContentsAudioInputStream(
292 render_process_id, render_view_id, message_loop,
293 BrowserMainLoop::GetAudioMirroringManager(),
294 new WebContentsTracker(),
295 new media::VirtualAudioInputStream(
296 params, message_loop,
297 media::VirtualAudioInputStream::AfterCloseCallback()));
DaleCurtis 2013/01/17 21:11:12 This is a bit weird, you're binding in a callback
miu 2013/01/17 22:44:12 No, that's not what's going on here at all. After
298 }
299
300 WebContentsAudioInputStream::WebContentsAudioInputStream(
301 int render_process_id, int render_view_id,
302 base::MessageLoopProxy* message_loop,
303 AudioMirroringManager* mirroring_manager,
304 const scoped_refptr<WebContentsTracker>& tracker,
305 media::VirtualAudioInputStream* mixer_stream)
306 : impl_(new Impl(render_process_id, render_view_id, message_loop,
307 mirroring_manager, tracker, mixer_stream)) {}
308
309 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
310
311 bool WebContentsAudioInputStream::Open() {
312 return impl_->Open();
313 }
314
315 void WebContentsAudioInputStream::Start(AudioInputCallback* callback) {
316 impl_->Start(callback);
317 }
318
319 void WebContentsAudioInputStream::Stop() {
320 impl_->Stop();
321 }
322
323 void WebContentsAudioInputStream::Close() {
324 impl_->Close();
325 delete this;
326 }
327
328 double WebContentsAudioInputStream::GetMaxVolume() {
329 return impl_->mixer_stream()->GetMaxVolume();
330 }
331
332 void WebContentsAudioInputStream::SetVolume(double volume) {
333 impl_->mixer_stream()->SetVolume(volume);
334 }
335
336 double WebContentsAudioInputStream::GetVolume() {
337 return impl_->mixer_stream()->GetVolume();
338 }
339
340 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) {
341 impl_->mixer_stream()->SetAutomaticGainControl(enabled);
342 }
343
344 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
345 return impl_->mixer_stream()->GetAutomaticGainControl();
346 }
347
348 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698