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

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: Moved Glue to cc file, and renamed to SourceSharingCallback. Created 8 years 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/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "base/process.h" 9 #include "base/process.h"
10 #include "base/shared_memory.h" 10 #include "base/shared_memory.h"
11 #include "content/browser/browser_main_loop.h" 11 #include "content/browser/browser_main_loop.h"
12 #include "content/browser/renderer_host/media/audio_sync_reader.h" 12 #include "content/browser/renderer_host/media/audio_sync_reader.h"
13 #include "content/common/media/audio_messages.h" 13 #include "content/common/media/audio_messages.h"
14 #include "content/public/browser/media_observer.h" 14 #include "content/public/browser/media_observer.h"
15 #include "media/audio/audio_io.h"
15 #include "media/audio/shared_memory_util.h" 16 #include "media/audio/shared_memory_util.h"
16 #include "media/base/audio_bus.h" 17 #include "media/base/audio_bus.h"
17 #include "media/base/limits.h" 18 #include "media/base/limits.h"
18 19
19 using media::AudioBus; 20 using media::AudioBus;
20 21
21 namespace content { 22 namespace content {
22 23
23 struct AudioRendererHost::AudioEntry { 24 struct AudioRendererHost::AudioEntry {
24 AudioEntry(); 25 AudioEntry();
25 ~AudioEntry(); 26 ~AudioEntry();
26 27
27 // The AudioOutputController that manages the audio stream. 28 // The AudioOutputController that manages the audio stream.
28 scoped_refptr<media::AudioOutputController> controller; 29 scoped_refptr<media::AudioOutputController> controller;
29 30
30 // The audio stream ID. 31 // The audio stream ID.
31 int stream_id; 32 int stream_id;
32 33
34 // The routing ID of the source render view.
35 int render_view_id;
36
33 // Shared memory for transmission of the audio data. 37 // Shared memory for transmission of the audio data.
34 base::SharedMemory shared_memory; 38 base::SharedMemory shared_memory;
35 39
36 // The synchronous reader to be used by the controller. We have the 40 // The synchronous reader to be used by the controller. We have the
37 // ownership of the reader. 41 // ownership of the reader.
38 scoped_ptr<media::AudioOutputController::SyncReader> reader; 42 scoped_ptr<media::AudioOutputController::SyncReader> reader;
39 43
44 // When non-NULL, normal audio output is being diverted for audio mirroring.
45 media::AudioOutputStream::AudioSourceCallback* diverted_callback;
46
40 // Set to true after we called Close() for the controller. 47 // Set to true after we called Close() for the controller.
41 bool pending_close; 48 bool pending_close;
42 }; 49 };
43 50
44 AudioRendererHost::AudioEntry::AudioEntry() 51 AudioRendererHost::AudioEntry::AudioEntry()
45 : stream_id(0), 52 : stream_id(0),
53 render_view_id(MSG_ROUTING_NONE),
54 diverted_callback(NULL),
46 pending_close(false) { 55 pending_close(false) {
47 } 56 }
48 57
49 AudioRendererHost::AudioEntry::~AudioEntry() {} 58 AudioRendererHost::AudioEntry::~AudioEntry() {
59 DCHECK(!diverted_callback);
60 }
61
62 AudioRendererHost::MirroringDestination::~MirroringDestination() {}
50 63
51 /////////////////////////////////////////////////////////////////////////////// 64 ///////////////////////////////////////////////////////////////////////////////
52 // AudioRendererHost implementations. 65 // AudioRendererHost implementations.
53 AudioRendererHost::AudioRendererHost( 66 AudioRendererHost::AudioRendererHost(
67 int render_process_id,
54 media::AudioManager* audio_manager, MediaObserver* media_observer) 68 media::AudioManager* audio_manager, MediaObserver* media_observer)
55 : audio_manager_(audio_manager), 69 : render_process_id_(render_process_id),
70 audio_manager_(audio_manager),
56 media_observer_(media_observer) { 71 media_observer_(media_observer) {
57 } 72 }
58 73
59 AudioRendererHost::~AudioRendererHost() { 74 AudioRendererHost::~AudioRendererHost() {
60 DCHECK(audio_entries_.empty()); 75 DCHECK(audio_entries_.empty());
76 DCHECK(mirror_sessions_.empty());
77 }
78
79 base::LazyInstance<AudioRendererHost::ActiveHostMap>::Leaky
80 AudioRendererHost::g_host_map_ = LAZY_INSTANCE_INITIALIZER;
81
82 void AudioRendererHost::OnChannelConnected(int32 peer_pid) {
83 g_host_map_.Get().insert(std::make_pair(render_process_id_, this));
84
85 BrowserMessageFilter::OnChannelConnected(peer_pid);
61 } 86 }
62 87
63 void AudioRendererHost::OnChannelClosing() { 88 void AudioRendererHost::OnChannelClosing() {
64 BrowserMessageFilter::OnChannelClosing(); 89 BrowserMessageFilter::OnChannelClosing();
65 90
66 // Since the IPC channel is gone, close all requested audio streams. 91 // Since the IPC channel is gone, close all requested audio streams.
67 DeleteEntries(); 92 DeleteEntries();
93
94 while (!mirror_sessions_.empty()) {
95 MirrorSessionMap::iterator it = mirror_sessions_.begin();
96 DoStopMirroring(render_process_id_, it->first, it->second);
97 }
98
99 g_host_map_.Get().erase(render_process_id_);
68 } 100 }
69 101
70 void AudioRendererHost::OnDestruct() const { 102 void AudioRendererHost::OnDestruct() const {
71 BrowserThread::DeleteOnIOThread::Destruct(this); 103 BrowserThread::DeleteOnIOThread::Destruct(this);
72 } 104 }
73 105
74 /////////////////////////////////////////////////////////////////////////////// 106 ///////////////////////////////////////////////////////////////////////////////
75 // media::AudioOutputController::EventHandler implementations. 107 // media::AudioOutputController::EventHandler implementations.
76 void AudioRendererHost::OnCreated(media::AudioOutputController* controller) { 108 void AudioRendererHost::OnCreated(media::AudioOutputController* controller) {
77 BrowserThread::PostTask( 109 BrowserThread::PostTask(
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
209 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) 241 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream)
210 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream) 242 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream)
211 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) 243 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream)
212 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) 244 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
213 IPC_MESSAGE_UNHANDLED(handled = false) 245 IPC_MESSAGE_UNHANDLED(handled = false)
214 IPC_END_MESSAGE_MAP_EX() 246 IPC_END_MESSAGE_MAP_EX()
215 247
216 return handled; 248 return handled;
217 } 249 }
218 250
251 // static
252 AudioRendererHost* AudioRendererHost::FromRenderProcessID(
253 int render_process_id) {
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
255
256 // Note: It's possible for look-ups to fail since, for example, an
257 // AudioRendererHost could be shutdown before StartMirroring() is called.
258 // It's common for mirroring sessions to target a different render
259 // process/view than the one that will receive the mirrored audio data.
260 // Example sequence of events:
261 //
262 // 1. RenderProcess_1 wants to mirror a tab within RenderProcess_2.
263 // 2. RenderProcess_1 starts sending IPCs to initiate a mirroring session.
264 // 3. In the meantime, RenderProcess_2 is shut down (e.g., tab closed). The
265 // AudioRendererHost for RenderProcess_2 is destroyed as a result.
266 // 4. A browser-side entity for RenderProcess_1 attempts to call
267 // StartMirroring(), but since the AudioRendererHost for RenderProcess_2
268 // is gone, the look-up here fails.
269
270 ActiveHostMap& host_map = g_host_map_.Get();
271 ActiveHostMap::const_iterator it = host_map.find(render_process_id);
272 return it == host_map.end() ? NULL : it->second;
273 }
274
275 // static
276 void AudioRendererHost::StartMirroring(
277 int render_process_id, int render_view_id,
278 const scoped_refptr<MirroringDestination>& destination) {
279 BrowserThread::PostTask(
280 BrowserThread::IO, FROM_HERE,
281 base::Bind(&AudioRendererHost::DoStartMirroring,
282 render_process_id, render_view_id, destination));
283 }
284
285 // static
286 void AudioRendererHost::DoStartMirroring(
287 int render_process_id, int render_view_id,
288 const scoped_refptr<MirroringDestination>& destination) {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
290 DCHECK(destination);
291
292 AudioRendererHost* const host = FromRenderProcessID(render_process_id);
293 if (!host) {
294 return; // See comment in FromRenderProcessID().
295 }
296
297 // Insert an entry into the set of active mirroring sessions. If a mirroring
298 // session is already active for |render_process_id| + |render_view_id|,
299 // replace the entry.
300 MirrorSessionMap::iterator session_it =
301 host->mirror_sessions_.find(render_view_id);
302 scoped_refptr<MirroringDestination> old_destination;
303 if (session_it == host->mirror_sessions_.end()) {
304 host->mirror_sessions_.insert(std::make_pair(render_view_id, destination));
305
306 DVLOG(1) << "Start mirroring render_process_id:render_view_id="
307 << render_process_id << ':' << render_view_id
308 << " --> MirroringDestination@" << destination;
309 } else {
310 old_destination = session_it->second;
311 session_it->second = destination;
312
313 DVLOG(1) << "Switch mirroring of render_process_id:render_view_id="
314 << render_process_id << ':' << render_view_id
315 << " MirroringDestination@" << old_destination
316 << " --> MirroringDestination@" << destination;
317 }
318
319 // Divert any existing audio outputs to |destination|. If streams were
320 // already diverted to the |old_destination|, move them to the new
321 // |destination|.
322 for (AudioEntryMap::const_iterator it = host->audio_entries_.begin();
323 it != host->audio_entries_.end(); ++it) {
324 AudioEntry* const entry = it->second;
325 if (entry->render_view_id == render_view_id && !entry->pending_close) {
326 if (old_destination) {
327 DCHECK(entry->diverted_callback);
328 old_destination->RemoveInput(
329 entry->diverted_callback,
330 base::Bind(&MirroringDestination::AddInput, destination,
331 entry->diverted_callback, entry->controller->params()));
332 } else {
333 DCHECK(!entry->diverted_callback);
334 entry->diverted_callback = entry->controller->Divert();
335 destination->AddInput(entry->diverted_callback,
336 entry->controller->params());
337 }
338 }
339 }
340 }
341
342 // static
343 void AudioRendererHost::StopMirroring(
344 int render_process_id, int render_view_id,
345 const scoped_refptr<MirroringDestination>& destination) {
346 BrowserThread::PostTask(
347 BrowserThread::IO, FROM_HERE,
348 base::Bind(&AudioRendererHost::DoStopMirroring,
349 render_process_id, render_view_id, destination));
350 }
351
352 // static
353 void AudioRendererHost::DoStopMirroring(
354 int render_process_id, int render_view_id,
355 const scoped_refptr<MirroringDestination>& destination) {
356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
357
358 AudioRendererHost* const host = FromRenderProcessID(render_process_id);
359 if (!host) {
360 return; // See comment in FromRenderProcessID().
361 }
362
363 MirrorSessionMap::iterator session_it =
364 host->mirror_sessions_.find(render_view_id);
365 if (session_it == host->mirror_sessions_.end() ||
366 destination != session_it->second) {
367 return;
368 }
369
370 DVLOG(1) << "Stop mirroring render_process_id:render_view_id="
371 << render_process_id << ':' << render_view_id
372 << " --> MirroringDestination@" << destination;
373
374 // Revert the "divert" for each audio stream currently active in the mirroring
375 // session being stopped.
376 for (AudioEntryMap::const_iterator it = host->audio_entries_.begin();
377 it != host->audio_entries_.end(); ++it) {
378 AudioEntry* const entry = it->second;
379 if (entry->render_view_id == render_view_id && entry->diverted_callback) {
380 destination->RemoveInput(
381 entry->diverted_callback,
382 base::Bind(&media::AudioOutputController::Revert, entry->controller,
383 entry->diverted_callback));
384 entry->diverted_callback = NULL;
385 }
386 }
387
388 host->mirror_sessions_.erase(session_it);
389 }
390
219 void AudioRendererHost::OnCreateStream( 391 void AudioRendererHost::OnCreateStream(
220 int stream_id, const media::AudioParameters& params, int input_channels) { 392 int stream_id, const media::AudioParameters& params, int input_channels) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
222 DCHECK(LookupById(stream_id) == NULL); 394 DCHECK(LookupById(stream_id) == NULL);
223 395
224 media::AudioParameters audio_params(params); 396 media::AudioParameters audio_params(params);
225 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); 397 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params);
226 DCHECK_GT(buffer_size, 0U); 398 DCHECK_GT(buffer_size, 0U);
227 DCHECK_LE(buffer_size, 399 DCHECK_LE(buffer_size,
228 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); 400 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes));
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 // If we have created the controller successfully, create an entry and add it 450 // If we have created the controller successfully, create an entry and add it
279 // to the map. 451 // to the map.
280 entry->stream_id = stream_id; 452 entry->stream_id = stream_id;
281 audio_entries_.insert(std::make_pair(stream_id, entry.release())); 453 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
282 if (media_observer_) 454 if (media_observer_)
283 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); 455 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created");
284 } 456 }
285 457
286 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id, 458 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
287 int render_view_id) { 459 int render_view_id) {
288 // TODO(miu): Will use render_view_id in upcoming change. 460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
289 DVLOG(1) << "AudioRendererHost@" << this 461
290 << "::OnAssociateStreamWithProducer(stream_id=" << stream_id 462 AudioEntry* const entry = LookupById(stream_id);
291 << ", render_view_id=" << render_view_id << ")"; 463 if (!entry) {
464 SendErrorMessage(stream_id);
465 return;
466 }
467
468 if (entry->render_view_id == render_view_id)
469 return; // No change.
470
471 MirrorSessionMap::const_iterator prev_session_it =
472 mirror_sessions_.find(entry->render_view_id);
473 entry->render_view_id = render_view_id;
474 MirrorSessionMap::const_iterator next_session_it =
475 mirror_sessions_.find(render_view_id);
476
477 // Determine whether this stream was already part of a mirroring session, and
478 // whether it should be participate in another mirroring session.
479 if (prev_session_it != mirror_sessions_.end() && entry->diverted_callback) {
480 if (next_session_it != mirror_sessions_.end() && !entry->pending_close) {
481 // Stream was part of a previous mirroring session, and needs to be
482 // transferred to a different one.
483 prev_session_it->second->RemoveInput(
484 entry->diverted_callback,
485 base::Bind(&MirroringDestination::AddInput, next_session_it->second,
486 entry->diverted_callback, entry->controller->params()));
487 } else {
488 // Stream is no longer participating in a mirroring session.
489 prev_session_it->second->RemoveInput(
490 entry->diverted_callback,
491 base::Bind(&media::AudioOutputController::Revert, entry->controller,
492 entry->diverted_callback));
493 entry->diverted_callback = NULL;
494 }
495 } else if (next_session_it != mirror_sessions_.end() &&
496 !entry->pending_close) {
497 // Stream wasn't before, but is now participating in a mirroring session.
498 entry->diverted_callback = entry->controller->Divert();
499 next_session_it->second->AddInput(entry->diverted_callback,
500 entry->controller->params());
501 }
292 } 502 }
293 503
294 void AudioRendererHost::OnPlayStream(int stream_id) { 504 void AudioRendererHost::OnPlayStream(int stream_id) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
296 506
297 AudioEntry* entry = LookupById(stream_id); 507 AudioEntry* entry = LookupById(stream_id);
298 if (!entry) { 508 if (!entry) {
299 SendErrorMessage(stream_id); 509 SendErrorMessage(stream_id);
300 return; 510 return;
301 } 511 }
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 for (AudioEntryMap::iterator i = audio_entries_.begin(); 583 for (AudioEntryMap::iterator i = audio_entries_.begin();
374 i != audio_entries_.end(); ++i) { 584 i != audio_entries_.end(); ++i) {
375 CloseAndDeleteStream(i->second); 585 CloseAndDeleteStream(i->second);
376 } 586 }
377 } 587 }
378 588
379 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) { 589 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
381 591
382 if (!entry->pending_close) { 592 if (!entry->pending_close) {
593 // If this stream is currently being mirrored, remove it from the mirroring
594 // session.
595 MirrorSessionMap::const_iterator session_it =
596 mirror_sessions_.find(entry->render_view_id);
597 if (session_it != mirror_sessions_.end() && entry->diverted_callback) {
598 session_it->second->RemoveInput(
599 entry->diverted_callback,
600 base::Bind(&media::AudioOutputController::Revert, entry->controller,
601 entry->diverted_callback));
602 }
603
383 entry->controller->Close( 604 entry->controller->Close(
384 base::Bind(&AudioRendererHost::DeleteEntry, this, entry)); 605 base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
606 entry->diverted_callback = NULL;
385 entry->pending_close = true; 607 entry->pending_close = true;
386 } 608 }
387 } 609 }
388 610
389 void AudioRendererHost::DeleteEntry(AudioEntry* entry) { 611 void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 612 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
391 613
392 // Delete the entry when this method goes out of scope. 614 // Delete the entry when this method goes out of scope.
393 scoped_ptr<AudioEntry> entry_deleter(entry); 615 scoped_ptr<AudioEntry> entry_deleter(entry);
394 616
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 return NULL; 657 return NULL;
436 } 658 }
437 659
438 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( 660 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting(
439 int stream_id) { 661 int stream_id) {
440 AudioEntry* const entry = LookupById(stream_id); 662 AudioEntry* const entry = LookupById(stream_id);
441 return entry ? entry->controller : NULL; 663 return entry ? entry->controller : NULL;
442 } 664 }
443 665
444 } // namespace content 666 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698