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

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: More style fixes 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)) {
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
miu 2015/06/09 19:49:25 This sounds like a TODO item. Is the behavior sup
Guido Urdaneta 2015/06/10 09:44:55 Done.
460 // because MediaStreamUIProxy::CheckAccess does not currently support
461 // MEDIA_DEVICE_AUDIO_OUTPUT.
462 ui_proxy->CheckAccess(
463 security_origin, MEDIA_DEVICE_AUDIO_CAPTURE,
464 render_process_id_, render_frame_id,
465 base::Bind(&AudioRendererHost::OutputDeviceAccessChecked, this,
466 base::Passed(&ui_proxy), stream_id, device_id,
467 security_origin, render_frame_id, request_id));
468 }
469 }
470
471 void AudioRendererHost::OutputDeviceAccessChecked(
472 scoped_ptr<MediaStreamUIProxy> ui_proxy,
473 int stream_id,
474 const std::string& device_id,
475 const GURL& security_origin,
476 int render_frame_id,
477 int request_id,
478 bool have_access) {
479 DCHECK_CURRENTLY_ON(BrowserThread::IO);
480 DVLOG(1) << __FUNCTION__;
481 if (!have_access) {
482 DVLOG(0) << __FUNCTION__
483 << ": Have no access to media devices. Not switching device.";
484 Send(new AudioMsg_NotifyOutputDeviceSwitched(
485 stream_id, request_id, media::AudioOutputIPCDelegate::kSecurityError));
486 return;
487 }
488
489 scoped_refptr<base::SingleThreadTaskRunner> audio_worker_runner =
490 AudioManager::Get()->GetWorkerTaskRunner();
491 audio_worker_runner->PostTask(
492 FROM_HERE,
493 base::Bind(&AudioRendererHost::StartTranslateOutputDeviceName, this,
494 stream_id, device_id, security_origin, request_id));
495 }
496
497 void AudioRendererHost::StartTranslateOutputDeviceName(
498 int stream_id,
499 const std::string& device_id,
500 const GURL& security_origin,
501 int request_id) {
502 DCHECK(AudioManager::Get()->GetWorkerTaskRunner()->BelongsToCurrentThread());
503 DCHECK(!device_id.empty());
504 DVLOG(1) << __FUNCTION__;
505
506 media::AudioDeviceNames* device_names = new media::AudioDeviceNames;
tommi (sloooow) - chröme 2015/06/09 20:27:48 nit: use ()
Guido Urdaneta 2015/06/10 09:44:55 Done.
507 AudioManager::Get()->GetAudioOutputDeviceNames(device_names);
508
509 BrowserThread::PostTask(
510 BrowserThread::IO, FROM_HERE,
511 base::Bind(&AudioRendererHost::FinishTranslateOutputDeviceName, this,
512 stream_id, device_id, security_origin, request_id,
513 base::Owned(device_names)));
514 }
515
516 void AudioRendererHost::FinishTranslateOutputDeviceName(
517 int stream_id,
518 const std::string& device_id,
519 const GURL& security_origin,
520 int request_id,
521 media::AudioDeviceNames* device_names) {
522 DCHECK_CURRENTLY_ON(BrowserThread::IO);
523 DCHECK(!device_id.empty());
524 DVLOG(1) << __FUNCTION__;
525
526 std::string raw_device_id = "";
tommi (sloooow) - chröme 2015/06/09 20:27:48 nit: initialization to "" not necessary...(or?)
Guido Urdaneta 2015/06/10 09:44:55 Done.
527 // Process the enumeration here because |salt_callback_| can run
528 // only on the IO thread
529 for (const auto& device_name : *device_names) {
530 const std::string candidate_device_id = content::GetHMACForMediaDeviceID(
531 salt_callback_, security_origin, device_name.unique_id);
532 if (candidate_device_id == device_id) {
533 DVLOG(1) << "Requested device " << device_name.unique_id << " - "
534 << device_name.device_name;
535 raw_device_id = device_name.unique_id;
536 }
537 }
538
539 if (raw_device_id.empty()) {
540 DVLOG(1) << "Requested device " << device_id << " could not be found.";
541 Send(new AudioMsg_NotifyOutputDeviceSwitched(
542 stream_id, request_id, media::AudioOutputIPCDelegate::kNotFoundError));
543 return;
544 }
545
546 DoSwitchOutputDevice(stream_id, raw_device_id, request_id);
547 }
548
549 void AudioRendererHost::DoSwitchOutputDevice(int stream_id,
550 const std::string& raw_device_id,
551 int request_id) {
552 DCHECK_CURRENTLY_ON(BrowserThread::IO);
553 DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << raw_device_id << ", "
554 << request_id << ")";
555 AudioEntry* entry = LookupById(stream_id);
556 if (!entry) {
557 Send(new AudioMsg_NotifyOutputDeviceSwitched(
558 stream_id, request_id, media::AudioOutputIPCDelegate::kOtherError));
miu 2015/06/09 19:49:25 This is the only place kOtherError is provided. S
Guido Urdaneta 2015/06/10 09:44:55 Done.
559 return;
560 }
561
562 entry->controller()->SwitchOutputDevice(
563 raw_device_id, base::Bind(&AudioRendererHost::DoOutputDeviceSwitched,
564 this, stream_id, request_id));
565 audio_log_->OnSwitchOutputDevice(entry->stream_id(), raw_device_id);
566 }
567
568 void AudioRendererHost::DoOutputDeviceSwitched(int stream_id, int request_id) {
569 DCHECK_CURRENTLY_ON(BrowserThread::IO);
570 DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << request_id << ")";
571 Send(new AudioMsg_NotifyOutputDeviceSwitched(
572 stream_id, request_id, media::AudioOutputIPCDelegate::kSuccess));
573 }
574
425 void AudioRendererHost::SendErrorMessage(int stream_id) { 575 void AudioRendererHost::SendErrorMessage(int stream_id) {
426 Send(new AudioMsg_NotifyStreamStateChanged( 576 Send(new AudioMsg_NotifyStreamStateChanged(
427 stream_id, media::AudioOutputIPCDelegate::kError)); 577 stream_id, media::AudioOutputIPCDelegate::kError));
428 } 578 }
429 579
430 void AudioRendererHost::OnCloseStream(int stream_id) { 580 void AudioRendererHost::OnCloseStream(int stream_id) {
431 DCHECK_CURRENTLY_ON(BrowserThread::IO); 581 DCHECK_CURRENTLY_ON(BrowserThread::IO);
432 582
433 // Prevent oustanding callbacks from attempting to close/delete the same 583 // Prevent oustanding callbacks from attempting to close/delete the same
434 // AudioEntry twice. 584 // AudioEntry twice.
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
512 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); 662 for (AudioEntryMap::const_iterator it = audio_entries_.begin();
513 it != audio_entries_.end(); 663 it != audio_entries_.end();
514 ++it) { 664 ++it) {
515 AudioEntry* entry = it->second; 665 AudioEntry* entry = it->second;
516 if (entry->render_frame_id() == render_frame_id && entry->playing()) 666 if (entry->render_frame_id() == render_frame_id && entry->playing())
517 return true; 667 return true;
518 } 668 }
519 return false; 669 return false;
520 } 670 }
521 671
672 bool AudioRendererHost::IsURLAllowed(const GURL& url) {
673 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
674 render_process_id_, url)) {
675 LOG(ERROR) << "MSDH: Renderer requested a URL it's not allowed to use.";
676 return false;
677 }
678 return true;
679 }
680
522 } // namespace content 681 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698