Chromium Code Reviews| Index: content/browser/renderer_host/media/audio_renderer_host.cc |
| diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc |
| index dddeee21626a31f30aff5b4f749c396fcd5024b8..d0903466b3574564475d61273ab8174eb793d79b 100644 |
| --- a/content/browser/renderer_host/media/audio_renderer_host.cc |
| +++ b/content/browser/renderer_host/media/audio_renderer_host.cc |
| @@ -61,6 +61,41 @@ void NotifyResourceDispatcherOfAudioStateChange(int render_process_id, |
| render_process_id, render_view_id, is_playing); |
| } |
| +media::AudioParameters DummyParams() { |
| + return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LINEAR, |
| + media::CHANNEL_LAYOUT_STEREO, |
| + media::limits::kMinSampleRate, 1, 1); |
| +} |
| + |
| +std::pair<int, std::pair<bool, std::string>> MakeAuthorizationData( |
| + int stream_id, |
| + bool authorized, |
| + const std::string& device_unique_id) { |
| + return std::make_pair(stream_id, |
| + std::make_pair(authorized, device_unique_id)); |
| +} |
| + |
| +GURL ConvertToGURL(const url::Origin& origin) { |
| + return origin.unique() ? GURL() : GURL(origin.Serialize()); |
| +} |
| + |
| +bool IsValidDeviceId(const std::string& device_id) { |
| + static const std::string::size_type kValidLength = 64; |
| + |
| + if (device_id.empty()) |
|
palmer
2015/09/18 19:24:24
Why are empty device IDs valid? What are the seman
Guido Urdaneta
2015/09/18 20:17:56
The empty device ID represents the default output
|
| + return true; |
| + |
| + if (device_id.length() != kValidLength) |
| + return false; |
| + |
| + for (const char& c : device_id) { |
| + if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| } // namespace |
| class AudioRendererHost::AudioEntry |
| @@ -184,6 +219,9 @@ void AudioRendererHost::OnChannelClosing() { |
| // Note: OnCloseStream() removes the entries from audio_entries_. |
| OnCloseStream(audio_entries_.begin()->first); |
| } |
| + |
| + // Remove any authorizations for streams that were not yet created |
| + authorizations_.clear(); |
| } |
| void AudioRendererHost::OnDestruct() const { |
| @@ -312,6 +350,8 @@ AudioRendererHost::DoGetOutputControllers() const { |
| bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) |
| + IPC_MESSAGE_HANDLER(AudioHostMsg_RequestDeviceAuthorization, |
| + OnRequestDeviceAuthorization) |
| IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream) |
| IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream) |
| IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) |
| @@ -324,17 +364,128 @@ bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { |
| return handled; |
| } |
| +void AudioRendererHost::OnRequestDeviceAuthorization( |
| + int stream_id, |
| + int render_frame_id, |
| + int session_id, |
| + const std::string& device_id, |
| + const url::Origin& security_origin) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DVLOG(1) << "AudioRendererHost@" << this << "::OnRequestDeviceAuthorization" |
| + << "(stream_id=" << stream_id |
| + << ", render_frame_id=" << render_frame_id |
| + << ", session_id=" << session_id << ", device_id=" << device_id |
| + << ", security_origin=" << security_origin << ")"; |
| + |
| + if (LookupById(stream_id) || IsAuthorizationStarted(stream_id)) |
| + return; |
| + |
| + if (!IsValidDeviceId(device_id)) { |
| + Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, false, DummyParams())); |
| + return; |
| + } |
| + |
| + // If attempting to use the output device associated to an opened input |
| + // device and the output device is found, reuse the input device |
| + // permissions. |
| + if (session_id != 0) { |
| + const StreamDeviceInfo* info = |
| + media_stream_manager_->audio_input_device_manager() |
| + ->GetOpenedDeviceInfoById(session_id); |
| + if (info) { |
| + media::AudioParameters output_params( |
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| + static_cast<media::ChannelLayout>( |
| + info->device.matched_output.channel_layout), |
| + info->device.matched_output.sample_rate, 16, |
| + info->device.matched_output.frames_per_buffer); |
| + output_params.set_effects(info->device.matched_output.effects); |
| + authorizations_.insert(MakeAuthorizationData( |
| + stream_id, true, info->device.matched_output_device_id)); |
| + Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, true, output_params)); |
| + return; |
| + } |
| + } |
| + |
| + authorizations_.insert( |
| + MakeAuthorizationData(stream_id, false, std::string())); |
| + GURL gurl_security_origin = ConvertToGURL(security_origin); |
| + CheckOutputDeviceAccess( |
| + render_frame_id, device_id, gurl_security_origin, |
| + base::Bind(&AudioRendererHost::OnDeviceAuthorized, this, stream_id, |
| + device_id, gurl_security_origin)); |
| +} |
| + |
| +void AudioRendererHost::OnDeviceAuthorized(int stream_id, |
| + const std::string& device_id, |
| + const GURL& gurl_security_origin, |
| + bool have_access) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + const auto& auth_data = authorizations_.find(stream_id); |
| + |
| + // A close request was received while access check was in progress. |
| + if (auth_data == authorizations_.end()) |
| + return; |
| + |
| + if (!have_access) { |
| + authorizations_.erase(auth_data); |
| + Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, false, DummyParams())); |
| + return; |
| + } |
| + |
| + TranslateDeviceID( |
| + device_id, gurl_security_origin, |
| + base::Bind(&AudioRendererHost::OnDeviceIDTranslated, this, stream_id)); |
| +} |
| + |
| +void AudioRendererHost::OnDeviceIDTranslated( |
| + int stream_id, |
| + bool device_found, |
| + const AudioOutputDeviceInfo& device_info) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + const auto& auth_data = authorizations_.find(stream_id); |
| + |
| + // A close request was received while translation was in progress |
| + if (auth_data == authorizations_.end()) |
| + return; |
| + |
| + if (!device_found) { |
| + authorizations_.erase(auth_data); |
| + Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, false, DummyParams())); |
| + return; |
| + } |
| + |
| + auth_data->second.first = true; |
| + auth_data->second.second = device_info.unique_id; |
| + Send(new AudioMsg_NotifyDeviceAuthorized(stream_id, true, |
| + device_info.output_params)); |
| +} |
| + |
| void AudioRendererHost::OnCreateStream(int stream_id, |
| int render_frame_id, |
| - int session_id, |
| const media::AudioParameters& params) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream" |
| + << "(stream_id=" << stream_id << ")"; |
| - DVLOG(1) << "AudioRendererHost@" << this |
| - << "::OnCreateStream(stream_id=" << stream_id |
| - << ", render_frame_id=" << render_frame_id |
| - << ", session_id=" << session_id << ")"; |
| - DCHECK_GT(render_frame_id, 0); |
| + const auto& auth_data = authorizations_.find(stream_id); |
| + |
| + // If no previous authorization requested, assume default device |
| + if (auth_data == authorizations_.end()) { |
| + DoCreateStream(stream_id, render_frame_id, params, std::string()); |
| + return; |
| + } |
| + |
| + CHECK(auth_data->second.first); |
| + DoCreateStream(stream_id, render_frame_id, params, auth_data->second.second); |
| + authorizations_.erase(auth_data); |
| +} |
| + |
| +void AudioRendererHost::DoCreateStream(int stream_id, |
| + int render_frame_id, |
| + const media::AudioParameters& params, |
| + const std::string& device_unique_id) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| // media::AudioParameters is validated in the deserializer. |
| if (LookupById(stream_id) != NULL) { |
| @@ -342,15 +493,6 @@ void AudioRendererHost::OnCreateStream(int stream_id, |
| return; |
| } |
| - // Initialize the |output_device_id| to an empty string which indicates that |
| - // the default device should be used. If a StreamDeviceInfo instance was found |
| - // though, then we use the matched output device. |
| - std::string output_device_id; |
| - const StreamDeviceInfo* info = media_stream_manager_-> |
| - audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); |
| - if (info) |
| - output_device_id = info->device.matched_output_device_id; |
| - |
| // Create the shared memory and share with the renderer process. |
| uint32 shared_memory_size = AudioBus::CalculateMemorySize(params); |
| scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); |
| @@ -371,19 +513,16 @@ void AudioRendererHost::OnCreateStream(int stream_id, |
| if (media_observer) |
| media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id); |
| - scoped_ptr<AudioEntry> entry(new AudioEntry(this, |
| - stream_id, |
| - render_frame_id, |
| - params, |
| - output_device_id, |
| - shared_memory.Pass(), |
| - reader.Pass())); |
| + scoped_ptr<AudioEntry> entry( |
| + new AudioEntry(this, stream_id, render_frame_id, params, device_unique_id, |
| + shared_memory.Pass(), reader.Pass())); |
| if (mirroring_manager_) { |
| mirroring_manager_->AddDiverter( |
| render_process_id_, entry->render_frame_id(), entry->controller()); |
| } |
| audio_entries_.insert(std::make_pair(stream_id, entry.release())); |
| - audio_log_->OnCreated(stream_id, params, output_device_id); |
| + |
| + audio_log_->OnCreated(stream_id, params, device_unique_id); |
| MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry( |
| stream_id, render_process_id_, render_frame_id, audio_log_.get()); |
| } |
| @@ -430,153 +569,77 @@ void AudioRendererHost::OnSetVolume(int stream_id, double volume) { |
| audio_log_->OnSetVolume(stream_id, volume); |
| } |
| -void AudioRendererHost::OnSwitchOutputDevice(int stream_id, |
| - int render_frame_id, |
| - const std::string& device_id, |
| - const GURL& security_origin, |
| - int request_id) { |
| +void AudioRendererHost::OnSwitchOutputDevice( |
| + int stream_id, |
| + int render_frame_id, |
| + const std::string& device_id, |
| + const url::Origin& security_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DVLOG(1) << "AudioRendererHost@" << this |
| << "::OnSwitchOutputDevice(stream_id=" << stream_id |
| << ", render_frame_id=" << render_frame_id |
| << ", device_id=" << device_id |
| - << ", security_origin=" << security_origin |
| - << ", request_id=" << request_id << ")"; |
| - if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( |
| - render_process_id_, security_origin)) { |
| - content::bad_message::ReceivedBadMessage(this, |
| - bad_message::ARH_UNAUTHORIZED_URL); |
| + << ", security_origin=" << security_origin << ")"; |
| + if (!LookupById(stream_id) || !IsValidDeviceId(device_id)) { |
| + Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| + stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL)); |
| return; |
| } |
| - |
| - if (device_id.empty()) { |
| - DVLOG(1) << __FUNCTION__ << ": default output device requested. " |
| - << "No permissions check or device translation/validation needed."; |
| - DoSwitchOutputDevice(stream_id, device_id, request_id); |
| - } else { |
| - // Check that MediaStream device permissions have been granted, |
| - // hence the use of a MediaStreamUIProxy. |
| - scoped_ptr<MediaStreamUIProxy> ui_proxy = MediaStreamUIProxy::Create(); |
| - |
| - // Use MEDIA_DEVICE_AUDIO_CAPTURE instead of MEDIA_DEVICE_AUDIO_OUTPUT |
| - // because MediaStreamUIProxy::CheckAccess does not currently support |
| - // MEDIA_DEVICE_AUDIO_OUTPUT. |
| - // TODO(guidou): Change to MEDIA_DEVICE_AUDIO_OUTPUT when support becomes |
| - // available. http://crbug.com/498675 |
| - ui_proxy->CheckAccess( |
| - security_origin, MEDIA_DEVICE_AUDIO_CAPTURE, |
| - render_process_id_, render_frame_id, |
| - base::Bind(&AudioRendererHost::OutputDeviceAccessChecked, this, |
| - base::Passed(&ui_proxy), stream_id, device_id, |
| - security_origin, render_frame_id, request_id)); |
| - } |
| + GURL gurl_security_origin = ConvertToGURL(security_origin); |
| + CheckOutputDeviceAccess( |
| + render_frame_id, device_id, gurl_security_origin, |
| + base::Bind(&AudioRendererHost::OnSwitchDeviceAuthorized, this, stream_id, |
| + device_id, gurl_security_origin)); |
| } |
| -void AudioRendererHost::OutputDeviceAccessChecked( |
| - scoped_ptr<MediaStreamUIProxy> ui_proxy, |
| +void AudioRendererHost::OnSwitchDeviceAuthorized( |
| int stream_id, |
| const std::string& device_id, |
| - const GURL& security_origin, |
| - int render_frame_id, |
| - int request_id, |
| + const GURL& gurl_security_origin, |
| bool have_access) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - DVLOG(1) << __FUNCTION__; |
| if (!have_access) { |
| - DVLOG(0) << __FUNCTION__ |
| - << ": Have no access to media devices. Not switching device."; |
| Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| - stream_id, request_id, |
| - media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED)); |
| + stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED)); |
| return; |
| } |
| - scoped_refptr<base::SingleThreadTaskRunner> audio_worker_runner = |
| - AudioManager::Get()->GetWorkerTaskRunner(); |
| - audio_worker_runner->PostTask( |
| - FROM_HERE, |
| - base::Bind(&AudioRendererHost::StartTranslateOutputDeviceName, this, |
| - stream_id, device_id, security_origin, request_id)); |
| + // TODO(guidou): Ensure that output parameters of the new device match the |
| + // output parameters of the current device to avoid shared-memory sizing |
| + // issues. http://crbug.com/526657 |
| + TranslateDeviceID(device_id, gurl_security_origin, |
| + base::Bind(&AudioRendererHost::OnSwitchDeviceIDTranslated, |
| + this, stream_id)); |
| } |
| -void AudioRendererHost::StartTranslateOutputDeviceName( |
| +void AudioRendererHost::OnSwitchDeviceIDTranslated( |
| int stream_id, |
| - const std::string& device_id, |
| - const GURL& security_origin, |
| - int request_id) { |
| - DCHECK(AudioManager::Get()->GetWorkerTaskRunner()->BelongsToCurrentThread()); |
| - DCHECK(!device_id.empty()); |
| - DVLOG(1) << __FUNCTION__; |
| - |
| - media::AudioDeviceNames* device_names(new media::AudioDeviceNames); |
| - AudioManager::Get()->GetAudioOutputDeviceNames(device_names); |
| - |
| - BrowserThread::PostTask( |
| - BrowserThread::IO, FROM_HERE, |
| - base::Bind(&AudioRendererHost::FinishTranslateOutputDeviceName, this, |
| - stream_id, device_id, security_origin, request_id, |
| - base::Owned(device_names))); |
| -} |
| - |
| -void AudioRendererHost::FinishTranslateOutputDeviceName( |
| - int stream_id, |
| - const std::string& device_id, |
| - const GURL& security_origin, |
| - int request_id, |
| - media::AudioDeviceNames* device_names) { |
| + bool device_found, |
| + const AudioOutputDeviceInfo& device_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - DCHECK(!device_id.empty()); |
| - DVLOG(1) << __FUNCTION__; |
| - |
| - std::string raw_device_id; |
| - // Process the enumeration here because |salt_callback_| can run |
| - // only on the IO thread |
| - for (const auto& device_name : *device_names) { |
| - const std::string candidate_device_id = content::GetHMACForMediaDeviceID( |
| - salt_callback_, security_origin, device_name.unique_id); |
| - if (candidate_device_id == device_id) { |
| - DVLOG(1) << "Requested device " << device_name.unique_id << " - " |
| - << device_name.device_name; |
| - raw_device_id = device_name.unique_id; |
| - } |
| - } |
| - |
| - if (raw_device_id.empty()) { |
| - DVLOG(1) << "Requested device " << device_id << " could not be found."; |
| + if (!device_found) { |
| Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| - stream_id, request_id, |
| - media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND)); |
| + stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND)); |
| return; |
| } |
| - DoSwitchOutputDevice(stream_id, raw_device_id, request_id); |
| -} |
| - |
| -void AudioRendererHost::DoSwitchOutputDevice(int stream_id, |
| - const std::string& raw_device_id, |
| - int request_id) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << raw_device_id << ", " |
| - << request_id << ")"; |
| AudioEntry* entry = LookupById(stream_id); |
| if (!entry) { |
| Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| - stream_id, request_id, |
| - media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE)); |
| + stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL)); |
| return; |
| } |
| entry->controller()->SwitchOutputDevice( |
| - raw_device_id, base::Bind(&AudioRendererHost::DoOutputDeviceSwitched, |
| - this, stream_id, request_id)); |
| - audio_log_->OnSwitchOutputDevice(entry->stream_id(), raw_device_id); |
| + device_info.unique_id, |
| + base::Bind(&AudioRendererHost::OnDeviceSwitched, this, stream_id)); |
| + audio_log_->OnSwitchOutputDevice(entry->stream_id(), device_info.unique_id); |
| } |
| -void AudioRendererHost::DoOutputDeviceSwitched(int stream_id, int request_id) { |
| +void AudioRendererHost::OnDeviceSwitched(int stream_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - DVLOG(1) << __FUNCTION__ << "(" << stream_id << ", " << request_id << ")"; |
| Send(new AudioMsg_NotifyOutputDeviceSwitched( |
| - stream_id, request_id, media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS)); |
| + stream_id, media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS)); |
| } |
| void AudioRendererHost::SendErrorMessage(int stream_id) { |
| @@ -586,6 +649,7 @@ void AudioRendererHost::SendErrorMessage(int stream_id) { |
| void AudioRendererHost::OnCloseStream(int stream_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + authorizations_.erase(stream_id); |
| // Prevent oustanding callbacks from attempting to close/delete the same |
| // AudioEntry twice. |
| @@ -683,4 +747,91 @@ bool AudioRendererHost::RenderFrameHasActiveAudio(int render_frame_id) const { |
| return false; |
| } |
| +void AudioRendererHost::CheckOutputDeviceAccess( |
| + int render_frame_id, |
| + const std::string& device_id, |
| + const GURL& gurl_security_origin, |
| + const OutputDeviceAccessCB& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + |
| + // Skip origin check in requests for the default device with an empty |
| + // security origin. |
| + bool skip_origin_check = gurl_security_origin.is_empty() && device_id.empty(); |
| + if (!skip_origin_check && |
| + !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( |
| + render_process_id_, gurl_security_origin)) { |
| + content::bad_message::ReceivedBadMessage(this, |
| + bad_message::ARH_UNAUTHORIZED_URL); |
| + return; |
| + } |
| + |
| + if (device_id.empty()) { |
| + callback.Run(true); |
| + } else { |
| + // Check that MediaStream device permissions have been granted, |
| + // hence the use of a MediaStreamUIProxy. |
| + scoped_ptr<MediaStreamUIProxy> ui_proxy = MediaStreamUIProxy::Create(); |
| + |
| + // Use MEDIA_DEVICE_AUDIO_CAPTURE instead of MEDIA_DEVICE_AUDIO_OUTPUT |
| + // because MediaStreamUIProxy::CheckAccess does not currently support |
| + // MEDIA_DEVICE_AUDIO_OUTPUT. |
| + // TODO(guidou): Change to MEDIA_DEVICE_AUDIO_OUTPUT when support becomes |
| + // available. http://crbug.com/498675 |
| + ui_proxy->CheckAccess(gurl_security_origin, MEDIA_DEVICE_AUDIO_CAPTURE, |
| + render_process_id_, render_frame_id, |
| + base::Bind(&AudioRendererHost::AccessChecked, this, |
| + base::Passed(&ui_proxy), callback)); |
| + } |
| +} |
| + |
| +void AudioRendererHost::AccessChecked(scoped_ptr<MediaStreamUIProxy> ui_proxy, |
| + const OutputDeviceAccessCB& callback, |
| + bool have_access) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + callback.Run(have_access); |
| +} |
| + |
| +void AudioRendererHost::TranslateDeviceID(const std::string& device_id, |
| + const GURL& gurl_security_origin, |
| + const OutputDeviceInfoCB& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + AudioOutputDeviceEnumerator* enumerator = |
| + media_stream_manager_->audio_output_device_enumerator(); |
| + enumerator->Enumerate(base::Bind(&AudioRendererHost::FinishTranslateDeviceID, |
| + this, device_id, gurl_security_origin, |
| + callback)); |
| +} |
| + |
| +void AudioRendererHost::FinishTranslateDeviceID( |
| + const std::string& device_id, |
| + const GURL& security_origin, |
| + const OutputDeviceInfoCB& callback, |
| + const AudioOutputDeviceEnumeration& device_infos) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + bool use_default_device = device_id.empty() && security_origin.is_empty(); |
| + for (const AudioOutputDeviceInfo& device_info : device_infos) { |
| + if (use_default_device) { |
| + if (device_info.unique_id == media::AudioManagerBase::kDefaultDeviceId) { |
| + callback.Run(true, device_info); |
| + return; |
| + } |
| + } else if (content::DoesMediaDeviceIDMatchHMAC(salt_callback_, |
| + security_origin, device_id, |
| + device_info.unique_id)) { |
| + callback.Run(true, device_info); |
| + return; |
| + } |
| + } |
| + DCHECK(!use_default_device); // Default device must always be found |
| + AudioOutputDeviceInfo device_info = {std::string(), std::string(), |
| + DummyParams()}; |
| + callback.Run(false, device_info); |
| +} |
| + |
| +bool AudioRendererHost::IsAuthorizationStarted(int stream_id) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + const auto& i = authorizations_.find(stream_id); |
| + return i != authorizations_.end(); |
| +} |
| + |
| } // namespace content |