OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |