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

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: Destructors hate ASSERT() in unit tests. 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)
tommi (sloooow) - chröme 2013/01/15 10:58:42 DCHECK_EQ(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 media::VirtualAudioOutputStream* const stream =
237 new media::VirtualAudioOutputStream(
238 params, message_loop_, mixer_stream_.get());
239 // Note: The closure created here holds a reference to "this," which will
240 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
241 // VirtualAudioOutputStream.
242 stream->RunOnceClosed(base::Bind(&Impl::ReleaseInput, this, stream));
243
244 return stream;
245 }
246
247 void WebContentsAudioInputStream::Impl::ReleaseInput(
248 media::VirtualAudioOutputStream* stream) {
249 delete stream;
250 }
251
252 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
253 int render_view_id) {
254 DCHECK(message_loop_->BelongsToCurrentThread());
255
256 if (target_render_process_id_ == render_process_id &&
257 target_render_view_id_ == render_view_id)
258 return;
259
260 DVLOG(1) << "Target RenderView has changed from "
261 << target_render_process_id_ << ':' << target_render_view_id_
262 << " to " << render_process_id << ':' << render_view_id;
263
264 if (state_ == MIRRORING)
265 StopMirroring();
266
267 target_render_process_id_ = render_process_id;
268 target_render_view_id_ = render_view_id;
269
270 if (state_ == MIRRORING) {
271 if (IsTargetLost()) {
272 ReportError();
273 Stop();
274 } else {
275 StartMirroring();
276 }
277 }
278 }
279
280 // static
281 WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
282 const std::string& device_id,
283 const media::AudioParameters& params,
284 base::MessageLoopProxy* message_loop) {
285 int render_process_id;
286 int render_view_id;
287 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
288 device_id, &render_process_id, &render_view_id)) {
289 return NULL;
290 }
291
292 return new WebContentsAudioInputStream(
293 render_process_id, render_view_id, message_loop,
294 BrowserMainLoop::GetAudioMirroringManager(),
295 new WebContentsTracker(),
296 new media::VirtualAudioInputStream(params, message_loop));
297 }
298
299 WebContentsAudioInputStream::WebContentsAudioInputStream(
300 int render_process_id, int render_view_id,
301 base::MessageLoopProxy* message_loop,
302 AudioMirroringManager* mirroring_manager,
303 const scoped_refptr<WebContentsTracker>& tracker,
304 media::VirtualAudioInputStream* mixer_stream)
305 : impl_(new Impl(render_process_id, render_view_id, message_loop,
306 mirroring_manager, tracker, mixer_stream)) {}
307
308 WebContentsAudioInputStream::~WebContentsAudioInputStream() {}
309
310 bool WebContentsAudioInputStream::Open() {
311 return impl_->Open();
312 }
313
314 void WebContentsAudioInputStream::Start(AudioInputCallback* callback) {
315 impl_->Start(callback);
316 }
317
318 void WebContentsAudioInputStream::Stop() {
319 impl_->Stop();
320 }
321
322 void WebContentsAudioInputStream::Close() {
323 impl_->Close();
324 delete this;
325 }
326
327 double WebContentsAudioInputStream::GetMaxVolume() {
328 return impl_->mixer_stream()->GetMaxVolume();
329 }
330
331 void WebContentsAudioInputStream::SetVolume(double volume) {
332 impl_->mixer_stream()->SetVolume(volume);
333 }
334
335 double WebContentsAudioInputStream::GetVolume() {
336 return impl_->mixer_stream()->GetVolume();
337 }
338
339 void WebContentsAudioInputStream::SetAutomaticGainControl(bool enabled) {
340 impl_->mixer_stream()->SetAutomaticGainControl(enabled);
341 }
342
343 bool WebContentsAudioInputStream::GetAutomaticGainControl() {
344 return impl_->mixer_stream()->GetAutomaticGainControl();
345 }
346
347 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698