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

Unified Diff: content/renderer/media/user_media_client_impl.cc

Issue 2538033003: Implement GetUserMedia device ID constraint processing in the renderer. (Closed)
Patch Set: hta's comments Created 4 years 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/renderer/media/user_media_client_impl.cc
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index 766ca8c73cdde201c5d439a94c7232b1c2f7721a..2381c460580c126d7733acfb87bcaf6e1a154aa6 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -6,12 +6,13 @@
#include <stddef.h>
+#include <algorithm>
#include <utility>
-#include <vector>
#include "base/hash.h"
#include "base/location.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -36,79 +37,142 @@
#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
#include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
+#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
namespace content {
namespace {
-void CopyVector(const blink::WebVector<blink::WebString>& source,
- std::vector<std::string>* destination) {
- for (const auto& web_string : source) {
- destination->push_back(web_string.utf8());
- }
+void CopyFirstString(const blink::StringConstraint& constraint,
+ std::string* destination) {
+ if (!constraint.exact().isEmpty())
+ *destination = constraint.exact()[0].utf8();
}
-void CopyFirstString(const blink::WebVector<blink::WebString>& source,
- std::string* destination) {
- if (!source.isEmpty())
- *destination = source[0].utf8();
+bool FindDeviceId(const blink::WebVector<blink::WebString> candidates,
+ const MediaDeviceInfoArray& device_infos,
+ std::string* device_id) {
+ for (const auto& candidate : candidates) {
+ auto it = std::find_if(device_infos.begin(), device_infos.end(),
+ [&candidate](const MediaDeviceInfo& info) {
+ return info.device_id == candidate.utf8();
+ });
+
+ if (it != device_infos.end()) {
+ *device_id = it->device_id;
+ return true;
+ }
+ }
+
+ return false;
}
-void CopyBlinkRequestToStreamControls(const blink::WebUserMediaRequest& request,
- StreamControls* controls) {
- if (request.isNull()) {
- return;
+// If a device ID requested as exact basic constraint in |constraints| is found
+// in |device_infos|, it is copied to |*device_id|, and the function returns
+// false. If such a device is not found in |device_infos|, the function returns
+// false and |*device_id| is left unmodified.
+// If more than one device ID is requested as an exact basic constraint in
+// |constraint|, the function returns false and |*device_id| is left unmodified.
+// If no device ID is requested as an exact basic constraint, and at least one
+// device ID requested as an ideal basic constraint or as an exact or ideal
+// advanced constraint in |constraints| is found in |device_infos|, the first
+// such device ID is copied to |*device_id| and the function returns true.
+// If no such device ID is found, |*device_id| is left unmodified and the
+// function returns true.
+// TODO(guidou): Replace with a spec-compliant selection algorithm. See
+// http://crbug.com/657733.
+bool PickDeviceId(const blink::WebMediaConstraints& constraints,
+ const MediaDeviceInfoArray& device_infos,
+ std::string* device_id) {
+ DCHECK(!constraints.isNull());
+ DCHECK(device_id->empty());
+
+ if (constraints.basic().deviceId.exact().size() > 1) {
+ LOG(ERROR) << "Only one required device ID is supported";
+ return false;
}
- if (!request.audioConstraints().isNull()) {
- const blink::WebMediaTrackConstraintSet& audio_basic =
- request.audioConstraints().basic();
- CopyFirstString(audio_basic.mediaStreamSource.exact(),
- &controls->audio.stream_source);
- CopyVector(audio_basic.deviceId.exact(), &(controls->audio.device_ids));
- // Optionals. They may be either in ideal or in advanced.exact.
- CopyVector(audio_basic.deviceId.ideal(),
- &controls->audio.alternate_device_ids);
- for (const auto& constraint : request.audioConstraints().advanced()) {
- CopyVector(constraint.deviceId.exact(),
- &controls->audio.alternate_device_ids);
- CopyVector(constraint.deviceId.ideal(),
- &controls->audio.alternate_device_ids);
+
+ if (constraints.basic().deviceId.exact().size() == 1 &&
+ !FindDeviceId(constraints.basic().deviceId.exact(), device_infos,
+ device_id)) {
+ LOG(ERROR) << "Invalid mandatory device ID = "
+ << constraints.basic().deviceId.exact()[0].utf8();
+ return false;
+ }
+
+ // There is no required device ID. Look at the alternates.
+ if (FindDeviceId(constraints.basic().deviceId.ideal(), device_infos,
+ device_id)) {
+ return true;
+ }
+
+ for (const auto& advanced : constraints.advanced()) {
+ if (FindDeviceId(advanced.deviceId.exact(), device_infos, device_id)) {
+ return true;
}
- if (audio_basic.hotwordEnabled.hasExact()) {
- controls->hotword_enabled = audio_basic.hotwordEnabled.exact();
- } else {
- for (const auto& audio_advanced : request.audioConstraints().advanced()) {
- if (audio_advanced.hotwordEnabled.hasExact()) {
- controls->hotword_enabled = audio_advanced.hotwordEnabled.exact();
- break;
- }
- }
+ if (FindDeviceId(advanced.deviceId.ideal(), device_infos, device_id)) {
+ return true;
}
+ }
- if (request.audioConstraints().basic().disableLocalEcho.hasExact()) {
- controls->disable_local_echo =
- request.audioConstraints().basic().disableLocalEcho.exact();
- } else {
- controls->disable_local_echo =
- controls->audio.stream_source != kMediaStreamSourceDesktop;
+ // No valid alternate device ID found. Select default device.
+ return true;
+}
+
+bool IsDeviceSource(const std::string& source) {
+ return source.empty();
+}
+
+void CopyConstraintsToTrackControls(
+ const blink::WebMediaConstraints& constraints,
+ TrackControls* track_controls,
+ bool* request_devices) {
+ DCHECK(!constraints.isNull());
+ track_controls->requested = true;
+ CopyFirstString(constraints.basic().mediaStreamSource,
+ &track_controls->stream_source);
+ if (IsDeviceSource(track_controls->stream_source)) {
+ bool request_devices_advanced = false;
+ for (const auto& advanced : constraints.advanced()) {
+ if (!advanced.deviceId.isEmpty()) {
+ request_devices_advanced = true;
+ break;
+ }
}
+ *request_devices =
+ request_devices_advanced || !constraints.basic().deviceId.isEmpty();
+ } else {
+ CopyFirstString(constraints.basic().deviceId, &track_controls->device_id);
+ *request_devices = false;
}
- if (!request.videoConstraints().isNull()) {
- const blink::WebMediaTrackConstraintSet& video_basic =
- request.videoConstraints().basic();
- CopyFirstString(video_basic.mediaStreamSource.exact(),
- &(controls->video.stream_source));
- CopyVector(video_basic.deviceId.exact(), &(controls->video.device_ids));
- CopyVector(video_basic.deviceId.ideal(),
- &(controls->video.alternate_device_ids));
- for (const auto& constraint : request.videoConstraints().advanced()) {
- CopyVector(constraint.deviceId.exact(),
- &controls->video.alternate_device_ids);
- CopyVector(constraint.deviceId.ideal(),
- &controls->video.alternate_device_ids);
+}
+
+void CopyHotwordAndLocalEchoToStreamControls(
+ const blink::WebMediaConstraints& audio_constraints,
+ StreamControls* controls) {
+ if (audio_constraints.isNull())
+ return;
+
+ if (audio_constraints.basic().hotwordEnabled.hasExact()) {
+ controls->hotword_enabled =
+ audio_constraints.basic().hotwordEnabled.exact();
+ } else {
+ for (const auto& audio_advanced : audio_constraints.advanced()) {
+ if (audio_advanced.hotwordEnabled.hasExact()) {
+ controls->hotword_enabled = audio_advanced.hotwordEnabled.exact();
+ break;
+ }
}
}
+
+ if (audio_constraints.basic().disableLocalEcho.hasExact()) {
+ controls->disable_local_echo =
+ audio_constraints.basic().disableLocalEcho.exact();
+ } else {
+ controls->disable_local_echo =
+ controls->audio.stream_source != kMediaStreamSourceDesktop;
+ }
}
bool IsSameDevice(const StreamDeviceInfo& device,
@@ -183,19 +247,34 @@ void UserMediaClientImpl::requestUserMedia(
}
int request_id = g_next_request_id++;
- StreamControls controls;
- url::Origin security_origin;
- bool enable_automatic_output_device_selection = false;
+ std::unique_ptr<StreamControls> controls = base::MakeUnique<StreamControls>();
// |user_media_request| can't be mocked. So in order to test at all we check
// if it isNull.
+ // TODO(guidou): Remove this test-specific code path.
if (user_media_request.isNull()) {
// We are in a test.
- controls.audio.requested = true;
- controls.video.requested = true;
+ controls->audio.requested = true;
+ controls->video.requested = true;
+ FinalizeRequestUserMedia(
+ request_id, user_media_request, std::move(controls),
+ false /* automatic_output_device_selection */, url::Origin());
} else {
+ // ownerDocument may be null if we are in a test.
+ // In that case, it's OK to not check frame().
+ DCHECK(user_media_request.ownerDocument().isNull() ||
+ render_frame()->GetWebFrame() ==
+ static_cast<blink::WebFrame*>(
+ user_media_request.ownerDocument().frame()));
+
+ bool enable_automatic_output_device_selection = false;
+ bool request_audio_input_devices = false;
if (user_media_request.audio()) {
- controls.audio.requested = true;
+ CopyConstraintsToTrackControls(user_media_request.audioConstraints(),
+ &controls->audio,
+ &request_audio_input_devices);
+ CopyHotwordAndLocalEchoToStreamControls(
+ user_media_request.audioConstraints(), controls.get());
// Check if this input device should be used to select a matching output
// device for audio rendering.
GetConstraintValueAsBoolean(
@@ -203,52 +282,88 @@ void UserMediaClientImpl::requestUserMedia(
&blink::WebMediaTrackConstraintSet::renderToAssociatedSink,
&enable_automatic_output_device_selection);
}
+ bool request_video_input_devices = false;
if (user_media_request.video()) {
- controls.video.requested = true;
+ CopyConstraintsToTrackControls(user_media_request.videoConstraints(),
+ &controls->video,
+ &request_video_input_devices);
+ }
+
+ url::Origin security_origin = user_media_request.getSecurityOrigin();
+ if (request_audio_input_devices || request_video_input_devices) {
+ GetMediaDevicesDispatcher()->EnumerateDevices(
+ request_audio_input_devices, request_video_input_devices,
+ false /* request_audio_output_devices */, security_origin,
+ base::Bind(&UserMediaClientImpl::SelectUserMediaDevice,
+ weak_factory_.GetWeakPtr(), request_id, user_media_request,
+ base::Passed(&controls),
+ enable_automatic_output_device_selection,
+ security_origin));
+ } else {
+ FinalizeRequestUserMedia(
+ request_id, user_media_request, std::move(controls),
+ enable_automatic_output_device_selection, security_origin);
}
- CopyBlinkRequestToStreamControls(user_media_request, &controls);
- security_origin = user_media_request.getSecurityOrigin();
- // ownerDocument may be null if we are in a test.
- // In that case, it's OK to not check frame().
- DCHECK(user_media_request.ownerDocument().isNull() ||
- render_frame()->GetWebFrame() ==
- static_cast<blink::WebFrame*>(
- user_media_request.ownerDocument().frame()));
}
+}
- DVLOG(1) << "UserMediaClientImpl::requestUserMedia(" << request_id << ", [ "
- << "audio=" << (controls.audio.requested)
- << " select associated sink: "
- << enable_automatic_output_device_selection
- << ", video=" << (controls.video.requested) << " ], "
- << security_origin << ")";
+void UserMediaClientImpl::SelectUserMediaDevice(
+ int request_id,
+ const blink::WebUserMediaRequest& user_media_request,
+ std::unique_ptr<StreamControls> controls,
+ bool enable_automatic_output_device_selection,
+ const url::Origin& security_origin,
+ const EnumerationResult& device_enumeration) {
+ DCHECK(CalledOnValidThread());
- std::string audio_device_id;
- if (!user_media_request.isNull() && user_media_request.audio()) {
- GetConstraintValueAsString(user_media_request.audioConstraints(),
- &blink::WebMediaTrackConstraintSet::deviceId,
- &audio_device_id);
+ if (controls->audio.requested &&
+ IsDeviceSource(controls->audio.stream_source)) {
+ if (!PickDeviceId(user_media_request.audioConstraints(),
+ device_enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT],
+ &controls->audio.device_id)) {
+ GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE,
+ "");
+ return;
+ }
}
- std::string video_device_id;
- if (!user_media_request.isNull() && user_media_request.video()) {
- GetConstraintValueAsString(user_media_request.videoConstraints(),
- &blink::WebMediaTrackConstraintSet::deviceId,
- &video_device_id);
+ if (controls->video.requested &&
+ IsDeviceSource(controls->video.stream_source)) {
+ if (!PickDeviceId(user_media_request.videoConstraints(),
+ device_enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT],
+ &controls->video.device_id)) {
+ GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE,
+ "");
+ return;
+ }
}
- WebRtcLogMessage(base::StringPrintf(
- "MSI::requestUserMedia. request_id=%d"
- ", audio source id=%s"
- ", video source id=%s",
- request_id, audio_device_id.c_str(), video_device_id.c_str()));
+ FinalizeRequestUserMedia(request_id, user_media_request, std::move(controls),
+ enable_automatic_output_device_selection,
+ security_origin);
+}
+
+void UserMediaClientImpl::FinalizeRequestUserMedia(
+ int request_id,
+ const blink::WebUserMediaRequest& user_media_request,
+ std::unique_ptr<StreamControls> controls,
+ bool enable_automatic_output_device_selection,
+ const url::Origin& security_origin) {
+ DCHECK(CalledOnValidThread());
+
+ WebRtcLogMessage(
+ base::StringPrintf("MSI::requestUserMedia. request_id=%d"
+ ", audio source id=%s"
+ ", video source id=%s",
+ request_id, controls->audio.device_id.c_str(),
+ controls->video.device_id.c_str()));
user_media_requests_.push_back(
new UserMediaRequestInfo(request_id, user_media_request,
enable_automatic_output_device_selection));
media_stream_dispatcher_->GenerateStream(
- request_id, weak_factory_.GetWeakPtr(), controls, security_origin);
+ request_id, weak_factory_.GetWeakPtr(), *controls, security_origin);
}
void UserMediaClientImpl::cancelUserMediaRequest(
« no previous file with comments | « content/renderer/media/user_media_client_impl.h ('k') | content/renderer/media/user_media_client_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698