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

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: fixes 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 // Returns false if an device ID requested as mandatory is not found in
72 // |device_infos| is . Otherwise, if a valid device is found, its ID is copied
73 // to |*device_id|; if no valid device is found, |*device_id| is left unmodified
74 // and true is returned.
hta - Chromium 2016/11/30 18:51:43 Suggested reword: If a deviceId requested as mand
Guido Urdaneta 2016/12/01 10:28:53 Done.
75 bool PickDeviceId(const blink::WebMediaConstraints& constraints,
76 const MediaDeviceInfoArray& device_infos,
77 std::string* device_id) {
78 DCHECK(!constraints.isNull());
79 DCHECK(device_id->empty());
80
81 if (constraints.basic().deviceId.exact().size() > 1) {
82 LOG(ERROR) << "Only one required device ID is supported";
83 return false;
84 }
85
86 if (constraints.basic().deviceId.exact().size() == 1 &&
87 !FindDeviceId(constraints.basic().deviceId.exact(), device_infos,
88 device_id)) {
89 LOG(ERROR) << "Invalid mandatory device ID = "
90 << constraints.basic().deviceId.exact()[0].utf8();
91 return false;
92 }
93
94 // There is no required device ID. Look at the alternates.
95 if (FindDeviceId(constraints.basic().deviceId.ideal(), device_infos,
96 device_id)) {
97 return true;
98 }
99
100 for (const auto& advanced : constraints.advanced()) {
101 if (FindDeviceId(advanced.deviceId.exact(), device_infos, device_id)) {
102 return true;
103 }
104 if (FindDeviceId(advanced.deviceId.ideal(), device_infos, device_id)) {
105 return true;
106 }
107 }
108
109 // No valid alternate device ID found. Select default device.
110 return true;
111 }
112
113 bool IsDeviceSource(const std::string& source) {
114 return source.empty();
115 }
116
117 void CopyConstraintsToTrackControls(
118 const blink::WebMediaConstraints& constraints,
119 TrackControls* track_controls,
120 bool* request_devices) {
121 DCHECK(!constraints.isNull());
122 track_controls->requested = true;
123 CopyFirstString(constraints.basic().mediaStreamSource,
124 &track_controls->stream_source);
125 if (IsDeviceSource(track_controls->stream_source)) {
126 bool request_devices_advanced = false;
127 for (const auto& advanced : constraints.advanced()) {
128 if (!advanced.deviceId.isEmpty()) {
129 request_devices_advanced = true;
130 break;
131 }
132 }
133 *request_devices =
134 request_devices_advanced || !constraints.basic().deviceId.isEmpty();
135 } else {
136 CopyFirstString(constraints.basic().deviceId, &track_controls->device_id);
137 *request_devices = false;
49 } 138 }
50 } 139 }
51 140
52 void CopyFirstString(const blink::WebVector<blink::WebString>& source, 141 void CopyHotwordAndLocalEchoToStreamControls(
53 std::string* destination) { 142 const blink::WebMediaConstraints& audio_constraints,
54 if (!source.isEmpty()) 143 StreamControls* controls) {
55 *destination = source[0].utf8(); 144 if (audio_constraints.isNull())
56 } 145 return;
57 146
58 void CopyBlinkRequestToStreamControls(const blink::WebUserMediaRequest& request, 147 if (audio_constraints.basic().hotwordEnabled.hasExact()) {
hta - Chromium 2016/11/30 18:51:43 This looks like you should be using the constraint
Guido Urdaneta 2016/12/01 10:28:53 This part is copied practically verbatim from the
59 StreamControls* controls) { 148 controls->hotword_enabled =
60 if (request.isNull()) { 149 audio_constraints.basic().hotwordEnabled.exact();
61 return; 150 } else {
62 } 151 for (const auto& audio_advanced : audio_constraints.advanced()) {
63 if (!request.audioConstraints().isNull()) { 152 if (audio_advanced.hotwordEnabled.hasExact()) {
64 const blink::WebMediaTrackConstraintSet& audio_basic = 153 controls->hotword_enabled = audio_advanced.hotwordEnabled.exact();
65 request.audioConstraints().basic(); 154 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 } 155 }
87 } 156 }
157 }
88 158
89 if (request.audioConstraints().basic().disableLocalEcho.hasExact()) { 159 if (audio_constraints.basic().disableLocalEcho.hasExact()) {
90 controls->disable_local_echo = 160 controls->disable_local_echo =
91 request.audioConstraints().basic().disableLocalEcho.exact(); 161 audio_constraints.basic().disableLocalEcho.exact();
92 } else { 162 } else {
93 controls->disable_local_echo = 163 controls->disable_local_echo =
94 controls->audio.stream_source != kMediaStreamSourceDesktop; 164 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 } 165 }
112 } 166 }
113 167
114 bool IsSameDevice(const StreamDeviceInfo& device, 168 bool IsSameDevice(const StreamDeviceInfo& device,
115 const StreamDeviceInfo& other_device) { 169 const StreamDeviceInfo& other_device) {
116 return device.device.id == other_device.device.id && 170 return device.device.id == other_device.device.id &&
117 device.device.type == other_device.device.type && 171 device.device.type == other_device.device.type &&
118 device.session_id == other_device.session_id; 172 device.session_id == other_device.session_id;
119 } 173 }
120 174
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 // webGetUserMedia. 230 // webGetUserMedia.
177 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA); 231 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA);
178 DCHECK(CalledOnValidThread()); 232 DCHECK(CalledOnValidThread());
179 233
180 if (RenderThreadImpl::current()) { 234 if (RenderThreadImpl::current()) {
181 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia( 235 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia(
182 user_media_request); 236 user_media_request);
183 } 237 }
184 238
185 int request_id = g_next_request_id++; 239 int request_id = g_next_request_id++;
186 StreamControls controls; 240 std::unique_ptr<StreamControls> controls = base::MakeUnique<StreamControls>();
187 url::Origin security_origin;
188 bool enable_automatic_output_device_selection = false;
189 241
190 // |user_media_request| can't be mocked. So in order to test at all we check 242 // |user_media_request| can't be mocked. So in order to test at all we check
191 // if it isNull. 243 // if it isNull.
192 if (user_media_request.isNull()) { 244 if (user_media_request.isNull()) {
193 // We are in a test. 245 // We are in a test.
194 controls.audio.requested = true; 246 controls->audio.requested = true;
195 controls.video.requested = true; 247 controls->video.requested = true;
hta - Chromium 2016/11/30 18:51:43 I added the ability to initialize an UserMediaRequ
Guido Urdaneta 2016/12/01 10:28:53 Added TODO. Will try to do that when implementing
248 FinalizeRequestUserMedia(
249 request_id, user_media_request, std::move(controls),
250 false /* automatic_output_device_selection */, url::Origin());
196 } else { 251 } else {
252 // ownerDocument may be null if we are in a test.
253 // In that case, it's OK to not check frame().
254 DCHECK(user_media_request.ownerDocument().isNull() ||
255 render_frame()->GetWebFrame() ==
256 static_cast<blink::WebFrame*>(
257 user_media_request.ownerDocument().frame()));
258
259 bool enable_automatic_output_device_selection = false;
260 bool request_audio_input_devices = false;
197 if (user_media_request.audio()) { 261 if (user_media_request.audio()) {
198 controls.audio.requested = true; 262 CopyConstraintsToTrackControls(user_media_request.audioConstraints(),
263 &controls->audio,
264 &request_audio_input_devices);
265 CopyHotwordAndLocalEchoToStreamControls(
266 user_media_request.audioConstraints(), controls.get());
199 // Check if this input device should be used to select a matching output 267 // Check if this input device should be used to select a matching output
200 // device for audio rendering. 268 // device for audio rendering.
201 GetConstraintValueAsBoolean( 269 GetConstraintValueAsBoolean(
202 user_media_request.audioConstraints(), 270 user_media_request.audioConstraints(),
203 &blink::WebMediaTrackConstraintSet::renderToAssociatedSink, 271 &blink::WebMediaTrackConstraintSet::renderToAssociatedSink,
204 &enable_automatic_output_device_selection); 272 &enable_automatic_output_device_selection);
205 } 273 }
274 bool request_video_input_devices = false;
206 if (user_media_request.video()) { 275 if (user_media_request.video()) {
207 controls.video.requested = true; 276 CopyConstraintsToTrackControls(user_media_request.videoConstraints(),
277 &controls->video,
278 &request_video_input_devices);
208 } 279 }
209 CopyBlinkRequestToStreamControls(user_media_request, &controls); 280
210 security_origin = user_media_request.getSecurityOrigin(); 281 url::Origin security_origin = user_media_request.getSecurityOrigin();
211 // ownerDocument may be null if we are in a test. 282 if (request_audio_input_devices || request_video_input_devices) {
212 // In that case, it's OK to not check frame(). 283 GetMediaDevicesDispatcher()->EnumerateDevices(
hta - Chromium 2016/11/30 18:51:43 Will this use a cache if it's available?
Guido Urdaneta 2016/12/01 10:28:53 Yes, it will. At the moment the cache is on the br
213 DCHECK(user_media_request.ownerDocument().isNull() || 284 request_audio_input_devices, request_video_input_devices,
214 render_frame()->GetWebFrame() == 285 false /* request_audio_output_devices */, security_origin,
215 static_cast<blink::WebFrame*>( 286 base::Bind(&UserMediaClientImpl::SelectUserMediaDevice,
216 user_media_request.ownerDocument().frame())); 287 weak_factory_.GetWeakPtr(), request_id, user_media_request,
288 base::Passed(&controls),
289 enable_automatic_output_device_selection,
290 security_origin));
291 } else {
292 FinalizeRequestUserMedia(
293 request_id, user_media_request, std::move(controls),
294 enable_automatic_output_device_selection, security_origin);
295 }
296 }
297 }
298
299 void UserMediaClientImpl::SelectUserMediaDevice(
300 int request_id,
301 const blink::WebUserMediaRequest& user_media_request,
302 std::unique_ptr<StreamControls> controls,
303 bool enable_automatic_output_device_selection,
304 const url::Origin& security_origin,
305 const EnumerationResult& device_enumeration) {
306 DCHECK(CalledOnValidThread());
307
308 if (controls->audio.requested &&
309 IsDeviceSource(controls->audio.stream_source)) {
310 if (!PickDeviceId(user_media_request.audioConstraints(),
311 device_enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT],
312 &controls->audio.device_id)) {
313 GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE,
314 "");
315 return;
316 }
217 } 317 }
218 318
219 DVLOG(1) << "UserMediaClientImpl::requestUserMedia(" << request_id << ", [ " 319 if (controls->video.requested &&
220 << "audio=" << (controls.audio.requested) 320 IsDeviceSource(controls->video.stream_source)) {
221 << " select associated sink: " 321 if (!PickDeviceId(user_media_request.videoConstraints(),
222 << enable_automatic_output_device_selection 322 device_enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT],
223 << ", video=" << (controls.video.requested) << " ], " 323 &controls->video.device_id)) {
224 << security_origin << ")"; 324 GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE,
225 325 "");
226 std::string audio_device_id; 326 return;
227 if (!user_media_request.isNull() && user_media_request.audio()) { 327 }
228 GetConstraintValueAsString(user_media_request.audioConstraints(),
229 &blink::WebMediaTrackConstraintSet::deviceId,
230 &audio_device_id);
231 } 328 }
232 329
233 std::string video_device_id; 330 FinalizeRequestUserMedia(request_id, user_media_request, std::move(controls),
234 if (!user_media_request.isNull() && user_media_request.video()) { 331 enable_automatic_output_device_selection,
235 GetConstraintValueAsString(user_media_request.videoConstraints(), 332 security_origin);
236 &blink::WebMediaTrackConstraintSet::deviceId, 333 }
237 &video_device_id);
238 }
239 334
240 WebRtcLogMessage(base::StringPrintf( 335 void UserMediaClientImpl::FinalizeRequestUserMedia(
241 "MSI::requestUserMedia. request_id=%d" 336 int request_id,
242 ", audio source id=%s" 337 const blink::WebUserMediaRequest& user_media_request,
243 ", video source id=%s", 338 std::unique_ptr<StreamControls> controls,
244 request_id, audio_device_id.c_str(), video_device_id.c_str())); 339 bool enable_automatic_output_device_selection,
340 const url::Origin& security_origin) {
341 DCHECK(CalledOnValidThread());
342
343 WebRtcLogMessage(
344 base::StringPrintf("MSI::requestUserMedia. request_id=%d"
345 ", audio source id=%s"
346 ", video source id=%s",
347 request_id, controls->audio.device_id.c_str(),
348 controls->video.device_id.c_str()));
245 349
246 user_media_requests_.push_back( 350 user_media_requests_.push_back(
247 new UserMediaRequestInfo(request_id, user_media_request, 351 new UserMediaRequestInfo(request_id, user_media_request,
248 enable_automatic_output_device_selection)); 352 enable_automatic_output_device_selection));
249 353
250 media_stream_dispatcher_->GenerateStream( 354 media_stream_dispatcher_->GenerateStream(
251 request_id, weak_factory_.GetWeakPtr(), controls, security_origin); 355 request_id, weak_factory_.GetWeakPtr(), *controls, security_origin);
252 } 356 }
253 357
254 void UserMediaClientImpl::cancelUserMediaRequest( 358 void UserMediaClientImpl::cancelUserMediaRequest(
255 const blink::WebUserMediaRequest& user_media_request) { 359 const blink::WebUserMediaRequest& user_media_request) {
256 DCHECK(CalledOnValidThread()); 360 DCHECK(CalledOnValidThread());
257 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request); 361 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request);
258 if (request) { 362 if (request) {
259 // We can't abort the stream generation process. 363 // We can't abort the stream generation process.
260 // Instead, erase the request. Once the stream is generated we will stop the 364 // Instead, erase the request. Once the stream is generated we will stop the
261 // stream if the request does not exist. 365 // stream if the request does not exist.
(...skipping 716 matching lines...) Expand 10 before | Expand all | Expand 10 after
978 1082
979 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const { 1083 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const {
980 return !sources_waiting_for_callback_.empty(); 1084 return !sources_waiting_for_callback_.empty();
981 } 1085 }
982 1086
983 void UserMediaClientImpl::OnDestruct() { 1087 void UserMediaClientImpl::OnDestruct() {
984 delete this; 1088 delete this;
985 } 1089 }
986 1090
987 } // namespace content 1091 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698