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

Side by Side Diff: content/browser/renderer_host/media/audio_renderer_host.cc

Issue 1171953002: Add IPC interface for switching the audio output device for a given audio stream in the browser. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address reviewers' comments Created 5 years, 6 months 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
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/bind_helpers.h" 8 #include "base/bind_helpers.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/shared_memory.h" 10 #include "base/memory/shared_memory.h"
11 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
12 #include "base/process/process.h" 12 #include "base/process/process.h"
13 #include "content/browser/browser_main_loop.h" 13 #include "content/browser/browser_main_loop.h"
14 #include "content/browser/child_process_security_policy_impl.h"
14 #include "content/browser/loader/resource_dispatcher_host_impl.h" 15 #include "content/browser/loader/resource_dispatcher_host_impl.h"
15 #include "content/browser/media/audio_stream_monitor.h" 16 #include "content/browser/media/audio_stream_monitor.h"
16 #include "content/browser/media/capture/audio_mirroring_manager.h" 17 #include "content/browser/media/capture/audio_mirroring_manager.h"
17 #include "content/browser/media/media_internals.h" 18 #include "content/browser/media/media_internals.h"
18 #include "content/browser/renderer_host/media/audio_input_device_manager.h" 19 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
19 #include "content/browser/renderer_host/media/audio_sync_reader.h" 20 #include "content/browser/renderer_host/media/audio_sync_reader.h"
20 #include "content/browser/renderer_host/media/media_stream_manager.h" 21 #include "content/browser/renderer_host/media/media_stream_manager.h"
22 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
21 #include "content/browser/renderer_host/render_widget_host_impl.h" 23 #include "content/browser/renderer_host/render_widget_host_impl.h"
22 #include "content/common/media/audio_messages.h" 24 #include "content/common/media/audio_messages.h"
23 #include "content/public/browser/content_browser_client.h" 25 #include "content/public/browser/content_browser_client.h"
26 #include "content/public/browser/media_device_id.h"
24 #include "content/public/browser/media_observer.h" 27 #include "content/public/browser/media_observer.h"
25 #include "content/public/browser/render_frame_host.h" 28 #include "content/public/browser/render_frame_host.h"
26 #include "content/public/browser/render_view_host.h" 29 #include "content/public/browser/render_view_host.h"
27 #include "content/public/common/content_switches.h" 30 #include "content/public/common/content_switches.h"
31 #include "media/audio/audio_device_name.h"
28 #include "media/audio/audio_manager_base.h" 32 #include "media/audio/audio_manager_base.h"
29 #include "media/base/audio_bus.h" 33 #include "media/base/audio_bus.h"
30 #include "media/base/limits.h" 34 #include "media/base/limits.h"
31 35
32 using media::AudioBus; 36 using media::AudioBus;
33 using media::AudioManager; 37 using media::AudioManager;
34 38
35 namespace content { 39 namespace content {
36 40
37 namespace { 41 namespace {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 AudioRendererHost::AudioEntry::~AudioEntry() {} 143 AudioRendererHost::AudioEntry::~AudioEntry() {}
140 144
141 /////////////////////////////////////////////////////////////////////////////// 145 ///////////////////////////////////////////////////////////////////////////////
142 // AudioRendererHost implementations. 146 // AudioRendererHost implementations.
143 147
144 AudioRendererHost::AudioRendererHost( 148 AudioRendererHost::AudioRendererHost(
145 int render_process_id, 149 int render_process_id,
146 media::AudioManager* audio_manager, 150 media::AudioManager* audio_manager,
147 AudioMirroringManager* mirroring_manager, 151 AudioMirroringManager* mirroring_manager,
148 MediaInternals* media_internals, 152 MediaInternals* media_internals,
149 MediaStreamManager* media_stream_manager) 153 MediaStreamManager* media_stream_manager,
154 const ResourceContext::SaltCallback& salt_callback)
150 : BrowserMessageFilter(AudioMsgStart), 155 : BrowserMessageFilter(AudioMsgStart),
151 render_process_id_(render_process_id), 156 render_process_id_(render_process_id),
152 audio_manager_(audio_manager), 157 audio_manager_(audio_manager),
153 mirroring_manager_(mirroring_manager), 158 mirroring_manager_(mirroring_manager),
154 audio_log_(media_internals->CreateAudioLog( 159 audio_log_(media_internals->CreateAudioLog(
155 media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER)), 160 media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER)),
156 media_stream_manager_(media_stream_manager), 161 media_stream_manager_(media_stream_manager),
157 num_playing_streams_(0) { 162 num_playing_streams_(0),
163 salt_callback_(salt_callback) {
158 DCHECK(audio_manager_); 164 DCHECK(audio_manager_);
159 DCHECK(media_stream_manager_); 165 DCHECK(media_stream_manager_);
160 } 166 }
161 167
162 AudioRendererHost::~AudioRendererHost() { 168 AudioRendererHost::~AudioRendererHost() {
163 DCHECK(audio_entries_.empty()); 169 DCHECK(audio_entries_.empty());
164 } 170 }
165 171
166 void AudioRendererHost::GetOutputControllers( 172 void AudioRendererHost::GetOutputControllers(
167 const RenderProcessHost::GetAudioOutputControllersCallback& 173 const RenderProcessHost::GetAudioOutputControllersCallback&
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 /////////////////////////////////////////////////////////////////////////////// 309 ///////////////////////////////////////////////////////////////////////////////
304 // IPC Messages handler 310 // IPC Messages handler
305 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { 311 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) {
306 bool handled = true; 312 bool handled = true;
307 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) 313 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message)
308 IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream) 314 IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream)
309 IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream) 315 IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream)
310 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) 316 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream)
311 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) 317 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream)
312 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) 318 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume)
319 IPC_MESSAGE_HANDLER(AudioHostMsg_SwitchOutputDevice, OnSwitchOutputDevice)
313 IPC_MESSAGE_UNHANDLED(handled = false) 320 IPC_MESSAGE_UNHANDLED(handled = false)
314 IPC_END_MESSAGE_MAP() 321 IPC_END_MESSAGE_MAP()
315 322
316 return handled; 323 return handled;
317 } 324 }
318 325
319 void AudioRendererHost::OnCreateStream(int stream_id, 326 void AudioRendererHost::OnCreateStream(int stream_id,
320 int render_frame_id, 327 int render_frame_id,
321 int session_id, 328 int session_id,
322 const media::AudioParameters& params) { 329 const media::AudioParameters& params) {
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 return; 422 return;
416 } 423 }
417 424
418 // Make sure the volume is valid. 425 // Make sure the volume is valid.
419 if (volume < 0 || volume > 1.0) 426 if (volume < 0 || volume > 1.0)
420 return; 427 return;
421 entry->controller()->SetVolume(volume); 428 entry->controller()->SetVolume(volume);
422 audio_log_->OnSetVolume(stream_id, volume); 429 audio_log_->OnSetVolume(stream_id, volume);
423 } 430 }
424 431
432 void AudioRendererHost::OnSwitchOutputDevice(int stream_id,
433 int render_frame_id,
434 const std::string& device_id,
435 const GURL& security_origin,
436 int request_id) {
437 DCHECK_CURRENTLY_ON(BrowserThread::IO);
438 DVLOG(1) << "AudioRendererHost@" << this
439 << "::OnSwitchOutputDevice(stream_id=" << stream_id
440 << ", render_frame_id=" << render_frame_id
441 << ", device_id=" << device_id
442 << ", security_origin=" << security_origin
443 << ", request_id=" << request_id << ")";
444 if (!IsURLAllowed(security_origin)) {
ncarter (slow) 2015/06/10 18:10:48 When a renderer steps out of bounds like this, the
Guido Urdaneta 2015/06/11 00:51:57 Done.
445 Send(new AudioMsg_NotifyOutputDeviceSwitched(
446 stream_id, request_id, media::AudioOutputIPCDelegate::kSecurityError));
447 return;
448 }
449
450 if (device_id.empty()) {
451 DVLOG(1) << __FUNCTION__ << ": default output device requested. "
452 << "No permissions check or device translation/validation needed.";
453 DoSwitchOutputDevice(stream_id, device_id, request_id);
454 } else {
455 // Check that MediaStream device permissions have been granted,
456 // hence the use of a MediaStreamUIProxy.
457 scoped_ptr<MediaStreamUIProxy> ui_proxy = MediaStreamUIProxy::Create();
458
459 // Use MEDIA_DEVICE_AUDIO_CAPTURE instead of MEDIA_DEVICE_AUDIO_OUTPUT
460 // because MediaStreamUIProxy::CheckAccess does not currently support
461 // MEDIA_DEVICE_AUDIO_OUTPUT.
462 // TODO(guidou): Change to MEDIA_DEVICE_AUDIO_OUTPUT when support becomes
463 // available. http://crbug.com/498675
464 ui_proxy->CheckAccess(
465 security_origin, MEDIA_DEVICE_AUDIO_CAPTURE,
466 render_process_id_, render_frame_id,
467 base::Bind(&AudioRendererHost::OutputDeviceAccessChecked, this,
468 base::Passed(&ui_proxy), stream_id, device_id,
469 security_origin, render_frame_id, request_id));
470 }
471 }
472
473 void AudioRendererHost::OutputDeviceAccessChecked(
474 scoped_ptr<MediaStreamUIProxy> ui_proxy,
475 int stream_id,
476 const std::string& device_id,
477 const GURL& security_origin,
478 int render_frame_id,
479 int request_id,
480 bool have_access) {
481 DCHECK_CURRENTLY_ON(BrowserThread::IO);
482 DVLOG(1) << __FUNCTION__;
483 if (!have_access) {
484 DVLOG(0) << __FUNCTION__
485 << ": Have no access to media devices. Not switching device.";
486 Send(new AudioMsg_NotifyOutputDeviceSwitched(
487 stream_id, request_id, media::AudioOutputIPCDelegate::kSecurityError));
ncarter (slow) 2015/06/10 18:10:47 Get rid of kSecurityError, and use bad_message. I
Guido Urdaneta 2015/06/11 00:51:57 In this case, we have to communicate the problem b
488 return;
489 }
490
491 scoped_refptr<base::SingleThreadTaskRunner> audio_worker_runner =
492 AudioManager::Get()->GetWorkerTaskRunner();
493 audio_worker_runner->PostTask(
494 FROM_HERE,
495 base::Bind(&AudioRendererHost::StartTranslateOutputDeviceName, this,
496 stream_id, device_id, security_origin, request_id));
497 }
498
499 void AudioRendererHost::StartTranslateOutputDeviceName(
500 int stream_id,
501 const std::string& device_id,
502 const GURL& security_origin,
503 int request_id) {
504 DCHECK(AudioManager::Get()->GetWorkerTaskRunner()->BelongsToCurrentThread());
505 DCHECK(!device_id.empty());
506 DVLOG(1) << __FUNCTION__;
507
508 media::AudioDeviceNames* device_names(new media::AudioDeviceNames);
509 AudioManager::Get()->GetAudioOutputDeviceNames(device_names);
510
511 BrowserThread::PostTask(
512 BrowserThread::IO, FROM_HERE,
513 base::Bind(&AudioRendererHost::FinishTranslateOutputDeviceName, this,
514 stream_id, device_id, security_origin, request_id,
515 base::Owned(device_names)));
516 }
517
518 void AudioRendererHost::FinishTranslateOutputDeviceName(
519 int stream_id,
520 const std::string& device_id,
521 const GURL& security_origin,
522 int request_id,
523 media::AudioDeviceNames* device_names) {
524 DCHECK_CURRENTLY_ON(BrowserThread::IO);
525 DCHECK(!device_id.empty());
526 DVLOG(1) << __FUNCTION__;
527
528 std::string raw_device_id;
529 // Process the enumeration here because |salt_callback_| can run
530 // only on the IO thread
531 for (const auto& device_name : *device_names) {
532 const std::string candidate_device_id = content::GetHMACForMediaDeviceID(
533 salt_callback_, security_origin, device_name.unique_id);
534 if (candidate_device_id == device_id) {
535 DVLOG(1) << "Requested device " << device_name.unique_id << " - "
536 << device_name.device_name;
537 raw_device_id = device_name.unique_id;
538 }
539 }
540
541 if (raw_device_id.empty()) {
542 DVLOG(1) << "Requested device " << device_id << " could not be found.";
543 Send(new AudioMsg_NotifyOutputDeviceSwitched(
544 stream_id, request_id, media::AudioOutputIPCDelegate::kNotFoundError));
545 return;
546 }
547
548 DoSwitchOutputDevice(stream_id, raw_device_id, request_id);
549 }
550
551 void AudioRendererHost::DoSwitchOutputDevice(int stream_id,
552 const std::string& raw_device_id,
553 int request_id) {
554 DCHECK_CURRENTLY_ON(BrowserThread::IO);
555 DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << raw_device_id << ", "
556 << request_id << ")";
557 AudioEntry* entry = LookupById(stream_id);
558 if (!entry) {
559 Send(new AudioMsg_NotifyOutputDeviceSwitched(
560 stream_id, request_id, media::AudioOutputIPCDelegate::kObsoleteError));
561 return;
562 }
563
564 entry->controller()->SwitchOutputDevice(
565 raw_device_id, base::Bind(&AudioRendererHost::DoOutputDeviceSwitched,
566 this, stream_id, request_id));
567 audio_log_->OnSwitchOutputDevice(entry->stream_id(), raw_device_id);
568 }
569
570 void AudioRendererHost::DoOutputDeviceSwitched(int stream_id, int request_id) {
571 DCHECK_CURRENTLY_ON(BrowserThread::IO);
572 DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << request_id << ")";
573 Send(new AudioMsg_NotifyOutputDeviceSwitched(
574 stream_id, request_id, media::AudioOutputIPCDelegate::kSuccess));
575 }
576
425 void AudioRendererHost::SendErrorMessage(int stream_id) { 577 void AudioRendererHost::SendErrorMessage(int stream_id) {
426 Send(new AudioMsg_NotifyStreamStateChanged( 578 Send(new AudioMsg_NotifyStreamStateChanged(
427 stream_id, media::AudioOutputIPCDelegate::kError)); 579 stream_id, media::AudioOutputIPCDelegate::kError));
428 } 580 }
429 581
430 void AudioRendererHost::OnCloseStream(int stream_id) { 582 void AudioRendererHost::OnCloseStream(int stream_id) {
431 DCHECK_CURRENTLY_ON(BrowserThread::IO); 583 DCHECK_CURRENTLY_ON(BrowserThread::IO);
432 584
433 // Prevent oustanding callbacks from attempting to close/delete the same 585 // Prevent oustanding callbacks from attempting to close/delete the same
434 // AudioEntry twice. 586 // AudioEntry twice.
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
512 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); 664 for (AudioEntryMap::const_iterator it = audio_entries_.begin();
513 it != audio_entries_.end(); 665 it != audio_entries_.end();
514 ++it) { 666 ++it) {
515 AudioEntry* entry = it->second; 667 AudioEntry* entry = it->second;
516 if (entry->render_frame_id() == render_frame_id && entry->playing()) 668 if (entry->render_frame_id() == render_frame_id && entry->playing())
517 return true; 669 return true;
518 } 670 }
519 return false; 671 return false;
520 } 672 }
521 673
674 bool AudioRendererHost::IsURLAllowed(const GURL& url) {
675 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
676 render_process_id_, url)) {
677 LOG(ERROR) << "MSDH: Renderer requested a URL it's not allowed to use.";
ncarter (slow) 2015/06/10 18:10:48 You don't need this LOG if you switch to bad_messa
Guido Urdaneta 2015/06/11 00:51:57 Done.
678 return false;
679 }
680 return true;
681 }
682
522 } // namespace content 683 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698