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

Side by Side Diff: content/browser/renderer_host/media/audio_renderer_host.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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/media/audio_renderer_host.h" 5 #include "content/browser/renderer_host/media/audio_renderer_host.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/lazy_instance.h"
8 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
9 #include "base/process.h" 10 #include "base/process.h"
10 #include "base/shared_memory.h" 11 #include "base/shared_memory.h"
11 #include "content/browser/browser_main_loop.h" 12 #include "content/browser/browser_main_loop.h"
12 #include "content/browser/renderer_host/media/audio_sync_reader.h" 13 #include "content/browser/renderer_host/media/audio_sync_reader.h"
14 #include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
13 #include "content/common/media/audio_messages.h" 15 #include "content/common/media/audio_messages.h"
14 #include "content/public/browser/media_observer.h" 16 #include "content/public/browser/media_observer.h"
17 #include "media/audio/audio_io.h"
15 #include "media/audio/shared_memory_util.h" 18 #include "media/audio/shared_memory_util.h"
16 #include "media/base/audio_bus.h" 19 #include "media/base/audio_bus.h"
17 #include "media/base/limits.h" 20 #include "media/base/limits.h"
18 21
19 using media::AudioBus; 22 using media::AudioBus;
20 23
21 namespace content { 24 namespace content {
22 25
23 struct AudioRendererHost::AudioEntry { 26 struct AudioRendererHost::AudioEntry {
24 AudioEntry(); 27 AudioEntry();
25 ~AudioEntry(); 28 ~AudioEntry();
26 29
27 // The AudioOutputController that manages the audio stream. 30 // The AudioOutputController that manages the audio stream.
28 scoped_refptr<media::AudioOutputController> controller; 31 scoped_refptr<media::AudioOutputController> controller;
29 32
30 // The audio stream ID. 33 // The audio stream ID.
31 int stream_id; 34 int stream_id;
32 35
36 // The routing ID of the source render view.
37 int render_view_id;
38
33 // Shared memory for transmission of the audio data. 39 // Shared memory for transmission of the audio data.
34 base::SharedMemory shared_memory; 40 base::SharedMemory shared_memory;
35 41
36 // The synchronous reader to be used by the controller. We have the 42 // The synchronous reader to be used by the controller. We have the
37 // ownership of the reader. 43 // ownership of the reader.
38 scoped_ptr<media::AudioOutputController::SyncReader> reader; 44 scoped_ptr<media::AudioOutputController::SyncReader> reader;
39 45
46 // When non-NULL, normal audio output is being diverted for audio mirroring.
47 scoped_ptr<media::DivertedAudioOutputStream> diverted_stream;
Alpha Left Google 2012/11/20 21:49:43 Should this be owned by AudioOutputController inst
48
40 // Set to true after we called Close() for the controller. 49 // Set to true after we called Close() for the controller.
41 bool pending_close; 50 bool pending_close;
42 }; 51 };
43 52
44 AudioRendererHost::AudioEntry::AudioEntry() 53 AudioRendererHost::AudioEntry::AudioEntry()
45 : stream_id(0), 54 : stream_id(0),
55 render_view_id(MSG_ROUTING_NONE),
46 pending_close(false) { 56 pending_close(false) {
47 } 57 }
48 58
49 AudioRendererHost::AudioEntry::~AudioEntry() {} 59 AudioRendererHost::AudioEntry::~AudioEntry() {}
50 60
61 namespace {
62
63 struct GlobalLookup {
64 base::Lock lock;
Alpha Left Google 2012/11/20 21:49:43 I don't think a lock is necessary, rather ASSERT f
miu 2012/11/21 08:27:48 Unfortunately, the lock will be needed since an en
65 std::map<int, AudioRendererHost*> by_process_id;
66 };
67
68 base::LazyInstance<GlobalLookup>::Leaky g_lookup = LAZY_INSTANCE_INITIALIZER;
69
70 void RegisterAudioRendererHost(int render_process_id, AudioRendererHost* arh) {
71 GlobalLookup& lookup = g_lookup.Get();
72 base::AutoLock guard(lookup.lock);
73 lookup.by_process_id.insert(std::make_pair(render_process_id, arh));
74 }
75
76 void DeregisterAudioRendererHost(int render_process_id) {
77 GlobalLookup& lookup = g_lookup.Get();
78 base::AutoLock guard(lookup.lock);
79 lookup.by_process_id.erase(render_process_id);
80 }
81
82 } // namespace
83
84 // static
85 scoped_refptr<AudioRendererHost> AudioRendererHost::FromRenderProcessID(
86 int render_process_id) {
87 GlobalLookup& lookup = g_lookup.Get();
Alpha Left Google 2012/11/20 21:49:43 Assert for threads.
88 base::AutoLock guard(lookup.lock);
89 std::map<int, AudioRendererHost*>::const_iterator it =
90 lookup.by_process_id.find(render_process_id);
91 return (it != lookup.by_process_id.end()) ? it->second : NULL;
92 }
93
51 /////////////////////////////////////////////////////////////////////////////// 94 ///////////////////////////////////////////////////////////////////////////////
52 // AudioRendererHost implementations. 95 // AudioRendererHost implementations.
53 AudioRendererHost::AudioRendererHost( 96 AudioRendererHost::AudioRendererHost(
97 int render_process_id,
54 media::AudioManager* audio_manager, MediaObserver* media_observer) 98 media::AudioManager* audio_manager, MediaObserver* media_observer)
55 : audio_manager_(audio_manager), 99 : render_process_id_(render_process_id),
100 audio_manager_(audio_manager),
56 media_observer_(media_observer) { 101 media_observer_(media_observer) {
102 RegisterAudioRendererHost(render_process_id_, this);
57 } 103 }
58 104
59 AudioRendererHost::~AudioRendererHost() { 105 AudioRendererHost::~AudioRendererHost() {
60 DCHECK(audio_entries_.empty()); 106 DCHECK(audio_entries_.empty());
107 DCHECK(mirror_sessions_.empty());
108 DeregisterAudioRendererHost(render_process_id_);
Alpha Left Google 2012/11/20 21:49:43 How does each |destination| know when to call Stop
miu 2012/11/21 08:27:48 AudioRendererHost is ref-counted. Each |destinati
61 } 109 }
62 110
63 void AudioRendererHost::OnChannelClosing() { 111 void AudioRendererHost::OnChannelClosing() {
64 BrowserMessageFilter::OnChannelClosing(); 112 BrowserMessageFilter::OnChannelClosing();
65 113
66 // Since the IPC channel is gone, close all requested audio streams. 114 // Since the IPC channel is gone, close all requested audio streams.
67 DeleteEntries(); 115 DeleteEntries();
68 } 116 }
69 117
70 void AudioRendererHost::OnDestruct() const { 118 void AudioRendererHost::OnDestruct() const {
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) 255 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream)
208 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream) 256 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream)
209 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) 257 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream)
210 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) 258 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
211 IPC_MESSAGE_UNHANDLED(handled = false) 259 IPC_MESSAGE_UNHANDLED(handled = false)
212 IPC_END_MESSAGE_MAP_EX() 260 IPC_END_MESSAGE_MAP_EX()
213 261
214 return handled; 262 return handled;
215 } 263 }
216 264
265 void AudioRendererHost::StartMirroring(
266 int render_view_id, WebContentsAudioInputStream* destination) {
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
268 DCHECK(destination);
269
270 MirrorSessionMap::iterator session_it = mirror_sessions_.find(render_view_id);
271 if (session_it != mirror_sessions_.end()) {
272 DVLOG(1) << "Forcing StopMirroring(" << render_view_id
273 << ") on existing mirroring session (@" << session_it->second
274 << ").";
275 StopMirroring(render_view_id, session_it->second);
Alpha Left Google 2012/11/20 21:49:43 Should this just be an error?
miu 2012/11/21 08:27:48 Justin and I talked about this. He believe the ex
276 }
277
278 DVLOG(1) << "StartMirroring RenderView " << render_view_id
279 << " --> WebContentsAudioInputStream@" << destination;
280
281 mirror_sessions_.insert(std::make_pair(render_view_id, destination));
282 for (AudioEntryMap::const_iterator it = audio_entries_.begin();
283 it != audio_entries_.end(); ++it) {
284 if (it->second->render_view_id == render_view_id) {
285 it->second->diverted_stream.reset(it->second->controller->Divert());
286 destination->AddAudioOutputStream(it->second->diverted_stream.get());
287 }
288 }
289 }
290
291 void AudioRendererHost::StopMirroring(
292 int render_view_id, WebContentsAudioInputStream* destination) {
293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
294
295 MirrorSessionMap::iterator session_it = mirror_sessions_.find(render_view_id);
296 if (session_it == mirror_sessions_.end() ||
297 destination != session_it->second) {
298 return;
299 }
300
301 DVLOG(1) << "StopMirroring RenderView " << render_view_id
302 << " --> WebContentsAudioInputStream@" << destination;
303
304 for (AudioEntryMap::const_iterator it = audio_entries_.begin();
305 it != audio_entries_.end(); ++it) {
306 if (it->second->render_view_id == render_view_id) {
307 destination->RemoveAudioOutputStream(it->second->diverted_stream.get());
308 it->second->diverted_stream.reset();
309 }
310 }
311 mirror_sessions_.erase(session_it);
312 }
313
217 void AudioRendererHost::OnCreateStream( 314 void AudioRendererHost::OnCreateStream(
218 int stream_id, const media::AudioParameters& params, int input_channels) { 315 int stream_id, const media::AudioParameters& params, int input_channels) {
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
220 DCHECK(LookupById(stream_id) == NULL); 317 DCHECK(LookupById(stream_id) == NULL);
221 318
222 media::AudioParameters audio_params(params); 319 media::AudioParameters audio_params(params);
223 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); 320 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params);
224 DCHECK_GT(buffer_size, 0U); 321 DCHECK_GT(buffer_size, 0U);
225 DCHECK_LE(buffer_size, 322 DCHECK_LE(buffer_size,
226 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); 323 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes));
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 } 371 }
275 372
276 // If we have created the controller successfully, create an entry and add it 373 // If we have created the controller successfully, create an entry and add it
277 // to the map. 374 // to the map.
278 entry->stream_id = stream_id; 375 entry->stream_id = stream_id;
279 audio_entries_.insert(std::make_pair(stream_id, entry.release())); 376 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
280 if (media_observer_) 377 if (media_observer_)
281 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); 378 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created");
282 } 379 }
283 380
381 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
382 int render_view_id) {
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
384
385 AudioEntry* const entry = LookupById(stream_id);
386 if (!entry) {
387 SendErrorMessage(stream_id);
388 return;
389 }
390
391 if (entry->render_view_id == render_view_id)
392 return; // No change.
393
394 MirrorSessionMap::const_iterator prev_session_it =
Alpha Left Google 2012/11/20 21:49:43 Comments to explain what this is doing.
miu 2012/11/21 08:27:48 Done.
395 mirror_sessions_.find(entry->render_view_id);
396 scoped_ptr<media::DivertedAudioOutputStream> diverted_stream;
397 if (prev_session_it != mirror_sessions_.end()) {
398 prev_session_it->second->
399 RemoveAudioOutputStream(entry->diverted_stream.get());
400 diverted_stream.swap(entry->diverted_stream);
401 }
402
403 entry->render_view_id = render_view_id;
404
405 MirrorSessionMap::const_iterator next_session_it =
Alpha Left Google 2012/11/20 21:49:43 Would be helpful to explain what this is supposed
miu 2012/11/21 08:27:48 Done.
406 mirror_sessions_.find(render_view_id);
407 if (next_session_it != mirror_sessions_.end()) {
408 if (diverted_stream) {
409 entry->diverted_stream.swap(diverted_stream);
410 } else {
411 entry->diverted_stream.reset(entry->controller->Divert());
412 }
413 next_session_it->second->AddAudioOutputStream(entry->diverted_stream.get());
414 }
415 }
416
284 void AudioRendererHost::OnPlayStream(int stream_id) { 417 void AudioRendererHost::OnPlayStream(int stream_id) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
286 419
287 AudioEntry* entry = LookupById(stream_id); 420 AudioEntry* entry = LookupById(stream_id);
288 if (!entry) { 421 if (!entry) {
289 SendErrorMessage(stream_id); 422 SendErrorMessage(stream_id);
290 return; 423 return;
291 } 424 }
292 425
293 entry->controller->Play(); 426 entry->controller->Play();
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
363 for (AudioEntryMap::iterator i = audio_entries_.begin(); 496 for (AudioEntryMap::iterator i = audio_entries_.begin();
364 i != audio_entries_.end(); ++i) { 497 i != audio_entries_.end(); ++i) {
365 CloseAndDeleteStream(i->second); 498 CloseAndDeleteStream(i->second);
366 } 499 }
367 } 500 }
368 501
369 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) { 502 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
371 504
372 if (!entry->pending_close) { 505 if (!entry->pending_close) {
506 MirrorSessionMap::const_iterator session_it =
507 mirror_sessions_.find(entry->render_view_id);
508 if (session_it != mirror_sessions_.end()) {
509 session_it->second->RemoveAudioOutputStream(entry->diverted_stream.get());
510 entry->diverted_stream.reset();
511 }
373 entry->controller->Close( 512 entry->controller->Close(
374 base::Bind(&AudioRendererHost::DeleteEntry, this, entry)); 513 base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
375 entry->pending_close = true; 514 entry->pending_close = true;
376 } 515 }
377 } 516 }
378 517
379 void AudioRendererHost::DeleteEntry(AudioEntry* entry) { 518 void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
381 520
382 // Delete the entry when this method goes out of scope. 521 // Delete the entry when this method goes out of scope.
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
425 return NULL; 564 return NULL;
426 } 565 }
427 566
428 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( 567 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting(
429 int stream_id) { 568 int stream_id) {
430 AudioEntry* const entry = LookupById(stream_id); 569 AudioEntry* const entry = LookupById(stream_id);
431 return entry ? entry->controller : NULL; 570 return entry ? entry->controller : NULL;
432 } 571 }
433 572
434 } // namespace content 573 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698