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