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

Unified Diff: content/browser/renderer_host/media/audio_renderer_host.cc

Issue 1323403005: Allow AudioOutputDevice objects to be initialized with a specific hardware output device and store … (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Palmer's comments Created 5 years, 3 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 side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698