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 <utility> | |
| 8 | |
| 7 #include "base/bind.h" | 9 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| 9 #include "base/logging.h" | 11 #include "base/logging.h" |
| 10 #include "base/memory/shared_memory.h" | 12 #include "base/memory/shared_memory.h" |
| 11 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 12 #include "base/process/process.h" | 14 #include "base/process/process.h" |
| 13 #include "content/browser/bad_message.h" | 15 #include "content/browser/bad_message.h" |
| 14 #include "content/browser/browser_main_loop.h" | 16 #include "content/browser/browser_main_loop.h" |
| 15 #include "content/browser/child_process_security_policy_impl.h" | 17 #include "content/browser/child_process_security_policy_impl.h" |
| 16 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 18 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 bool is_playing, | 56 bool is_playing, |
| 55 int render_view_id) { | 57 int render_view_id) { |
| 56 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 58 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 57 if (render_view_id == MSG_ROUTING_NONE || !ResourceDispatcherHostImpl::Get()) | 59 if (render_view_id == MSG_ROUTING_NONE || !ResourceDispatcherHostImpl::Get()) |
| 58 return; | 60 return; |
| 59 | 61 |
| 60 ResourceDispatcherHostImpl::Get()->OnAudioRenderHostStreamStateChanged( | 62 ResourceDispatcherHostImpl::Get()->OnAudioRenderHostStreamStateChanged( |
| 61 render_process_id, render_view_id, is_playing); | 63 render_process_id, render_view_id, is_playing); |
| 62 } | 64 } |
| 63 | 65 |
| 66 media::AudioParameters DummyParams() { | |
| 67 return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LINEAR, | |
| 68 media::CHANNEL_LAYOUT_STEREO, | |
| 69 media::limits::kMinSampleRate, 1, 1); | |
| 70 } | |
| 71 | |
| 64 } // namespace | 72 } // namespace |
| 65 | 73 |
| 66 class AudioRendererHost::AudioEntry | 74 class AudioRendererHost::AudioEntry |
| 67 : public media::AudioOutputController::EventHandler { | 75 : public media::AudioOutputController::EventHandler { |
| 68 public: | 76 public: |
| 69 AudioEntry(AudioRendererHost* host, | 77 AudioEntry(AudioRendererHost* host, |
| 70 int stream_id, | 78 int stream_id, |
| 71 int render_frame_id, | 79 int render_frame_id, |
| 72 const media::AudioParameters& params, | 80 const media::AudioParameters& params, |
| 73 const std::string& output_device_id, | 81 const std::string& output_device_id, |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 136 this, | 144 this, |
| 137 params, | 145 params, |
| 138 output_device_id, | 146 output_device_id, |
| 139 reader_.get())), | 147 reader_.get())), |
| 140 playing_(false) { | 148 playing_(false) { |
| 141 DCHECK(controller_.get()); | 149 DCHECK(controller_.get()); |
| 142 } | 150 } |
| 143 | 151 |
| 144 AudioRendererHost::AudioEntry::~AudioEntry() {} | 152 AudioRendererHost::AudioEntry::~AudioEntry() {} |
| 145 | 153 |
| 154 class AudioRendererHost::AuthorizationData { | |
|
DaleCurtis
2015/09/09 01:31:57
Hmm, this is a lot of data and complexity added to
Guido Urdaneta
2015/09/09 16:22:23
I removed this class altogether and replaced it wi
| |
| 155 public: | |
| 156 explicit AuthorizationData(int render_frame_id); | |
| 157 AuthorizationData(int render_frame_id, | |
| 158 const std::string& unique_id, | |
| 159 const media::AudioParameters& output_params); | |
| 160 ~AuthorizationData() = default; | |
| 161 bool is_authorized() const { return is_authorized_; } | |
| 162 int render_frame_id() const { return render_frame_id_; } | |
| 163 bool is_creation_requested() const { return is_creation_requested_; } | |
| 164 media::AudioParameters creation_params() const { return creation_params_; } | |
| 165 std::string device_unique_id() const { return device_unique_id_; } | |
| 166 media::AudioParameters device_output_params() const { | |
| 167 return device_output_params_; | |
| 168 } | |
| 169 | |
| 170 void Authorize(const std::string& unique_id, | |
| 171 const media::AudioParameters& output_params); | |
| 172 void RequestCreation(const media::AudioParameters& params); | |
| 173 | |
| 174 private: | |
| 175 bool is_authorized_; | |
| 176 int render_frame_id_; | |
| 177 bool is_creation_requested_; | |
| 178 media::AudioParameters creation_params_; | |
| 179 std::string device_unique_id_; | |
| 180 media::AudioParameters device_output_params_; | |
| 181 }; | |
| 182 | |
| 183 AudioRendererHost::AuthorizationData::AuthorizationData(int render_frame_id) | |
| 184 : is_authorized_(false), | |
| 185 render_frame_id_(render_frame_id), | |
| 186 is_creation_requested_(false) {} | |
| 187 | |
| 188 AudioRendererHost::AuthorizationData::AuthorizationData( | |
| 189 int render_frame_id, | |
| 190 const std::string& device_unique_id, | |
| 191 const media::AudioParameters& device_output_params) | |
| 192 : is_authorized_(true), | |
| 193 render_frame_id_(render_frame_id), | |
| 194 is_creation_requested_(false), | |
| 195 device_unique_id_(device_unique_id), | |
| 196 device_output_params_(device_output_params) {} | |
| 197 | |
| 198 void AudioRendererHost::AuthorizationData::Authorize( | |
| 199 const std::string& device_unique_id, | |
| 200 const media::AudioParameters& device_output_params) { | |
| 201 is_authorized_ = true; | |
| 202 device_unique_id_ = device_unique_id; | |
| 203 device_output_params_ = device_output_params; | |
| 204 } | |
| 205 | |
| 206 void AudioRendererHost::AuthorizationData::RequestCreation( | |
| 207 const media::AudioParameters& params) { | |
| 208 is_creation_requested_ = true; | |
| 209 creation_params_ = params; | |
| 210 } | |
| 211 | |
| 146 /////////////////////////////////////////////////////////////////////////////// | 212 /////////////////////////////////////////////////////////////////////////////// |
| 147 // AudioRendererHost implementations. | 213 // AudioRendererHost implementations. |
| 148 | 214 |
| 149 AudioRendererHost::AudioRendererHost( | 215 AudioRendererHost::AudioRendererHost( |
| 150 int render_process_id, | 216 int render_process_id, |
| 151 media::AudioManager* audio_manager, | 217 media::AudioManager* audio_manager, |
| 152 AudioMirroringManager* mirroring_manager, | 218 AudioMirroringManager* mirroring_manager, |
| 153 MediaInternals* media_internals, | 219 MediaInternals* media_internals, |
| 154 MediaStreamManager* media_stream_manager, | 220 MediaStreamManager* media_stream_manager, |
| 155 const ResourceContext::SaltCallback& salt_callback) | 221 const ResourceContext::SaltCallback& salt_callback) |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 177 BrowserThread::IO, FROM_HERE, | 243 BrowserThread::IO, FROM_HERE, |
| 178 base::Bind(&AudioRendererHost::DoGetOutputControllers, this), callback); | 244 base::Bind(&AudioRendererHost::DoGetOutputControllers, this), callback); |
| 179 } | 245 } |
| 180 | 246 |
| 181 void AudioRendererHost::OnChannelClosing() { | 247 void AudioRendererHost::OnChannelClosing() { |
| 182 // Since the IPC sender is gone, close all requested audio streams. | 248 // Since the IPC sender is gone, close all requested audio streams. |
| 183 while (!audio_entries_.empty()) { | 249 while (!audio_entries_.empty()) { |
| 184 // Note: OnCloseStream() removes the entries from audio_entries_. | 250 // Note: OnCloseStream() removes the entries from audio_entries_. |
| 185 OnCloseStream(audio_entries_.begin()->first); | 251 OnCloseStream(audio_entries_.begin()->first); |
| 186 } | 252 } |
| 253 | |
| 254 // Remove any authorizations for streams that were not yet created | |
| 255 authorizations_.clear(); | |
| 187 } | 256 } |
| 188 | 257 |
| 189 void AudioRendererHost::OnDestruct() const { | 258 void AudioRendererHost::OnDestruct() const { |
| 190 BrowserThread::DeleteOnIOThread::Destruct(this); | 259 BrowserThread::DeleteOnIOThread::Destruct(this); |
| 191 } | 260 } |
| 192 | 261 |
| 193 void AudioRendererHost::AudioEntry::OnCreated() { | 262 void AudioRendererHost::AudioEntry::OnCreated() { |
| 194 BrowserThread::PostTask( | 263 BrowserThread::PostTask( |
| 195 BrowserThread::IO, | 264 BrowserThread::IO, |
| 196 FROM_HERE, | 265 FROM_HERE, |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 } | 374 } |
| 306 | 375 |
| 307 return controllers; | 376 return controllers; |
| 308 } | 377 } |
| 309 | 378 |
| 310 /////////////////////////////////////////////////////////////////////////////// | 379 /////////////////////////////////////////////////////////////////////////////// |
| 311 // IPC Messages handler | 380 // IPC Messages handler |
| 312 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { | 381 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { |
| 313 bool handled = true; | 382 bool handled = true; |
| 314 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) | 383 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) |
| 384 IPC_MESSAGE_HANDLER(AudioHostMsg_RequestDeviceAuthorization, | |
| 385 OnRequestDeviceAuthorization) | |
| 315 IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream) | 386 IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream) |
| 316 IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream) | 387 IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream) |
| 317 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) | 388 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) |
| 318 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) | 389 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) |
| 319 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) | 390 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) |
| 320 IPC_MESSAGE_HANDLER(AudioHostMsg_SwitchOutputDevice, OnSwitchOutputDevice) | 391 IPC_MESSAGE_HANDLER(AudioHostMsg_SwitchOutputDevice, OnSwitchOutputDevice) |
| 321 IPC_MESSAGE_UNHANDLED(handled = false) | 392 IPC_MESSAGE_UNHANDLED(handled = false) |
| 322 IPC_END_MESSAGE_MAP() | 393 IPC_END_MESSAGE_MAP() |
| 323 | 394 |
| 324 return handled; | 395 return handled; |
| 325 } | 396 } |
| 326 | 397 |
| 398 void AudioRendererHost::OnRequestDeviceAuthorization( | |
| 399 int stream_id, | |
| 400 int render_frame_id, | |
| 401 int session_id, | |
| 402 const std::string& device_id, | |
| 403 const GURL& security_origin) { | |
| 404 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 405 DVLOG(1) << "AudioRendererHost@" << this << "::OnRequestDeviceAuthorization" | |
| 406 << "(stream_id=" << stream_id | |
| 407 << ", render_frame_id=" << render_frame_id | |
| 408 << ", session_id=" << session_id << ", device_id=" << device_id | |
| 409 << ", security_origin=" << security_origin << ")"; | |
| 410 | |
| 411 if (LookupById(stream_id) || IsAuthorizationStarted(stream_id)) | |
| 412 return; | |
| 413 | |
| 414 // If attempting to use the output device associated to an opened input | |
| 415 // device and the output device is found, reuse the input device | |
| 416 // permissions. | |
| 417 if (session_id != 0) { | |
| 418 const StreamDeviceInfo* info = | |
| 419 media_stream_manager_->audio_input_device_manager() | |
| 420 ->GetOpenedDeviceInfoById(session_id); | |
| 421 if (info) { | |
| 422 media::AudioParameters output_params( | |
| 423 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
| 424 static_cast<media::ChannelLayout>( | |
| 425 info->device.matched_output.channel_layout), | |
| 426 info->device.matched_output.sample_rate, 16, | |
| 427 info->device.matched_output.frames_per_buffer, | |
| 428 info->device.matched_output.effects); | |
| 429 authorizations_.insert(std::pair<int, AuthorizationData>( | |
| 430 stream_id, AuthorizationData(render_frame_id, | |
| 431 info->device.matched_output_device_id, | |
| 432 output_params))); | |
| 433 Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, true, output_params)); | |
| 434 return; | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 authorizations_.insert(std::pair<int, AuthorizationData>( | |
| 439 stream_id, AuthorizationData(render_frame_id))); | |
| 440 CheckOutputDeviceAccess( | |
| 441 render_frame_id, device_id, security_origin, | |
| 442 base::Bind(&AudioRendererHost::RequestDeviceAuthorizationAccessChecked, | |
| 443 this, stream_id, device_id, security_origin)); | |
| 444 } | |
| 445 | |
| 446 void AudioRendererHost::RequestDeviceAuthorizationAccessChecked( | |
| 447 int stream_id, | |
| 448 const std::string& device_id, | |
| 449 const GURL& security_origin, | |
| 450 bool have_access) { | |
| 451 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 452 const auto& auth_data = authorizations_.find(stream_id); | |
| 453 | |
| 454 // A close request was received while access check was in progress. | |
| 455 if (auth_data == authorizations_.end()) | |
| 456 return; | |
| 457 | |
| 458 if (!have_access) { | |
| 459 authorizations_.erase(auth_data); | |
| 460 Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, false, DummyParams())); | |
| 461 return; | |
| 462 } | |
| 463 | |
| 464 TranslateDeviceID( | |
| 465 device_id, security_origin, | |
| 466 base::Bind(&AudioRendererHost::RequestDeviceAuthorizationTranslated, this, | |
| 467 stream_id)); | |
| 468 } | |
| 469 | |
| 470 void AudioRendererHost::RequestDeviceAuthorizationTranslated( | |
| 471 int stream_id, | |
| 472 bool device_found, | |
| 473 const AudioOutputDeviceInfo& device_info) { | |
| 474 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 475 const auto& auth_data = authorizations_.find(stream_id); | |
| 476 | |
| 477 // A close request was received while access check was in progress | |
|
DaleCurtis
2015/09/09 01:31:57
translate was in progress?
Guido Urdaneta
2015/09/09 16:22:23
Done.
| |
| 478 if (auth_data == authorizations_.end()) | |
| 479 return; | |
| 480 | |
| 481 if (!device_found) { | |
| 482 authorizations_.erase(auth_data); | |
| 483 Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, false, DummyParams())); | |
| 484 return; | |
| 485 } | |
| 486 | |
| 487 auth_data->second.Authorize(device_info.unique_id, device_info.output_params); | |
| 488 Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, true, | |
| 489 device_info.output_params)); | |
| 490 | |
| 491 if (auth_data->second.is_creation_requested()) { | |
| 492 DoCreateStream(stream_id, auth_data->second.render_frame_id(), | |
| 493 auth_data->second.creation_params(), device_info.unique_id); | |
| 494 authorizations_.erase(auth_data); | |
| 495 } | |
| 496 } | |
| 497 | |
| 327 void AudioRendererHost::OnCreateStream(int stream_id, | 498 void AudioRendererHost::OnCreateStream(int stream_id, |
| 328 int render_frame_id, | 499 int render_frame_id, |
| 329 int session_id, | |
| 330 const media::AudioParameters& params) { | 500 const media::AudioParameters& params) { |
| 331 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 501 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 332 | 502 |
| 333 DVLOG(1) << "AudioRendererHost@" << this | 503 DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream" |
| 334 << "::OnCreateStream(stream_id=" << stream_id | 504 << "(stream_id=" << stream_id << ")"; |
| 335 << ", render_frame_id=" << render_frame_id | 505 |
| 336 << ", session_id=" << session_id << ")"; | 506 const auto& auth_data = authorizations_.find(stream_id); |
| 337 DCHECK_GT(render_frame_id, 0); | 507 |
| 508 // If no previous authorization requested, assume default device | |
| 509 if (auth_data == authorizations_.end()) { | |
| 510 DoCreateStream(stream_id, render_frame_id, params, std::string()); | |
| 511 return; | |
| 512 } | |
| 513 | |
| 514 DCHECK_EQ(render_frame_id, auth_data->second.render_frame_id()); | |
| 515 // If authorization is ongoing, defer creation. | |
|
DaleCurtis
2015/09/09 01:31:57
This ends up adding substantial complexity. Can we
Guido Urdaneta
2015/09/09 16:22:23
Done. See comment above.
| |
| 516 if (!auth_data->second.is_authorized()) { | |
| 517 auth_data->second.RequestCreation(params); | |
| 518 return; | |
| 519 } | |
| 520 | |
| 521 // If already authorized, create stream and remove authorization | |
| 522 DoCreateStream(stream_id, render_frame_id, params, | |
| 523 auth_data->second.device_unique_id()); | |
| 524 authorizations_.erase(auth_data); | |
| 525 } | |
| 526 | |
| 527 void AudioRendererHost::DoCreateStream(int stream_id, | |
| 528 int render_frame_id, | |
| 529 const media::AudioParameters& params, | |
| 530 const std::string& device_unique_id) { | |
| 531 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 338 | 532 |
| 339 // media::AudioParameters is validated in the deserializer. | 533 // media::AudioParameters is validated in the deserializer. |
| 340 if (LookupById(stream_id) != NULL) { | 534 if (LookupById(stream_id) != NULL) { |
| 341 SendErrorMessage(stream_id); | 535 SendErrorMessage(stream_id); |
| 342 return; | 536 return; |
| 343 } | 537 } |
| 344 | 538 |
| 345 // Initialize the |output_device_id| to an empty string which indicates that | |
| 346 // the default device should be used. If a StreamDeviceInfo instance was found | |
| 347 // though, then we use the matched output device. | |
| 348 std::string output_device_id; | |
| 349 const StreamDeviceInfo* info = media_stream_manager_-> | |
| 350 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); | |
| 351 if (info) | |
| 352 output_device_id = info->device.matched_output_device_id; | |
| 353 | |
| 354 // Create the shared memory and share with the renderer process. | 539 // Create the shared memory and share with the renderer process. |
| 355 uint32 shared_memory_size = AudioBus::CalculateMemorySize(params); | 540 uint32 shared_memory_size = AudioBus::CalculateMemorySize(params); |
| 356 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); | 541 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); |
| 357 if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) { | 542 if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) { |
| 358 SendErrorMessage(stream_id); | 543 SendErrorMessage(stream_id); |
| 359 return; | 544 return; |
| 360 } | 545 } |
| 361 | 546 |
| 362 scoped_ptr<AudioSyncReader> reader( | 547 scoped_ptr<AudioSyncReader> reader( |
| 363 new AudioSyncReader(shared_memory.get(), params)); | 548 new AudioSyncReader(shared_memory.get(), params)); |
| 364 if (!reader->Init()) { | 549 if (!reader->Init()) { |
| 365 SendErrorMessage(stream_id); | 550 SendErrorMessage(stream_id); |
| 366 return; | 551 return; |
| 367 } | 552 } |
| 368 | 553 |
| 369 MediaObserver* const media_observer = | 554 MediaObserver* const media_observer = |
| 370 GetContentClient()->browser()->GetMediaObserver(); | 555 GetContentClient()->browser()->GetMediaObserver(); |
| 371 if (media_observer) | 556 if (media_observer) |
| 372 media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id); | 557 media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id); |
| 373 | 558 |
| 374 scoped_ptr<AudioEntry> entry(new AudioEntry(this, | 559 scoped_ptr<AudioEntry> entry( |
| 375 stream_id, | 560 new AudioEntry(this, stream_id, render_frame_id, params, device_unique_id, |
| 376 render_frame_id, | 561 shared_memory.Pass(), reader.Pass())); |
| 377 params, | |
| 378 output_device_id, | |
| 379 shared_memory.Pass(), | |
| 380 reader.Pass())); | |
| 381 if (mirroring_manager_) { | 562 if (mirroring_manager_) { |
| 382 mirroring_manager_->AddDiverter( | 563 mirroring_manager_->AddDiverter( |
| 383 render_process_id_, entry->render_frame_id(), entry->controller()); | 564 render_process_id_, entry->render_frame_id(), entry->controller()); |
| 384 } | 565 } |
| 385 audio_entries_.insert(std::make_pair(stream_id, entry.release())); | 566 audio_entries_.insert(std::make_pair(stream_id, entry.release())); |
| 386 audio_log_->OnCreated(stream_id, params, output_device_id); | 567 |
| 568 audio_log_->OnCreated(stream_id, params, device_unique_id); | |
| 387 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry( | 569 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry( |
| 388 stream_id, render_process_id_, render_frame_id, audio_log_.get()); | 570 stream_id, render_process_id_, render_frame_id, audio_log_.get()); |
| 389 } | 571 } |
| 390 | 572 |
| 391 void AudioRendererHost::OnPlayStream(int stream_id) { | 573 void AudioRendererHost::OnPlayStream(int stream_id) { |
| 392 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 574 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 393 | 575 |
| 394 AudioEntry* entry = LookupById(stream_id); | 576 AudioEntry* entry = LookupById(stream_id); |
| 395 if (!entry) { | 577 if (!entry) { |
| 396 SendErrorMessage(stream_id); | 578 SendErrorMessage(stream_id); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 423 return; | 605 return; |
| 424 } | 606 } |
| 425 | 607 |
| 426 // Make sure the volume is valid. | 608 // Make sure the volume is valid. |
| 427 if (volume < 0 || volume > 1.0) | 609 if (volume < 0 || volume > 1.0) |
| 428 return; | 610 return; |
| 429 entry->controller()->SetVolume(volume); | 611 entry->controller()->SetVolume(volume); |
| 430 audio_log_->OnSetVolume(stream_id, volume); | 612 audio_log_->OnSetVolume(stream_id, volume); |
| 431 } | 613 } |
| 432 | 614 |
| 433 void AudioRendererHost::OnSwitchOutputDevice(int stream_id, | 615 void AudioRendererHost::OnSwitchOutputDevice(int stream_id, |
|
DaleCurtis
2015/09/09 01:31:57
As mentioned in the AOD class, after this CL lands
Guido Urdaneta
2015/09/09 16:22:23
That is the goal.
A possible road map after this
DaleCurtis
2015/09/12 01:17:19
sgtm!
| |
| 434 int render_frame_id, | 616 int render_frame_id, |
| 435 const std::string& device_id, | 617 const std::string& device_id, |
| 436 const GURL& security_origin, | 618 const GURL& security_origin) { |
| 437 int request_id) { | |
| 438 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 619 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 439 DVLOG(1) << "AudioRendererHost@" << this | 620 DVLOG(1) << "AudioRendererHost@" << this |
| 440 << "::OnSwitchOutputDevice(stream_id=" << stream_id | 621 << "::OnSwitchOutputDevice(stream_id=" << stream_id |
| 441 << ", render_frame_id=" << render_frame_id | 622 << ", render_frame_id=" << render_frame_id |
| 442 << ", device_id=" << device_id | 623 << ", device_id=" << device_id |
| 443 << ", security_origin=" << security_origin | 624 << ", security_origin=" << security_origin << ")"; |
| 444 << ", request_id=" << request_id << ")"; | 625 CheckOutputDeviceAccess( |
| 445 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( | 626 render_frame_id, device_id, security_origin, |
| 446 render_process_id_, security_origin)) { | 627 base::Bind(&AudioRendererHost::SwitchOutputDeviceAccessChecked, this, |
| 447 content::bad_message::ReceivedBadMessage(this, | 628 stream_id, device_id, security_origin)); |
| 448 bad_message::ARH_UNAUTHORIZED_URL); | 629 } |
| 630 | |
| 631 void AudioRendererHost::SwitchOutputDeviceAccessChecked( | |
| 632 int stream_id, | |
| 633 const std::string& device_id, | |
| 634 const GURL& security_origin, | |
| 635 bool have_access) { | |
| 636 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 637 if (!have_access) { | |
| 638 Send(new AudioMsg_NotifyOutputDeviceSwitched( | |
| 639 stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED, | |
| 640 DummyParams())); | |
| 449 return; | 641 return; |
| 450 } | 642 } |
| 451 | 643 |
| 452 if (device_id.empty()) { | 644 TranslateDeviceID(device_id, security_origin, |
| 453 DVLOG(1) << __FUNCTION__ << ": default output device requested. " | 645 base::Bind(&AudioRendererHost::SwitchOutputDeviceTranslated, |
| 454 << "No permissions check or device translation/validation needed."; | 646 this, stream_id)); |
| 455 DoSwitchOutputDevice(stream_id, device_id, request_id); | |
| 456 } else { | |
| 457 // Check that MediaStream device permissions have been granted, | |
| 458 // hence the use of a MediaStreamUIProxy. | |
| 459 scoped_ptr<MediaStreamUIProxy> ui_proxy = MediaStreamUIProxy::Create(); | |
| 460 | |
| 461 // Use MEDIA_DEVICE_AUDIO_CAPTURE instead of MEDIA_DEVICE_AUDIO_OUTPUT | |
| 462 // because MediaStreamUIProxy::CheckAccess does not currently support | |
| 463 // MEDIA_DEVICE_AUDIO_OUTPUT. | |
| 464 // TODO(guidou): Change to MEDIA_DEVICE_AUDIO_OUTPUT when support becomes | |
| 465 // available. http://crbug.com/498675 | |
| 466 ui_proxy->CheckAccess( | |
| 467 security_origin, MEDIA_DEVICE_AUDIO_CAPTURE, | |
| 468 render_process_id_, render_frame_id, | |
| 469 base::Bind(&AudioRendererHost::OutputDeviceAccessChecked, this, | |
| 470 base::Passed(&ui_proxy), stream_id, device_id, | |
| 471 security_origin, render_frame_id, request_id)); | |
| 472 } | |
| 473 } | 647 } |
| 474 | 648 |
| 475 void AudioRendererHost::OutputDeviceAccessChecked( | 649 void AudioRendererHost::SwitchOutputDeviceTranslated( |
| 476 scoped_ptr<MediaStreamUIProxy> ui_proxy, | |
| 477 int stream_id, | 650 int stream_id, |
| 478 const std::string& device_id, | 651 bool device_found, |
| 479 const GURL& security_origin, | 652 const AudioOutputDeviceInfo& device_info) { |
| 480 int render_frame_id, | |
| 481 int request_id, | |
| 482 bool have_access) { | |
| 483 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 653 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 484 DVLOG(1) << __FUNCTION__; | 654 if (!device_found) { |
| 485 if (!have_access) { | |
| 486 DVLOG(0) << __FUNCTION__ | |
| 487 << ": Have no access to media devices. Not switching device."; | |
| 488 Send(new AudioMsg_NotifyOutputDeviceSwitched( | 655 Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| 489 stream_id, request_id, | 656 stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND, |
| 490 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED)); | 657 DummyParams())); |
| 491 return; | 658 return; |
| 492 } | 659 } |
| 493 | 660 |
| 494 scoped_refptr<base::SingleThreadTaskRunner> audio_worker_runner = | |
| 495 AudioManager::Get()->GetWorkerTaskRunner(); | |
| 496 audio_worker_runner->PostTask( | |
| 497 FROM_HERE, | |
| 498 base::Bind(&AudioRendererHost::StartTranslateOutputDeviceName, this, | |
| 499 stream_id, device_id, security_origin, request_id)); | |
| 500 } | |
| 501 | |
| 502 void AudioRendererHost::StartTranslateOutputDeviceName( | |
| 503 int stream_id, | |
| 504 const std::string& device_id, | |
| 505 const GURL& security_origin, | |
| 506 int request_id) { | |
| 507 DCHECK(AudioManager::Get()->GetWorkerTaskRunner()->BelongsToCurrentThread()); | |
| 508 DCHECK(!device_id.empty()); | |
| 509 DVLOG(1) << __FUNCTION__; | |
| 510 | |
| 511 media::AudioDeviceNames* device_names(new media::AudioDeviceNames); | |
| 512 AudioManager::Get()->GetAudioOutputDeviceNames(device_names); | |
| 513 | |
| 514 BrowserThread::PostTask( | |
| 515 BrowserThread::IO, FROM_HERE, | |
| 516 base::Bind(&AudioRendererHost::FinishTranslateOutputDeviceName, this, | |
| 517 stream_id, device_id, security_origin, request_id, | |
| 518 base::Owned(device_names))); | |
| 519 } | |
| 520 | |
| 521 void AudioRendererHost::FinishTranslateOutputDeviceName( | |
| 522 int stream_id, | |
| 523 const std::string& device_id, | |
| 524 const GURL& security_origin, | |
| 525 int request_id, | |
| 526 media::AudioDeviceNames* device_names) { | |
| 527 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 528 DCHECK(!device_id.empty()); | |
| 529 DVLOG(1) << __FUNCTION__; | |
| 530 | |
| 531 std::string raw_device_id; | |
| 532 // Process the enumeration here because |salt_callback_| can run | |
| 533 // only on the IO thread | |
| 534 for (const auto& device_name : *device_names) { | |
| 535 const std::string candidate_device_id = content::GetHMACForMediaDeviceID( | |
| 536 salt_callback_, security_origin, device_name.unique_id); | |
| 537 if (candidate_device_id == device_id) { | |
| 538 DVLOG(1) << "Requested device " << device_name.unique_id << " - " | |
| 539 << device_name.device_name; | |
| 540 raw_device_id = device_name.unique_id; | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 if (raw_device_id.empty()) { | |
| 545 DVLOG(1) << "Requested device " << device_id << " could not be found."; | |
| 546 Send(new AudioMsg_NotifyOutputDeviceSwitched( | |
| 547 stream_id, request_id, | |
| 548 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND)); | |
| 549 return; | |
| 550 } | |
| 551 | |
| 552 DoSwitchOutputDevice(stream_id, raw_device_id, request_id); | |
| 553 } | |
| 554 | |
| 555 void AudioRendererHost::DoSwitchOutputDevice(int stream_id, | |
| 556 const std::string& raw_device_id, | |
| 557 int request_id) { | |
| 558 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 559 DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << raw_device_id << ", " | |
| 560 << request_id << ")"; | |
| 561 AudioEntry* entry = LookupById(stream_id); | 661 AudioEntry* entry = LookupById(stream_id); |
| 562 if (!entry) { | 662 if (!entry) { |
| 563 Send(new AudioMsg_NotifyOutputDeviceSwitched( | 663 Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| 564 stream_id, request_id, | 664 stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL, |
| 565 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE)); | 665 DummyParams())); |
| 566 return; | 666 return; |
| 567 } | 667 } |
| 568 | 668 |
| 569 entry->controller()->SwitchOutputDevice( | 669 entry->controller()->SwitchOutputDevice( |
| 570 raw_device_id, base::Bind(&AudioRendererHost::DoOutputDeviceSwitched, | 670 device_info.unique_id, |
| 571 this, stream_id, request_id)); | 671 base::Bind(&AudioRendererHost::OutputDeviceSwitched, this, stream_id, |
| 572 audio_log_->OnSwitchOutputDevice(entry->stream_id(), raw_device_id); | 672 device_info.output_params)); |
| 673 audio_log_->OnSwitchOutputDevice(entry->stream_id(), device_info.unique_id); | |
| 573 } | 674 } |
| 574 | 675 |
| 575 void AudioRendererHost::DoOutputDeviceSwitched(int stream_id, int request_id) { | 676 void AudioRendererHost::OutputDeviceSwitched( |
| 677 int stream_id, | |
| 678 const media::AudioParameters& output_params) { | |
| 576 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 679 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 577 DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << request_id << ")"; | |
| 578 Send(new AudioMsg_NotifyOutputDeviceSwitched( | 680 Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| 579 stream_id, request_id, media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS)); | 681 stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS, output_params)); |
| 580 } | 682 } |
| 581 | 683 |
| 582 void AudioRendererHost::SendErrorMessage(int stream_id) { | 684 void AudioRendererHost::SendErrorMessage(int stream_id) { |
| 583 Send(new AudioMsg_NotifyStreamStateChanged( | 685 Send(new AudioMsg_NotifyStreamStateChanged( |
| 584 stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR)); | 686 stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR)); |
| 585 } | 687 } |
| 586 | 688 |
| 587 void AudioRendererHost::OnCloseStream(int stream_id) { | 689 void AudioRendererHost::OnCloseStream(int stream_id) { |
| 588 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 690 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 691 authorizations_.erase(stream_id); | |
| 589 | 692 |
| 590 // Prevent oustanding callbacks from attempting to close/delete the same | 693 // Prevent oustanding callbacks from attempting to close/delete the same |
| 591 // AudioEntry twice. | 694 // AudioEntry twice. |
| 592 AudioEntryMap::iterator i = audio_entries_.find(stream_id); | 695 AudioEntryMap::iterator i = audio_entries_.find(stream_id); |
| 593 if (i == audio_entries_.end()) | 696 if (i == audio_entries_.end()) |
| 594 return; | 697 return; |
| 595 scoped_ptr<AudioEntry> entry(i->second); | 698 scoped_ptr<AudioEntry> entry(i->second); |
| 596 audio_entries_.erase(i); | 699 audio_entries_.erase(i); |
| 597 | 700 |
| 598 media::AudioOutputController* const controller = entry->controller(); | 701 media::AudioOutputController* const controller = entry->controller(); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 676 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); | 779 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); |
| 677 it != audio_entries_.end(); | 780 it != audio_entries_.end(); |
| 678 ++it) { | 781 ++it) { |
| 679 AudioEntry* entry = it->second; | 782 AudioEntry* entry = it->second; |
| 680 if (entry->render_frame_id() == render_frame_id && entry->playing()) | 783 if (entry->render_frame_id() == render_frame_id && entry->playing()) |
| 681 return true; | 784 return true; |
| 682 } | 785 } |
| 683 return false; | 786 return false; |
| 684 } | 787 } |
| 685 | 788 |
| 789 void AudioRendererHost::CheckOutputDeviceAccess( | |
| 790 int render_frame_id, | |
| 791 const std::string& device_id, | |
| 792 const GURL& security_origin, | |
| 793 const OutputDeviceAccessCB& callback) { | |
| 794 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 795 | |
| 796 // Skip origin check in requests for the default device with an empty | |
| 797 // security origin. | |
| 798 bool skip_origin_check = security_origin.is_empty() && device_id.empty(); | |
| 799 if (!skip_origin_check && | |
| 800 !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( | |
| 801 render_process_id_, security_origin)) { | |
| 802 content::bad_message::ReceivedBadMessage(this, | |
| 803 bad_message::ARH_UNAUTHORIZED_URL); | |
| 804 return; | |
| 805 } | |
| 806 | |
| 807 if (device_id.empty()) { | |
| 808 callback.Run(true); | |
| 809 } else { | |
| 810 // Check that MediaStream device permissions have been granted, | |
| 811 // hence the use of a MediaStreamUIProxy. | |
| 812 scoped_ptr<MediaStreamUIProxy> ui_proxy = MediaStreamUIProxy::Create(); | |
| 813 | |
| 814 // Use MEDIA_DEVICE_AUDIO_CAPTURE instead of MEDIA_DEVICE_AUDIO_OUTPUT | |
| 815 // because MediaStreamUIProxy::CheckAccess does not currently support | |
| 816 // MEDIA_DEVICE_AUDIO_OUTPUT. | |
| 817 // TODO(guidou): Change to MEDIA_DEVICE_AUDIO_OUTPUT when support becomes | |
| 818 // available. http://crbug.com/498675 | |
| 819 ui_proxy->CheckAccess(security_origin, MEDIA_DEVICE_AUDIO_CAPTURE, | |
| 820 render_process_id_, render_frame_id, | |
| 821 base::Bind(&AudioRendererHost::AccessChecked, this, | |
| 822 base::Passed(&ui_proxy), callback)); | |
| 823 } | |
| 824 } | |
| 825 | |
| 826 void AudioRendererHost::AccessChecked(scoped_ptr<MediaStreamUIProxy> ui_proxy, | |
| 827 const OutputDeviceAccessCB& callback, | |
| 828 bool have_access) { | |
| 829 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 830 callback.Run(have_access); | |
| 831 } | |
| 832 | |
| 833 void AudioRendererHost::TranslateDeviceID(const std::string& device_id, | |
| 834 const GURL& security_origin, | |
| 835 const OutputDeviceInfoCB& callback) { | |
| 836 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 837 AudioOutputDeviceEnumerator* enumerator = | |
| 838 media_stream_manager_->audio_output_device_enumerator(); | |
| 839 enumerator->Enumerate(base::Bind(&AudioRendererHost::FinishTranslateDeviceID, | |
| 840 this, device_id, security_origin, callback)); | |
| 841 } | |
| 842 | |
| 843 void AudioRendererHost::FinishTranslateDeviceID( | |
| 844 const std::string& device_id, | |
| 845 const GURL& security_origin, | |
| 846 const OutputDeviceInfoCB& callback, | |
| 847 const AudioOutputDeviceEnumeration& device_infos) { | |
| 848 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 849 bool use_default_device = device_id.empty(); | |
| 850 for (const AudioOutputDeviceInfo& device_info : device_infos) { | |
| 851 if (use_default_device) { | |
| 852 if (device_info.unique_id == media::AudioManagerBase::kDefaultDeviceId) { | |
| 853 callback.Run(true, device_info); | |
| 854 return; | |
| 855 } | |
| 856 } else { | |
| 857 const std::string candidate_device_id = content::GetHMACForMediaDeviceID( | |
| 858 salt_callback_, security_origin, device_info.unique_id); | |
| 859 if (candidate_device_id == device_id) { | |
| 860 callback.Run(true, device_info); | |
| 861 return; | |
| 862 } | |
| 863 } | |
| 864 } | |
| 865 DCHECK(!use_default_device); // Default device must always be found | |
| 866 callback.Run(false, {std::string(), std::string(), DummyParams()}); | |
|
DaleCurtis
2015/09/09 01:31:57
Is this initialization syntax allowed? http://chro
Guido Urdaneta
2015/09/09 16:22:23
Structs are allowed to be initialized this way, bu
| |
| 867 } | |
| 868 | |
| 869 bool AudioRendererHost::IsAuthorizationStarted(int stream_id) { | |
| 870 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 871 const auto& i = authorizations_.find(stream_id); | |
| 872 return i != authorizations_.end(); | |
| 873 } | |
| 874 | |
| 686 } // namespace content | 875 } // namespace content |
| OLD | NEW |