Chromium Code Reviews| Index: content/renderer/media/media_stream_video_source.cc |
| diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc |
| index 118d66e3cddc59faa72ed1fd4c7448c0bc0c377e..555730a0966fa1590ba4ddd254ce11f3d710d76b 100644 |
| --- a/content/renderer/media/media_stream_video_source.cc |
| +++ b/content/renderer/media/media_stream_video_source.cc |
| @@ -11,6 +11,7 @@ |
| #include "base/strings/string_number_conversions.h" |
| #include "content/renderer/media/media_stream_dependency_factory.h" |
| #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" |
| +#include "third_party/libyuv/include/libyuv/convert.h" |
| namespace content { |
| @@ -36,6 +37,10 @@ const char kSourceId[] = "sourceId"; |
| // are unknown. |
| const char kGooglePrefix[] = "goog"; |
| +// MediaStreamVideoSource support cropping of video frames but only up to |
|
Jói
2014/02/27 14:52:25
support -> supports
perkj_chrome
2014/03/01 12:32:59
Done.
|
| +// kMaxCropFactor. |
| +const int kMaxCropFactor = 2; |
| + |
| // Returns true if |constraint| is fulfilled. |format| can be changed |
| // changed by a constraint. Ie - the frame rate can be changed by setting |
| // maxFrameRate. |
| @@ -96,11 +101,11 @@ bool UpdateFormatForConstraint( |
| if (constraint_name == MediaStreamVideoSource::kMinWidth) { |
| return (value <= format->frame_size.width()); |
| } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) { |
| - return (value >= format->frame_size.width()); |
| + return (value * kMaxCropFactor >= format->frame_size.width()); |
| } else if (constraint_name == MediaStreamVideoSource::kMinHeight) { |
| return (value <= format->frame_size.height()); |
| } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) { |
| - return (value >= format->frame_size.height()); |
| + return (value * kMaxCropFactor >= format->frame_size.height()); |
| } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) { |
| return (value <= format->frame_rate); |
| } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) { |
| @@ -185,25 +190,43 @@ media::VideoCaptureFormats FilterFormats( |
| return candidates; |
| } |
| -// Find the format that best matches the default video size. |
| -// This algorithm is chosen since a resolution must be picked even if no |
| -// constraints are provided. We don't just select the maximum supported |
| -// resolution since higher resolution cost more in terms of complexity and |
| -// many cameras perform worse at its maximum supported resolution. |
| -const media::VideoCaptureFormat& GetBestCaptureFormat( |
| - const media::VideoCaptureFormats& formats) { |
| - DCHECK(!formats.empty()); |
| +// Retrieve the desired max width and height from |constraints|. |
| +void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints, |
| + int* dw, int* dh) { |
| + bool mandatory_found = false; |
| + blink::WebString width; |
| + if (constraints.getMandatoryConstraintValue( |
| + MediaStreamVideoSource::kMaxWidth, width)) { |
| + base::StringToInt(width.utf8(), dw); |
| + mandatory_found = true; |
| + } |
| + blink::WebString height; |
| + if (constraints.getMandatoryConstraintValue( |
| + MediaStreamVideoSource::kMaxHeight, height)) { |
| + base::StringToInt(height.utf8(), dh); |
| + mandatory_found = true; |
| + } |
| + if (mandatory_found) |
| + return; |
| - int default_area = |
| - MediaStreamVideoSource::kDefaultWidth * |
| - MediaStreamVideoSource::kDefaultHeight; |
| + if (constraints.getOptionalConstraintValue( |
| + MediaStreamVideoSource::kMaxWidth, width)) { |
| + base::StringToInt(width.utf8(), dw); |
| + } |
| + if (constraints.getOptionalConstraintValue( |
| + MediaStreamVideoSource::kMaxHeight, height)) { |
| + base::StringToInt(height.utf8(), dh); |
| + } |
| +} |
| +const media::VideoCaptureFormat& GetBestFormatBasedOnArea( |
| + const media::VideoCaptureFormats& formats, |
| + int area) { |
| media::VideoCaptureFormats::const_iterator it = formats.begin(); |
| media::VideoCaptureFormats::const_iterator best_it = formats.begin(); |
| int best_diff = std::numeric_limits<int>::max(); |
| for (; it != formats.end(); ++it) { |
| - int diff = abs(default_area - |
| - it->frame_size.width() * it->frame_size.height()); |
| + int diff = abs(area - it->frame_size.width() * it->frame_size.height()); |
| if (diff < best_diff) { |
| best_diff = diff; |
| best_it = it; |
| @@ -212,6 +235,73 @@ const media::VideoCaptureFormat& GetBestCaptureFormat( |
| return *best_it; |
| } |
| +// Find the format that best matches the default video size. |
| +// This algorithm is chosen since a resolution must be picked even if no |
| +// constraints are provided. We don't just select the maximum supported |
| +// resolution since higher resolution cost more in terms of complexity and |
| +// many cameras perform worse at its maximum supported resolution. |
| +void GetBestCaptureFormat( |
| + const media::VideoCaptureFormats& formats, |
| + const blink::WebMediaConstraints& constraints, |
| + media::VideoCaptureFormat* capture_format, |
| + gfx::Size* frame_output_size) { |
| + DCHECK(!formats.empty()); |
| + DCHECK(frame_output_size); |
| + |
| + int max_width = std::numeric_limits<int>::max(); |
| + int max_height = std::numeric_limits<int>::max();; |
| + GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height); |
| + |
| + *capture_format = GetBestFormatBasedOnArea( |
|
Jói
2014/02/27 14:52:25
It seems this statement will never choose a captur
perkj_chrome
2014/03/01 12:32:59
Its intentional that we choose a size that we supp
Jói
2014/03/03 13:55:15
OK, thanks, I see the format filtering now. Still
|
| + formats, |
| + std::min(max_width, MediaStreamVideoSource::kDefaultWidth) * |
| + std::min(max_height, MediaStreamVideoSource::kDefaultHeight)); |
| + |
| + *frame_output_size = capture_format->frame_size; |
| + if (max_width < frame_output_size->width()) |
| + frame_output_size->set_width(max_width); |
| + if (max_height < frame_output_size->height()) |
| + frame_output_size->set_height(max_height); |
| +} |
| + |
| +scoped_refptr<media::VideoFrame> CreateCroppedFrame( |
| + const scoped_refptr<media::VideoFrame>& src, |
| + const scoped_refptr<media::VideoFrame>& dst) { |
| + const int src_width = src->coded_size().width(); |
| + const int src_height = src->coded_size().height(); |
| + const int dst_width = dst->coded_size().width(); |
| + const int dst_height = dst->coded_size().height(); |
| + DCHECK(src_width >= dst_width); |
|
Jói
2014/02/27 14:52:25
Why not > rather than >=, to make sure we're not c
perkj_chrome
2014/03/01 12:32:59
Done.
|
| + DCHECK(src_height >= dst_height); |
| + |
| + const int horiz_crop = ((src_width - dst_width) / 2) & ~1; |
| + const int vert_crop = ((src_height - dst_height) / 2) & ~1; |
| + |
| + const uint8* src_y = src->data(media::VideoFrame::kYPlane) + |
| + (src_width * vert_crop + horiz_crop); |
| + const int halfwidth = (src_width + 1) / 2; |
| + const uint8* src_u = src->data(media::VideoFrame::kUPlane) + |
| + (halfwidth * vert_crop + horiz_crop) / 2; |
| + const uint8* src_v = src->data(media::VideoFrame::kVPlane) + |
| + (halfwidth * vert_crop + horiz_crop) / 2; |
| + |
| + libyuv::I420Copy(src_y, |
| + src->stride(media::VideoFrame::kYPlane), |
| + src_u, |
| + src->stride(media::VideoFrame::kUPlane), |
| + src_v, |
| + src->stride(media::VideoFrame::kVPlane), |
| + dst->data(media::VideoFrame::kYPlane), |
| + dst->stride(media::VideoFrame::kYPlane), |
| + dst->data(media::VideoFrame::kUPlane), |
| + dst->stride(media::VideoFrame::kUPlane), |
| + dst->data(media::VideoFrame::kVPlane), |
| + dst->stride(media::VideoFrame::kVPlane), |
| + dst_width, |
| + dst_height); |
| + return dst; |
| +} |
| + |
| } // anonymous namespace |
| MediaStreamVideoSource::MediaStreamVideoSource( |
| @@ -255,7 +345,7 @@ void MediaStreamVideoSource::AddTrack( |
| case STARTING: |
| case RETRIEVING_CAPABILITIES: { |
| // The |callback| will be triggered once the delegate has started or |
| - // the capabilitites has been retrieved. |
| + // the capabilities has been retrieved. |
| break; |
| } |
| case ENDED: |
| @@ -302,8 +392,23 @@ void MediaStreamVideoSource::DoStopSource() { |
| void MediaStreamVideoSource::DeliverVideoFrame( |
| const scoped_refptr<media::VideoFrame>& frame) { |
| - if (capture_adapter_) |
| - capture_adapter_->OnFrameCaptured(frame); |
| + scoped_refptr<media::VideoFrame> video_frame(frame); |
| + |
| + if (frame->format() == media::VideoFrame::I420 || |
| + frame->format() == media::VideoFrame::YV12) { |
| + if (frame->coded_size() != frame_output_size_) { |
| + // Cropping is needed. |
| + gfx::Size size = frame_output_size_; |
| + size.SetToMin(frame->coded_size()); |
| + scoped_refptr<media::VideoFrame> dst = frame_pool_.CreateFrame( |
| + media::VideoFrame::I420, size, gfx::Rect(size), size, |
| + frame->GetTimestamp()); |
| + video_frame = CreateCroppedFrame(frame, dst); |
| + } |
| + |
| + if (capture_adapter_) |
| + capture_adapter_->OnFrameCaptured(video_frame); |
| + } |
| } |
| void MediaStreamVideoSource::OnSupportedFormats( |
| @@ -312,8 +417,10 @@ void MediaStreamVideoSource::OnSupportedFormats( |
| DCHECK_EQ(RETRIEVING_CAPABILITIES, state_); |
| supported_formats_ = formats; |
| - if (!FindBestFormatWithConstraints(supported_formats_, ¤t_format_, |
| - ¤t_constraints_)) { |
| + if (!FindBestFormatWithConstraints(supported_formats_, |
| + ¤t_format_, |
| + &frame_output_size_, |
| + ¤t_constraints_)) { |
| FinalizeAddTrack(); |
| SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded); |
| return; |
| @@ -328,13 +435,16 @@ void MediaStreamVideoSource::OnSupportedFormats( |
| media::VideoCaptureParams params; |
| params.requested_format = current_format_; |
| StartSourceImpl(params); |
| + |
| + |
| } |
| bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
| const media::VideoCaptureFormats& formats, |
| media::VideoCaptureFormat* best_format, |
| + gfx::Size* frame_output_size, |
| blink::WebMediaConstraints* resulting_constraints) { |
| - // Find the first constraints that we can fulfilled. |
| + // Find the first constraints that we can fulfill. |
| for (std::vector<RequestedConstraints>::iterator request_it = |
| requested_constraints_.begin(); |
| request_it != requested_constraints_.end(); ++request_it) { |
| @@ -345,7 +455,10 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints( |
| FilterFormats(requested_constraints, formats); |
| if (filtered_formats.size() > 0) { |
| // A request with constraints that can be fulfilled. |
| - *best_format = GetBestCaptureFormat(filtered_formats); |
| + GetBestCaptureFormat(filtered_formats, |
| + requested_constraints, |
| + best_format, |
| + frame_output_size); |
| *resulting_constraints= requested_constraints; |
| return true; |
| } |