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

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: Remove WCAudioInputStream changes (split into another change). 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 ActiveHostMap& host_map = g_host_map_.Get();
257 ActiveHostMap::const_iterator it = host_map.find(render_process_id);
258 return it == host_map.end() ? NULL : it->second;
DaleCurtis 2012/12/05 23:35:14 Should this just CHECK() fail on missing entry?
miu 2012/12/11 02:30:45 Short answer: No, because it's possible for look-u
259 }
260
261 // static
262 void AudioRendererHost::StartMirroring(
263 int render_process_id, int render_view_id,
264 const scoped_refptr<MirroringDestination>& destination) {
265 BrowserThread::PostTask(
266 BrowserThread::IO, FROM_HERE,
267 base::Bind(&AudioRendererHost::DoStartMirroring,
268 render_process_id, render_view_id, destination));
269 }
270
271 // static
272 void AudioRendererHost::DoStartMirroring(
273 int render_process_id, int render_view_id,
274 const scoped_refptr<MirroringDestination>& destination) {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
276 DCHECK(destination);
277
278 AudioRendererHost* const host = FromRenderProcessID(render_process_id);
279 if (!host) {
DaleCurtis 2012/12/05 23:35:14 CHECK() instead?
miu 2012/12/11 02:30:45 Explained above.
280 return;
281 }
282
283 // Insert an entry into the set of active mirroring sessions. If a mirroring
284 // session is already active for |render_process_id| + |render_view_id|,
285 // replace the entry.
286 MirrorSessionMap::iterator session_it =
287 host->mirror_sessions_.find(render_view_id);
288 scoped_refptr<MirroringDestination> old_destination;
289 if (session_it == host->mirror_sessions_.end()) {
290 host->mirror_sessions_.insert(std::make_pair(render_view_id, destination));
291
292 DVLOG(1) << "Start mirroring render_process_id:render_view_id="
293 << render_process_id << ':' << render_view_id
294 << " --> MirroringDestination@" << destination;
295 } else {
296 old_destination = session_it->second;
297 session_it->second = destination;
298
299 DVLOG(1) << "Switch mirroring of render_process_id:render_view_id="
300 << render_process_id << ':' << render_view_id
301 << " MirroringDestination@" << old_destination
302 << " --> MirroringDestination@" << destination;
303 }
304
305 // Divert any existing audio outputs to |destination|. If streams were
DaleCurtis 2012/12/05 23:35:14 This seems strange. Are you saying callers can cha
miu 2012/12/11 02:30:45 Yes. Two ways to answer this: 1. Architecture: L
306 // already diverted to the |old_destination|, move them to the new
307 // |destination|.
308 for (AudioEntryMap::const_iterator it = host->audio_entries_.begin();
309 it != host->audio_entries_.end(); ++it) {
310 AudioEntry* const entry = it->second;
311 if (entry->render_view_id == render_view_id && !entry->pending_close) {
312 if (old_destination) {
313 DCHECK(entry->diverted_callback);
314 old_destination->RemoveInput(entry->diverted_callback);
315 } else {
316 DCHECK(!entry->diverted_callback);
317 entry->diverted_callback = entry->controller->Divert();
318 }
319 destination->AddInput(entry->diverted_callback,
320 entry->controller->params());
321 }
322 }
323 }
324
325 // static
326 void AudioRendererHost::StopMirroring(
327 int render_process_id, int render_view_id,
328 const scoped_refptr<MirroringDestination>& destination) {
329 BrowserThread::PostTask(
330 BrowserThread::IO, FROM_HERE,
331 base::Bind(&AudioRendererHost::DoStopMirroring,
332 render_process_id, render_view_id, destination));
333 }
334
335 // static
336 void AudioRendererHost::DoStopMirroring(
337 int render_process_id, int render_view_id,
338 const scoped_refptr<MirroringDestination>& destination) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
340
341 AudioRendererHost* const host = FromRenderProcessID(render_process_id);
342 if (!host) {
DaleCurtis 2012/12/05 23:35:14 Same CHECK() comment.
miu 2012/12/11 02:30:45 Explained above.
343 return;
344 }
345
346 MirrorSessionMap::iterator session_it =
347 host->mirror_sessions_.find(render_view_id);
348 if (session_it == host->mirror_sessions_.end() ||
DaleCurtis 2012/12/05 23:35:14 Ditto.
miu 2012/12/11 02:30:45 This "if" check is necessary because StartMirrorin
349 destination != session_it->second) {
350 return;
351 }
352
353 DVLOG(1) << "Stop mirroring render_process_id:render_view_id="
354 << render_process_id << ':' << render_view_id
355 << " --> MirroringDestination@" << destination;
356
357 // Revert the "divert" for each audio stream currently active in the mirroring
358 // session being stopped.
359 for (AudioEntryMap::const_iterator it = host->audio_entries_.begin();
360 it != host->audio_entries_.end(); ++it) {
361 AudioEntry* const entry = it->second;
362 if (entry->render_view_id == render_view_id && entry->diverted_callback) {
363 destination->RemoveInput(entry->diverted_callback);
364 entry->controller->Revert(entry->diverted_callback);
365 entry->diverted_callback = NULL;
366 }
367 }
368
369 host->mirror_sessions_.erase(session_it);
370 }
371
219 void AudioRendererHost::OnCreateStream( 372 void AudioRendererHost::OnCreateStream(
220 int stream_id, const media::AudioParameters& params, int input_channels) { 373 int stream_id, const media::AudioParameters& params, int input_channels) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
222 DCHECK(LookupById(stream_id) == NULL); 375 DCHECK(LookupById(stream_id) == NULL);
223 376
224 media::AudioParameters audio_params(params); 377 media::AudioParameters audio_params(params);
225 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); 378 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params);
226 DCHECK_GT(buffer_size, 0U); 379 DCHECK_GT(buffer_size, 0U);
227 DCHECK_LE(buffer_size, 380 DCHECK_LE(buffer_size,
228 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); 381 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 431 // If we have created the controller successfully, create an entry and add it
279 // to the map. 432 // to the map.
280 entry->stream_id = stream_id; 433 entry->stream_id = stream_id;
281 audio_entries_.insert(std::make_pair(stream_id, entry.release())); 434 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
282 if (media_observer_) 435 if (media_observer_)
283 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); 436 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created");
284 } 437 }
285 438
286 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id, 439 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id,
287 int render_view_id) { 440 int render_view_id) {
288 // TODO(miu): Will use render_view_id in upcoming change. 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
289 DVLOG(1) << "AudioRendererHost@" << this 442
290 << "::OnAssociateStreamWithProducer(stream_id=" << stream_id 443 AudioEntry* const entry = LookupById(stream_id);
291 << ", render_view_id=" << render_view_id << ")"; 444 if (!entry) {
445 SendErrorMessage(stream_id);
446 return;
447 }
448
449 if (entry->render_view_id == render_view_id)
450 return; // No change.
451
452 // If this stream is currently being mirrored, remove it from that mirroring
453 // session.
454 MirrorSessionMap::const_iterator prev_session_it =
455 mirror_sessions_.find(entry->render_view_id);
456 if (prev_session_it != mirror_sessions_.end()) {
457 if (entry->diverted_callback) {
458 prev_session_it->second->RemoveInput(entry->diverted_callback);
459 }
460 }
461
462 entry->render_view_id = render_view_id;
463
464 // If a mirroring session is already active for the RenderView, add this
465 // stream to that mirroring session.
466 MirrorSessionMap::const_iterator next_session_it =
467 mirror_sessions_.find(render_view_id);
468 if (next_session_it != mirror_sessions_.end()) {
469 if (!entry->pending_close) {
470 if (!entry->diverted_callback) {
471 entry->diverted_callback = entry->controller->Divert();
472 }
473 next_session_it->second->AddInput(entry->diverted_callback,
474 entry->controller->params());
475 }
476 } else if (entry->diverted_callback) {
477 // Clean-up: The audio stream was removed from the previous mirroring
478 // session, but there is no other mirroring session for it to be added to.
479 // So, "revert the divert."
480 entry->controller->Revert(entry->diverted_callback);
481 entry->diverted_callback = NULL;
482 }
292 } 483 }
293 484
294 void AudioRendererHost::OnPlayStream(int stream_id) { 485 void AudioRendererHost::OnPlayStream(int stream_id) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
296 487
297 AudioEntry* entry = LookupById(stream_id); 488 AudioEntry* entry = LookupById(stream_id);
298 if (!entry) { 489 if (!entry) {
299 SendErrorMessage(stream_id); 490 SendErrorMessage(stream_id);
300 return; 491 return;
301 } 492 }
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 for (AudioEntryMap::iterator i = audio_entries_.begin(); 564 for (AudioEntryMap::iterator i = audio_entries_.begin();
374 i != audio_entries_.end(); ++i) { 565 i != audio_entries_.end(); ++i) {
375 CloseAndDeleteStream(i->second); 566 CloseAndDeleteStream(i->second);
376 } 567 }
377 } 568 }
378 569
379 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) { 570 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 571 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
381 572
382 if (!entry->pending_close) { 573 if (!entry->pending_close) {
574 // If this stream is currently being mirrored, remove it from the mirroring
575 // session.
576 MirrorSessionMap::const_iterator session_it =
577 mirror_sessions_.find(entry->render_view_id);
578 if (session_it != mirror_sessions_.end() && entry->diverted_callback) {
579 session_it->second->RemoveInput(entry->diverted_callback);
580 // Note: We don't call AudioOutputController::Revert() since we're about
581 // to close the controller below. If we did call Revert(), it would cause
582 // an unnecessary round of immediate starting and stopping.
583 }
584
383 entry->controller->Close( 585 entry->controller->Close(
384 base::Bind(&AudioRendererHost::DeleteEntry, this, entry)); 586 base::Bind(&AudioRendererHost::DeleteEntry, this, entry));
587 entry->diverted_callback = NULL;
385 entry->pending_close = true; 588 entry->pending_close = true;
386 } 589 }
387 } 590 }
388 591
389 void AudioRendererHost::DeleteEntry(AudioEntry* entry) { 592 void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 593 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
391 594
392 // Delete the entry when this method goes out of scope. 595 // Delete the entry when this method goes out of scope.
393 scoped_ptr<AudioEntry> entry_deleter(entry); 596 scoped_ptr<AudioEntry> entry_deleter(entry);
394 597
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 return NULL; 638 return NULL;
436 } 639 }
437 640
438 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( 641 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting(
439 int stream_id) { 642 int stream_id) {
440 AudioEntry* const entry = LookupById(stream_id); 643 AudioEntry* const entry = LookupById(stream_id);
441 return entry ? entry->controller : NULL; 644 return entry ? entry->controller : NULL;
442 } 645 }
443 646
444 } // namespace content 647 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698