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

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: Fixed issues with losing mirroring on tab navigations. 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 RefCountedThreadSafe<Impl>;
55
56 enum State {
57 kConstructed,
tommi (sloooow) - chröme 2013/01/14 14:06:48 CONSTRUCTED, OPENED, etc.
miu 2013/01/14 21:53:03 Done. Yeah, I keep forgetting the new style on th
58 kOpened,
59 kMirroring,
60 kClosed
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_(kConstructed),
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_ == kConstructed || state_ == kClosed);
126 }
127
128 bool WebContentsAudioInputStream::Impl::Open() {
129 DCHECK(message_loop_->BelongsToCurrentThread());
130
131 if (state_ != kConstructed)
tommi (sloooow) - chröme 2013/01/14 14:06:48 should we, in order to keep the implementation sim
miu 2013/01/14 21:53:03 I totally agree with you on principle. I'm mainly
tommi (sloooow) - chröme 2013/01/15 10:58:42 Great. Thanks for adding the checks and thanks for
132 return false;
133
134 if (!mixer_stream_->Open())
135 return false;
136
137 state_ = kOpened;
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_ != kOpened)
tommi (sloooow) - chröme 2013/01/14 14:06:48 same question here
151 return;
152
153 if (IsTargetLost()) {
154 ReportError();
155 return;
156 }
157
158 state_ = kMirroring;
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_ != kMirroring)
170 return;
171
172 state_ = kOpened;
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_ == kOpened) {
187 state_ = kConstructed;
188 tracker_->Stop();
189 mixer_stream_->Close();
190 }
191
192 if (state_ == kConstructed)
193 state_ = kClosed;
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 media::VirtualAudioOutputStream* const stream =
238 new media::VirtualAudioOutputStream(
239 params, message_loop_, mixer_stream_.get());
240 // Note: The closure created here holds a reference to "this," which will
241 // guarantee the VirtualAudioInputStream (mixer_stream_) outlives the
242 // VirtualAudioOutputStream.
243 stream->RunOnceClosed(base::Bind(&Impl::ReleaseInput, this, stream));
244
245 return stream;
246 }
247
248 void WebContentsAudioInputStream::Impl::ReleaseInput(
249 media::VirtualAudioOutputStream* stream) {
250 delete stream;
251 }
252
253 void WebContentsAudioInputStream::Impl::OnTargetChanged(int render_process_id,
254 int render_view_id) {
255 DCHECK(message_loop_->BelongsToCurrentThread());
256
257 if (target_render_process_id_ == render_process_id &&
258 target_render_view_id_ == render_view_id)
259 return; // No change.
tommi (sloooow) - chröme 2013/01/14 14:06:48 {}
miu 2013/01/14 21:53:03 I thought we don't put braces around one-line then
tommi (sloooow) - chröme 2013/01/15 10:58:42 Yeah, so this one I've learned through reviews and
miu 2013/01/16 03:22:18 Done. Okay, I understand it now. I really wish t
260
261 DVLOG(1) << "Target RenderView has changed from "
262 << target_render_process_id_ << ':' << target_render_view_id_
263 << " to " << render_process_id << ':' << render_view_id;
264
265 if (state_ == kMirroring)
266 StopMirroring();
267
268 target_render_process_id_ = render_process_id;
269 target_render_view_id_ = render_view_id;
270
271 if (state_ == kMirroring) {
272 if (IsTargetLost()) {
273 ReportError();
274 Stop();
275 } else {
276 StartMirroring();
277 }
278 }
279 }
280
281 //static
tommi (sloooow) - chröme 2013/01/14 14:06:48 // static
miu 2013/01/14 21:53:03 Done.
282 WebContentsAudioInputStream* WebContentsAudioInputStream::Create(
283 const std::string& device_id,
284 const media::AudioParameters& params,
285 base::MessageLoopProxy* message_loop) {
286 int render_process_id;
287 int render_view_id;
288 if (!WebContentsCaptureUtil::ExtractTabCaptureTarget(
289 device_id, &render_process_id, &render_view_id)) {
290 return NULL;
291 } else {
tommi (sloooow) - chröme 2013/01/14 14:06:48 no need for else?
miu 2013/01/14 21:53:03 Done.
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
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