OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/media_stream_video_source.h" | 5 #include "content/renderer/media/media_stream_video_source.h" |
6 | 6 |
7 #include <algorithm> | |
7 #include <limits> | 8 #include <limits> |
8 #include <string> | 9 #include <string> |
9 | 10 |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
12 #include "content/renderer/media/media_stream_dependency_factory.h" | 13 #include "content/renderer/media/media_stream_dependency_factory.h" |
13 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" | 14 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" |
14 | 15 |
15 namespace content { | 16 namespace content { |
16 | 17 |
(...skipping 12 matching lines...) Expand all Loading... | |
29 const int MediaStreamVideoSource::kDefaultFrameRate = 30; | 30 const int MediaStreamVideoSource::kDefaultFrameRate = 30; |
30 | 31 |
31 namespace { | 32 namespace { |
32 // Constraints keys for http://dev.w3.org/2011/webrtc/editor/getusermedia.html | 33 // Constraints keys for http://dev.w3.org/2011/webrtc/editor/getusermedia.html |
33 const char kSourceId[] = "sourceId"; | 34 const char kSourceId[] = "sourceId"; |
34 | 35 |
35 // Google-specific key prefix. Constraints with this prefix are ignored if they | 36 // Google-specific key prefix. Constraints with this prefix are ignored if they |
36 // are unknown. | 37 // are unknown. |
37 const char kGooglePrefix[] = "goog"; | 38 const char kGooglePrefix[] = "goog"; |
38 | 39 |
40 // MediaStreamVideoSource supports cropping of video frames but only up to | |
41 // kMaxCropFactor. | |
42 const int kMaxCropFactor = 2; | |
43 | |
39 // Returns true if |constraint| is fulfilled. |format| can be changed | 44 // Returns true if |constraint| is fulfilled. |format| can be changed |
40 // changed by a constraint. Ie - the frame rate can be changed by setting | 45 // changed by a constraint. Ie - the frame rate can be changed by setting |
41 // maxFrameRate. | 46 // maxFrameRate. |
42 bool UpdateFormatForConstraint( | 47 bool UpdateFormatForConstraint( |
43 const blink::WebMediaConstraint& constraint, | 48 const blink::WebMediaConstraint& constraint, |
44 bool mandatory, | 49 bool mandatory, |
45 media::VideoCaptureFormat* format) { | 50 media::VideoCaptureFormat* format) { |
46 DCHECK(format != NULL); | 51 DCHECK(format != NULL); |
47 | 52 |
48 if (!format->IsValid()) | 53 if (!format->IsValid()) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 | 94 |
90 int value; | 95 int value; |
91 if (!base::StringToInt(constraint_value, &value)) { | 96 if (!base::StringToInt(constraint_value, &value)) { |
92 DLOG(WARNING) << "Can't parse MediaStream constraint. Name:" | 97 DLOG(WARNING) << "Can't parse MediaStream constraint. Name:" |
93 << constraint_name << " Value:" << constraint_value; | 98 << constraint_name << " Value:" << constraint_value; |
94 return false; | 99 return false; |
95 } | 100 } |
96 if (constraint_name == MediaStreamVideoSource::kMinWidth) { | 101 if (constraint_name == MediaStreamVideoSource::kMinWidth) { |
97 return (value <= format->frame_size.width()); | 102 return (value <= format->frame_size.width()); |
98 } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) { | 103 } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) { |
99 return (value >= format->frame_size.width()); | 104 return (value * kMaxCropFactor >= format->frame_size.width()); |
100 } else if (constraint_name == MediaStreamVideoSource::kMinHeight) { | 105 } else if (constraint_name == MediaStreamVideoSource::kMinHeight) { |
101 return (value <= format->frame_size.height()); | 106 return (value <= format->frame_size.height()); |
102 } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) { | 107 } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) { |
103 return (value >= format->frame_size.height()); | 108 return (value * kMaxCropFactor >= format->frame_size.height()); |
104 } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) { | 109 } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) { |
105 return (value <= format->frame_rate); | 110 return (value <= format->frame_rate); |
106 } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) { | 111 } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) { |
107 if (value == 0) { | 112 if (value == 0) { |
108 // The frame rate is set by constraint. | 113 // The frame rate is set by constraint. |
109 // Don't allow 0 as frame rate if it is a mandatory constraint. | 114 // Don't allow 0 as frame rate if it is a mandatory constraint. |
110 // Set the frame rate to 1 if it is not mandatory. | 115 // Set the frame rate to 1 if it is not mandatory. |
111 if (mandatory) { | 116 if (mandatory) { |
112 return false; | 117 return false; |
113 } else { | 118 } else { |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
178 FilterFormatsByConstraint(optional[i], false, ¤t_candidates); | 183 FilterFormatsByConstraint(optional[i], false, ¤t_candidates); |
179 if (!current_candidates.empty()) { | 184 if (!current_candidates.empty()) { |
180 candidates = current_candidates; | 185 candidates = current_candidates; |
181 } | 186 } |
182 } | 187 } |
183 | 188 |
184 // We have done as good as we can to filter the supported resolutions. | 189 // We have done as good as we can to filter the supported resolutions. |
185 return candidates; | 190 return candidates; |
186 } | 191 } |
187 | 192 |
188 // Find the format that best matches the default video size. | 193 // Retrieve the desired max width and height from |constraints|. |
189 // This algorithm is chosen since a resolution must be picked even if no | 194 void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints, |
190 // constraints are provided. We don't just select the maximum supported | 195 int* dw, int* dh) { |
191 // resolution since higher resolution cost more in terms of complexity and | 196 bool mandatory_found = false; |
192 // many cameras perform worse at its maximum supported resolution. | 197 blink::WebString width; |
193 const media::VideoCaptureFormat& GetBestCaptureFormat( | 198 if (constraints.getMandatoryConstraintValue( |
194 const media::VideoCaptureFormats& formats) { | 199 MediaStreamVideoSource::kMaxWidth, width)) { |
195 DCHECK(!formats.empty()); | 200 base::StringToInt(width.utf8(), dw); |
201 mandatory_found = true; | |
202 } | |
203 blink::WebString height; | |
204 if (constraints.getMandatoryConstraintValue( | |
205 MediaStreamVideoSource::kMaxHeight, height)) { | |
206 base::StringToInt(height.utf8(), dh); | |
207 mandatory_found = true; | |
208 } | |
209 if (mandatory_found) | |
210 return; | |
196 | 211 |
197 int default_area = | 212 if (constraints.getOptionalConstraintValue( |
198 MediaStreamVideoSource::kDefaultWidth * | 213 MediaStreamVideoSource::kMaxWidth, width)) { |
199 MediaStreamVideoSource::kDefaultHeight; | 214 base::StringToInt(width.utf8(), dw); |
215 } | |
216 if (constraints.getOptionalConstraintValue( | |
217 MediaStreamVideoSource::kMaxHeight, height)) { | |
218 base::StringToInt(height.utf8(), dh); | |
219 } | |
220 } | |
200 | 221 |
222 const media::VideoCaptureFormat& GetBestFormatBasedOnArea( | |
223 const media::VideoCaptureFormats& formats, | |
224 int area) { | |
201 media::VideoCaptureFormats::const_iterator it = formats.begin(); | 225 media::VideoCaptureFormats::const_iterator it = formats.begin(); |
202 media::VideoCaptureFormats::const_iterator best_it = formats.begin(); | 226 media::VideoCaptureFormats::const_iterator best_it = formats.begin(); |
203 int best_diff = std::numeric_limits<int>::max(); | 227 int best_diff = std::numeric_limits<int>::max(); |
204 for (; it != formats.end(); ++it) { | 228 for (; it != formats.end(); ++it) { |
205 int diff = abs(default_area - | 229 int diff = abs(area - it->frame_size.width() * it->frame_size.height()); |
206 it->frame_size.width() * it->frame_size.height()); | |
207 if (diff < best_diff) { | 230 if (diff < best_diff) { |
208 best_diff = diff; | 231 best_diff = diff; |
209 best_it = it; | 232 best_it = it; |
210 } | 233 } |
211 } | 234 } |
212 return *best_it; | 235 return *best_it; |
213 } | 236 } |
214 | 237 |
238 // Find the format that best matches the default video size. | |
239 // This algorithm is chosen since a resolution must be picked even if no | |
240 // constraints are provided. We don't just select the maximum supported | |
241 // resolution since higher resolution cost more in terms of complexity and | |
242 // many cameras perform worse at its maximum supported resolution. | |
243 void GetBestCaptureFormat( | |
244 const media::VideoCaptureFormats& formats, | |
245 const blink::WebMediaConstraints& constraints, | |
246 media::VideoCaptureFormat* capture_format, | |
247 gfx::Size* frame_output_size) { | |
248 DCHECK(!formats.empty()); | |
249 DCHECK(frame_output_size); | |
250 | |
251 int max_width = std::numeric_limits<int>::max(); | |
252 int max_height = std::numeric_limits<int>::max();; | |
253 GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height); | |
254 | |
255 *capture_format = GetBestFormatBasedOnArea( | |
256 formats, | |
257 std::min(max_width, MediaStreamVideoSource::kDefaultWidth) * | |
258 std::min(max_height, MediaStreamVideoSource::kDefaultHeight)); | |
259 | |
260 *frame_output_size = capture_format->frame_size; | |
261 if (max_width < frame_output_size->width()) | |
262 frame_output_size->set_width(max_width); | |
263 if (max_height < frame_output_size->height()) | |
264 frame_output_size->set_height(max_height); | |
265 } | |
266 | |
267 // Empty method used for keeping a reference to the original media::VideoFrame | |
268 // in MediaStreamVideoSource::DeliverVideoFrame if cropping is needed. | |
269 void ReleaseOriginalFrame( | |
270 const scoped_refptr<media::VideoFrame>& frame) { | |
271 } | |
272 | |
215 } // anonymous namespace | 273 } // anonymous namespace |
216 | 274 |
217 MediaStreamVideoSource::MediaStreamVideoSource( | 275 MediaStreamVideoSource::MediaStreamVideoSource( |
218 MediaStreamDependencyFactory* factory) | 276 MediaStreamDependencyFactory* factory) |
219 : state_(NEW), | 277 : state_(NEW), |
220 factory_(factory), | 278 factory_(factory), |
221 capture_adapter_(NULL) { | 279 capture_adapter_(NULL) { |
222 DCHECK(factory_); | 280 DCHECK(factory_); |
223 } | 281 } |
224 | 282 |
(...skipping 23 matching lines...) Expand all Loading... | |
248 | 306 |
249 state_ = RETRIEVING_CAPABILITIES; | 307 state_ = RETRIEVING_CAPABILITIES; |
250 GetCurrentSupportedFormats(max_requested_width, | 308 GetCurrentSupportedFormats(max_requested_width, |
251 max_requested_height); | 309 max_requested_height); |
252 | 310 |
253 break; | 311 break; |
254 } | 312 } |
255 case STARTING: | 313 case STARTING: |
256 case RETRIEVING_CAPABILITIES: { | 314 case RETRIEVING_CAPABILITIES: { |
257 // The |callback| will be triggered once the delegate has started or | 315 // The |callback| will be triggered once the delegate has started or |
258 // the capabilitites has been retrieved. | 316 // the capabilities has been retrieved. |
259 break; | 317 break; |
260 } | 318 } |
261 case ENDED: | 319 case ENDED: |
262 case STARTED: { | 320 case STARTED: { |
263 // Currently, reconfiguring the source is not supported. | 321 // Currently, reconfiguring the source is not supported. |
264 FinalizeAddTrack(); | 322 FinalizeAddTrack(); |
265 } | 323 } |
266 } | 324 } |
267 } | 325 } |
268 | 326 |
(...skipping 26 matching lines...) Expand all Loading... | |
295 } | 353 } |
296 | 354 |
297 void MediaStreamVideoSource::DoStopSource() { | 355 void MediaStreamVideoSource::DoStopSource() { |
298 DVLOG(3) << "DoStopSource()"; | 356 DVLOG(3) << "DoStopSource()"; |
299 StopSourceImpl(); | 357 StopSourceImpl(); |
300 state_ = ENDED; | 358 state_ = ENDED; |
301 } | 359 } |
302 | 360 |
303 void MediaStreamVideoSource::DeliverVideoFrame( | 361 void MediaStreamVideoSource::DeliverVideoFrame( |
304 const scoped_refptr<media::VideoFrame>& frame) { | 362 const scoped_refptr<media::VideoFrame>& frame) { |
305 if (capture_adapter_) | 363 scoped_refptr<media::VideoFrame> video_frame(frame); |
306 capture_adapter_->OnFrameCaptured(frame); | 364 |
365 if (frame->coded_size() != frame_output_size_) { | |
366 const int visible_width = std::min(frame_output_size_.width(), | |
367 frame->coded_size().width()); | |
368 const int horiz_crop = | |
369 ((frame->coded_size().width() - visible_width) / 2) & ~1; | |
Jói
2014/03/05 15:57:10
Why do you have the & ~1 bit, which (unless my min
perkj_chrome
2014/03/06 09:14:42
Good question- I dont' understand either why setti
| |
370 | |
371 const int visible_height = std::min(frame_output_size_.height(), | |
372 frame->coded_size().height()); | |
373 const int vert_crop = | |
374 ((frame->coded_size().height() - visible_height) / 2) & ~1; | |
375 | |
376 gfx::Rect rect(horiz_crop, vert_crop, visible_width, | |
377 visible_height); | |
378 video_frame = media::VideoFrame::WrapVideoFrame( | |
379 frame, rect, base::Bind(&ReleaseOriginalFrame, frame)); | |
380 } | |
381 | |
382 if ((frame->format() == media::VideoFrame::I420 || | |
383 frame->format() == media::VideoFrame::YV12) && | |
384 capture_adapter_) { | |
385 capture_adapter_->OnFrameCaptured(video_frame); | |
386 } | |
307 } | 387 } |
308 | 388 |
309 void MediaStreamVideoSource::OnSupportedFormats( | 389 void MediaStreamVideoSource::OnSupportedFormats( |
310 const media::VideoCaptureFormats& formats) { | 390 const media::VideoCaptureFormats& formats) { |
311 DCHECK(CalledOnValidThread()); | 391 DCHECK(CalledOnValidThread()); |
312 DCHECK_EQ(RETRIEVING_CAPABILITIES, state_); | 392 DCHECK_EQ(RETRIEVING_CAPABILITIES, state_); |
313 | 393 |
314 supported_formats_ = formats; | 394 supported_formats_ = formats; |
315 if (!FindBestFormatWithConstraints(supported_formats_, ¤t_format_, | 395 if (!FindBestFormatWithConstraints(supported_formats_, |
316 ¤t_constraints_)) { | 396 ¤t_format_, |
397 &frame_output_size_, | |
398 ¤t_constraints_)) { | |
317 FinalizeAddTrack(); | 399 FinalizeAddTrack(); |
318 SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); | 400 SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); |
319 return; | 401 return; |
320 } | 402 } |
321 | 403 |
322 state_ = STARTING; | 404 state_ = STARTING; |
323 DVLOG(3) << "Starting the capturer with" | 405 DVLOG(3) << "Starting the capturer with" |
324 << " width = " << current_format_.frame_size.width() | 406 << " width = " << current_format_.frame_size.width() |
325 << " height = " << current_format_.frame_size.height() | 407 << " height = " << current_format_.frame_size.height() |
326 << " frame rate = " << current_format_.frame_rate; | 408 << " frame rate = " << current_format_.frame_rate; |
327 | 409 |
328 media::VideoCaptureParams params; | 410 media::VideoCaptureParams params; |
329 params.requested_format = current_format_; | 411 params.requested_format = current_format_; |
330 StartSourceImpl(params); | 412 StartSourceImpl(params); |
331 } | 413 } |
332 | 414 |
333 bool MediaStreamVideoSource::FindBestFormatWithConstraints( | 415 bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
334 const media::VideoCaptureFormats& formats, | 416 const media::VideoCaptureFormats& formats, |
335 media::VideoCaptureFormat* best_format, | 417 media::VideoCaptureFormat* best_format, |
418 gfx::Size* frame_output_size, | |
336 blink::WebMediaConstraints* resulting_constraints) { | 419 blink::WebMediaConstraints* resulting_constraints) { |
337 // Find the first constraints that we can fulfilled. | 420 // Find the first constraints that we can fulfill. |
338 for (std::vector<RequestedConstraints>::iterator request_it = | 421 for (std::vector<RequestedConstraints>::iterator request_it = |
339 requested_constraints_.begin(); | 422 requested_constraints_.begin(); |
340 request_it != requested_constraints_.end(); ++request_it) { | 423 request_it != requested_constraints_.end(); ++request_it) { |
341 const blink::WebMediaConstraints& requested_constraints = | 424 const blink::WebMediaConstraints& requested_constraints = |
342 request_it->constraints; | 425 request_it->constraints; |
343 | 426 |
344 media::VideoCaptureFormats filtered_formats = | 427 media::VideoCaptureFormats filtered_formats = |
345 FilterFormats(requested_constraints, formats); | 428 FilterFormats(requested_constraints, formats); |
346 if (filtered_formats.size() > 0) { | 429 if (filtered_formats.size() > 0) { |
347 // A request with constraints that can be fulfilled. | 430 // A request with constraints that can be fulfilled. |
348 *best_format = GetBestCaptureFormat(filtered_formats); | 431 GetBestCaptureFormat(filtered_formats, |
432 requested_constraints, | |
433 best_format, | |
434 frame_output_size); | |
349 *resulting_constraints= requested_constraints; | 435 *resulting_constraints= requested_constraints; |
350 return true; | 436 return true; |
351 } | 437 } |
352 } | 438 } |
353 return false; | 439 return false; |
354 } | 440 } |
355 | 441 |
356 void MediaStreamVideoSource::OnStartDone(bool success) { | 442 void MediaStreamVideoSource::OnStartDone(bool success) { |
357 DCHECK(CalledOnValidThread()); | 443 DCHECK(CalledOnValidThread()); |
358 DVLOG(3) << "OnStartDone({success =" << success << "})"; | 444 DVLOG(3) << "OnStartDone({success =" << success << "})"; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
396 MediaStreamVideoSource::RequestedConstraints::RequestedConstraints( | 482 MediaStreamVideoSource::RequestedConstraints::RequestedConstraints( |
397 const blink::WebMediaConstraints& constraints, | 483 const blink::WebMediaConstraints& constraints, |
398 const ConstraintsCallback& callback) | 484 const ConstraintsCallback& callback) |
399 : constraints(constraints), callback(callback) { | 485 : constraints(constraints), callback(callback) { |
400 } | 486 } |
401 | 487 |
402 MediaStreamVideoSource::RequestedConstraints::~RequestedConstraints() { | 488 MediaStreamVideoSource::RequestedConstraints::~RequestedConstraints() { |
403 } | 489 } |
404 | 490 |
405 } // namespace content | 491 } // namespace content |
OLD | NEW |