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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/user_media_client_impl.h" 5 #include "content/renderer/media/user_media_client_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm>
9 #include <utility> 10 #include <utility>
10 #include <vector>
11 11
12 #include "base/hash.h" 12 #include "base/hash.h"
13 #include "base/location.h" 13 #include "base/location.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
15 #include "base/single_thread_task_runner.h" 16 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h" 18 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h" 19 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h" 20 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/thread_task_runner_handle.h" 21 #include "base/threading/thread_task_runner_handle.h"
21 #include "build/build_config.h" 22 #include "build/build_config.h"
22 #include "content/public/renderer/render_frame.h" 23 #include "content/public/renderer/render_frame.h"
23 #include "content/renderer/media/local_media_stream_audio_source.h" 24 #include "content/renderer/media/local_media_stream_audio_source.h"
24 #include "content/renderer/media/media_stream.h" 25 #include "content/renderer/media/media_stream.h"
25 #include "content/renderer/media/media_stream_constraints_util.h" 26 #include "content/renderer/media/media_stream_constraints_util.h"
26 #include "content/renderer/media/media_stream_dispatcher.h" 27 #include "content/renderer/media/media_stream_dispatcher.h"
27 #include "content/renderer/media/media_stream_video_capturer_source.h" 28 #include "content/renderer/media/media_stream_video_capturer_source.h"
28 #include "content/renderer/media/media_stream_video_track.h" 29 #include "content/renderer/media/media_stream_video_track.h"
29 #include "content/renderer/media/peer_connection_tracker.h" 30 #include "content/renderer/media/peer_connection_tracker.h"
30 #include "content/renderer/media/webrtc/processed_local_audio_source.h" 31 #include "content/renderer/media/webrtc/processed_local_audio_source.h"
31 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" 32 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h"
32 #include "content/renderer/media/webrtc_logging.h" 33 #include "content/renderer/media/webrtc_logging.h"
33 #include "content/renderer/media/webrtc_uma_histograms.h" 34 #include "content/renderer/media/webrtc_uma_histograms.h"
34 #include "content/renderer/render_thread_impl.h" 35 #include "content/renderer/render_thread_impl.h"
35 #include "third_party/WebKit/public/platform/URLConversion.h" 36 #include "third_party/WebKit/public/platform/URLConversion.h"
36 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" 37 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
37 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h" 38 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
38 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" 39 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
40 #include "third_party/WebKit/public/platform/WebString.h"
39 #include "third_party/WebKit/public/web/WebDocument.h" 41 #include "third_party/WebKit/public/web/WebDocument.h"
40 #include "third_party/WebKit/public/web/WebLocalFrame.h" 42 #include "third_party/WebKit/public/web/WebLocalFrame.h"
41 43
42 namespace content { 44 namespace content {
43 namespace { 45 namespace {
44 46
45 void CopyVector(const blink::WebVector<blink::WebString>& source, 47 void CopyFirstString(const blink::StringConstraint& constraint,
46 std::vector<std::string>* destination) { 48 std::string* destination) {
47 for (const auto& web_string : source) { 49 if (!constraint.exact().isEmpty())
48 destination->push_back(web_string.utf8()); 50 *destination = constraint.exact()[0].utf8();
51 }
52
53 bool FindDeviceId(const blink::WebVector<blink::WebString> candidates,
54 const MediaDeviceInfoArray& device_infos,
55 std::string* device_id) {
56 for (const auto& candidate : candidates) {
57 auto it = std::find_if(device_infos.begin(), device_infos.end(),
58 [&candidate](const MediaDeviceInfo& info) {
59 return info.device_id == candidate.utf8();
60 });
61
62 if (it != device_infos.end()) {
63 *device_id = it->device_id;
64 return true;
65 }
66 }
67
68 return false;
69 }
70
71 // If a device ID requested as exact basic constraint in |constraints| is found
72 // in |device_infos|, it is copied to |*device_id|, and the function returns
73 // false. If such a device is not found in |device_infos|, the function returns
74 // false and |*device_id| is left unmodified.
75 // If more than one device ID is requested as an exact basic constraint in
76 // |constraint|, the function returns false and |*device_id| is left unmodified.
77 // If no device ID is requested as an exact basic constraint, and at least one
78 // device ID requested as an ideal basic constraint or as an exact or ideal
79 // advanced constraint in |constraints| is found in |device_infos|, the first
80 // such device ID is copied to |*device_id| and the function returns true.
81 // If no such device ID is found, |*device_id| is left unmodified and the
82 // function returns true.
83 // TODO(guidou): Replace with a spec-compliant selection algorithm. See
84 // http://crbug.com/657733.
85 bool PickDeviceId(const blink::WebMediaConstraints& constraints,
86 const MediaDeviceInfoArray& device_infos,
87 std::string* device_id) {
88 DCHECK(!constraints.isNull());
89 DCHECK(device_id->empty());
90
91 if (constraints.basic().deviceId.exact().size() > 1) {
92 LOG(ERROR) << "Only one required device ID is supported";
93 return false;
94 }
95
96 if (constraints.basic().deviceId.exact().size() == 1 &&
97 !FindDeviceId(constraints.basic().deviceId.exact(), device_infos,
98 device_id)) {
99 LOG(ERROR) << "Invalid mandatory device ID = "
100 << constraints.basic().deviceId.exact()[0].utf8();
101 return false;
102 }
103
104 // There is no required device ID. Look at the alternates.
105 if (FindDeviceId(constraints.basic().deviceId.ideal(), device_infos,
106 device_id)) {
107 return true;
108 }
109
110 for (const auto& advanced : constraints.advanced()) {
111 if (FindDeviceId(advanced.deviceId.exact(), device_infos, device_id)) {
112 return true;
113 }
114 if (FindDeviceId(advanced.deviceId.ideal(), device_infos, device_id)) {
115 return true;
116 }
117 }
118
119 // No valid alternate device ID found. Select default device.
120 return true;
121 }
122
123 bool IsDeviceSource(const std::string& source) {
124 return source.empty();
125 }
126
127 void CopyConstraintsToTrackControls(
128 const blink::WebMediaConstraints& constraints,
129 TrackControls* track_controls,
130 bool* request_devices) {
131 DCHECK(!constraints.isNull());
132 track_controls->requested = true;
133 CopyFirstString(constraints.basic().mediaStreamSource,
134 &track_controls->stream_source);
135 if (IsDeviceSource(track_controls->stream_source)) {
136 bool request_devices_advanced = false;
137 for (const auto& advanced : constraints.advanced()) {
138 if (!advanced.deviceId.isEmpty()) {
139 request_devices_advanced = true;
140 break;
141 }
142 }
143 *request_devices =
144 request_devices_advanced || !constraints.basic().deviceId.isEmpty();
145 } else {
146 CopyFirstString(constraints.basic().deviceId, &track_controls->device_id);
147 *request_devices = false;
49 } 148 }
50 } 149 }
51 150
52 void CopyFirstString(const blink::WebVector<blink::WebString>& source, 151 void CopyHotwordAndLocalEchoToStreamControls(
53 std::string* destination) { 152 const blink::WebMediaConstraints& audio_constraints,
54 if (!source.isEmpty()) 153 StreamControls* controls) {
55 *destination = source[0].utf8(); 154 if (audio_constraints.isNull())
56 } 155 return;
57 156
58 void CopyBlinkRequestToStreamControls(const blink::WebUserMediaRequest& request, 157 if (audio_constraints.basic().hotwordEnabled.hasExact()) {
59 StreamControls* controls) { 158 controls->hotword_enabled =
60 if (request.isNull()) { 159 audio_constraints.basic().hotwordEnabled.exact();
61 return; 160 } else {
62 } 161 for (const auto& audio_advanced : audio_constraints.advanced()) {
63 if (!request.audioConstraints().isNull()) { 162 if (audio_advanced.hotwordEnabled.hasExact()) {
64 const blink::WebMediaTrackConstraintSet& audio_basic = 163 controls->hotword_enabled = audio_advanced.hotwordEnabled.exact();
65 request.audioConstraints().basic(); 164 break;
66 CopyFirstString(audio_basic.mediaStreamSource.exact(),
67 &controls->audio.stream_source);
68 CopyVector(audio_basic.deviceId.exact(), &(controls->audio.device_ids));
69 // Optionals. They may be either in ideal or in advanced.exact.
70 CopyVector(audio_basic.deviceId.ideal(),
71 &controls->audio.alternate_device_ids);
72 for (const auto& constraint : request.audioConstraints().advanced()) {
73 CopyVector(constraint.deviceId.exact(),
74 &controls->audio.alternate_device_ids);
75 CopyVector(constraint.deviceId.ideal(),
76 &controls->audio.alternate_device_ids);
77 }
78 if (audio_basic.hotwordEnabled.hasExact()) {
79 controls->hotword_enabled = audio_basic.hotwordEnabled.exact();
80 } else {
81 for (const auto& audio_advanced : request.audioConstraints().advanced()) {
82 if (audio_advanced.hotwordEnabled.hasExact()) {
83 controls->hotword_enabled = audio_advanced.hotwordEnabled.exact();
84 break;
85 }
86 } 165 }
87 } 166 }
167 }
88 168
89 if (request.audioConstraints().basic().disableLocalEcho.hasExact()) { 169 if (audio_constraints.basic().disableLocalEcho.hasExact()) {
90 controls->disable_local_echo = 170 controls->disable_local_echo =
91 request.audioConstraints().basic().disableLocalEcho.exact(); 171 audio_constraints.basic().disableLocalEcho.exact();
92 } else { 172 } else {
93 controls->disable_local_echo = 173 controls->disable_local_echo =
94 controls->audio.stream_source != kMediaStreamSourceDesktop; 174 controls->audio.stream_source != kMediaStreamSourceDesktop;
95 }
96 }
97 if (!request.videoConstraints().isNull()) {
98 const blink::WebMediaTrackConstraintSet& video_basic =
99 request.videoConstraints().basic();
100 CopyFirstString(video_basic.mediaStreamSource.exact(),
101 &(controls->video.stream_source));
102 CopyVector(video_basic.deviceId.exact(), &(controls->video.device_ids));
103 CopyVector(video_basic.deviceId.ideal(),
104 &(controls->video.alternate_device_ids));
105 for (const auto& constraint : request.videoConstraints().advanced()) {
106 CopyVector(constraint.deviceId.exact(),
107 &controls->video.alternate_device_ids);
108 CopyVector(constraint.deviceId.ideal(),
109 &controls->video.alternate_device_ids);
110 }
111 } 175 }
112 } 176 }
113 177
114 bool IsSameDevice(const StreamDeviceInfo& device, 178 bool IsSameDevice(const StreamDeviceInfo& device,
115 const StreamDeviceInfo& other_device) { 179 const StreamDeviceInfo& other_device) {
116 return device.device.id == other_device.device.id && 180 return device.device.id == other_device.device.id &&
117 device.device.type == other_device.device.type && 181 device.device.type == other_device.device.type &&
118 device.session_id == other_device.session_id; 182 device.session_id == other_device.session_id;
119 } 183 }
120 184
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 // webGetUserMedia. 240 // webGetUserMedia.
177 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA); 241 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA);
178 DCHECK(CalledOnValidThread()); 242 DCHECK(CalledOnValidThread());
179 243
180 if (RenderThreadImpl::current()) { 244 if (RenderThreadImpl::current()) {
181 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia( 245 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia(
182 user_media_request); 246 user_media_request);
183 } 247 }
184 248
185 int request_id = g_next_request_id++; 249 int request_id = g_next_request_id++;
186 StreamControls controls; 250 std::unique_ptr<StreamControls> controls = base::MakeUnique<StreamControls>();
187 url::Origin security_origin;
188 bool enable_automatic_output_device_selection = false;
189 251
190 // |user_media_request| can't be mocked. So in order to test at all we check 252 // |user_media_request| can't be mocked. So in order to test at all we check
191 // if it isNull. 253 // if it isNull.
254 // TODO(guidou): Remove this test-specific code path.
192 if (user_media_request.isNull()) { 255 if (user_media_request.isNull()) {
193 // We are in a test. 256 // We are in a test.
194 controls.audio.requested = true; 257 controls->audio.requested = true;
195 controls.video.requested = true; 258 controls->video.requested = true;
259 FinalizeRequestUserMedia(
260 request_id, user_media_request, std::move(controls),
261 false /* automatic_output_device_selection */, url::Origin());
196 } else { 262 } else {
263 // ownerDocument may be null if we are in a test.
264 // In that case, it's OK to not check frame().
265 DCHECK(user_media_request.ownerDocument().isNull() ||
266 render_frame()->GetWebFrame() ==
267 static_cast<blink::WebFrame*>(
268 user_media_request.ownerDocument().frame()));
269
270 bool enable_automatic_output_device_selection = false;
271 bool request_audio_input_devices = false;
197 if (user_media_request.audio()) { 272 if (user_media_request.audio()) {
198 controls.audio.requested = true; 273 CopyConstraintsToTrackControls(user_media_request.audioConstraints(),
274 &controls->audio,
275 &request_audio_input_devices);
276 CopyHotwordAndLocalEchoToStreamControls(
277 user_media_request.audioConstraints(), controls.get());
199 // Check if this input device should be used to select a matching output 278 // Check if this input device should be used to select a matching output
200 // device for audio rendering. 279 // device for audio rendering.
201 GetConstraintValueAsBoolean( 280 GetConstraintValueAsBoolean(
202 user_media_request.audioConstraints(), 281 user_media_request.audioConstraints(),
203 &blink::WebMediaTrackConstraintSet::renderToAssociatedSink, 282 &blink::WebMediaTrackConstraintSet::renderToAssociatedSink,
204 &enable_automatic_output_device_selection); 283 &enable_automatic_output_device_selection);
205 } 284 }
285 bool request_video_input_devices = false;
206 if (user_media_request.video()) { 286 if (user_media_request.video()) {
207 controls.video.requested = true; 287 CopyConstraintsToTrackControls(user_media_request.videoConstraints(),
288 &controls->video,
289 &request_video_input_devices);
208 } 290 }
209 CopyBlinkRequestToStreamControls(user_media_request, &controls); 291
210 security_origin = user_media_request.getSecurityOrigin(); 292 url::Origin security_origin = user_media_request.getSecurityOrigin();
211 // ownerDocument may be null if we are in a test. 293 if (request_audio_input_devices || request_video_input_devices) {
212 // In that case, it's OK to not check frame(). 294 GetMediaDevicesDispatcher()->EnumerateDevices(
213 DCHECK(user_media_request.ownerDocument().isNull() || 295 request_audio_input_devices, request_video_input_devices,
214 render_frame()->GetWebFrame() == 296 false /* request_audio_output_devices */, security_origin,
215 static_cast<blink::WebFrame*>( 297 base::Bind(&UserMediaClientImpl::SelectUserMediaDevice,
216 user_media_request.ownerDocument().frame())); 298 weak_factory_.GetWeakPtr(), request_id, user_media_request,
299 base::Passed(&controls),
300 enable_automatic_output_device_selection,
301 security_origin));
302 } else {
303 FinalizeRequestUserMedia(
304 request_id, user_media_request, std::move(controls),
305 enable_automatic_output_device_selection, security_origin);
306 }
307 }
308 }
309
310 void UserMediaClientImpl::SelectUserMediaDevice(
311 int request_id,
312 const blink::WebUserMediaRequest& user_media_request,
313 std::unique_ptr<StreamControls> controls,
314 bool enable_automatic_output_device_selection,
315 const url::Origin& security_origin,
316 const EnumerationResult& device_enumeration) {
317 DCHECK(CalledOnValidThread());
318
319 if (controls->audio.requested &&
320 IsDeviceSource(controls->audio.stream_source)) {
321 if (!PickDeviceId(user_media_request.audioConstraints(),
322 device_enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT],
323 &controls->audio.device_id)) {
324 GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE,
325 "");
326 return;
327 }
217 } 328 }
218 329
219 DVLOG(1) << "UserMediaClientImpl::requestUserMedia(" << request_id << ", [ " 330 if (controls->video.requested &&
220 << "audio=" << (controls.audio.requested) 331 IsDeviceSource(controls->video.stream_source)) {
221 << " select associated sink: " 332 if (!PickDeviceId(user_media_request.videoConstraints(),
222 << enable_automatic_output_device_selection 333 device_enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT],
223 << ", video=" << (controls.video.requested) << " ], " 334 &controls->video.device_id)) {
224 << security_origin << ")"; 335 GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE,
225 336 "");
226 std::string audio_device_id; 337 return;
227 if (!user_media_request.isNull() && user_media_request.audio()) { 338 }
228 GetConstraintValueAsString(user_media_request.audioConstraints(),
229 &blink::WebMediaTrackConstraintSet::deviceId,
230 &audio_device_id);
231 } 339 }
232 340
233 std::string video_device_id; 341 FinalizeRequestUserMedia(request_id, user_media_request, std::move(controls),
234 if (!user_media_request.isNull() && user_media_request.video()) { 342 enable_automatic_output_device_selection,
235 GetConstraintValueAsString(user_media_request.videoConstraints(), 343 security_origin);
236 &blink::WebMediaTrackConstraintSet::deviceId, 344 }
237 &video_device_id);
238 }
239 345
240 WebRtcLogMessage(base::StringPrintf( 346 void UserMediaClientImpl::FinalizeRequestUserMedia(
241 "MSI::requestUserMedia. request_id=%d" 347 int request_id,
242 ", audio source id=%s" 348 const blink::WebUserMediaRequest& user_media_request,
243 ", video source id=%s", 349 std::unique_ptr<StreamControls> controls,
244 request_id, audio_device_id.c_str(), video_device_id.c_str())); 350 bool enable_automatic_output_device_selection,
351 const url::Origin& security_origin) {
352 DCHECK(CalledOnValidThread());
353
354 WebRtcLogMessage(
355 base::StringPrintf("MSI::requestUserMedia. request_id=%d"
356 ", audio source id=%s"
357 ", video source id=%s",
358 request_id, controls->audio.device_id.c_str(),
359 controls->video.device_id.c_str()));
245 360
246 user_media_requests_.push_back( 361 user_media_requests_.push_back(
247 new UserMediaRequestInfo(request_id, user_media_request, 362 new UserMediaRequestInfo(request_id, user_media_request,
248 enable_automatic_output_device_selection)); 363 enable_automatic_output_device_selection));
249 364
250 media_stream_dispatcher_->GenerateStream( 365 media_stream_dispatcher_->GenerateStream(
251 request_id, weak_factory_.GetWeakPtr(), controls, security_origin); 366 request_id, weak_factory_.GetWeakPtr(), *controls, security_origin);
252 } 367 }
253 368
254 void UserMediaClientImpl::cancelUserMediaRequest( 369 void UserMediaClientImpl::cancelUserMediaRequest(
255 const blink::WebUserMediaRequest& user_media_request) { 370 const blink::WebUserMediaRequest& user_media_request) {
256 DCHECK(CalledOnValidThread()); 371 DCHECK(CalledOnValidThread());
257 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request); 372 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request);
258 if (request) { 373 if (request) {
259 // We can't abort the stream generation process. 374 // We can't abort the stream generation process.
260 // Instead, erase the request. Once the stream is generated we will stop the 375 // Instead, erase the request. Once the stream is generated we will stop the
261 // stream if the request does not exist. 376 // stream if the request does not exist.
(...skipping 716 matching lines...) Expand 10 before | Expand all | Expand 10 after
978 1093
979 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const { 1094 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const {
980 return !sources_waiting_for_callback_.empty(); 1095 return !sources_waiting_for_callback_.empty();
981 } 1096 }
982 1097
983 void UserMediaClientImpl::OnDestruct() { 1098 void UserMediaClientImpl::OnDestruct() {
984 delete this; 1099 delete this;
985 } 1100 }
986 1101
987 } // namespace content 1102 } // namespace content
OLDNEW
« 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