forked from crosswalk-project/chromium-crosswalk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement all resolution change policies for desktop and tab capture.
Prior to this change, desktop/window capture would emit frames of any size, regardless of the resolution change policy; and tab capture would always emit frames of a fixed resolution. Both cause a problem where some receivers of the captured video need to do scaling/letterboxing themselves for proper UX, and other receivers require this be done on by the capture pipeline. This change builds upon https://codereview.chromium.org/1124263004, which allows the client to specify capture constraints that configure the capture pipeline to do (or not do) the scaling/letterboxing. All frame resolution calculation logic has been factored into one place in a new CaptureResolutionChooser class, which also depends on new utility functions added to media/base/video_util.*. BUG=473336 Review URL: https://codereview.chromium.org/1135823004 Cr-Commit-Position: refs/heads/master@{#330232} (cherry picked from commit 39254e6) Review URL: https://codereview.chromium.org/1133093005 Cr-Commit-Position: refs/branch-heads/2403@{crosswalk-project#22} Cr-Branched-From: f54b809-refs/heads/master@{#330231}
- Loading branch information
1 parent
b37fa6f
commit 78b9ca2
Showing
18 changed files
with
972 additions
and
246 deletions.
There are no files selected for viewing
120 changes: 120 additions & 0 deletions
120
content/browser/media/capture/capture_resolution_chooser.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "content/browser/media/capture/capture_resolution_chooser.h" | ||
|
||
#include "media/base/limits.h" | ||
#include "media/base/video_util.h" | ||
|
||
namespace content { | ||
|
||
namespace { | ||
|
||
// Compute the minimum frame size from the given |max_frame_size| and | ||
// |resolution_change_policy|. | ||
gfx::Size ComputeMinimumCaptureSize( | ||
const gfx::Size& max_frame_size, | ||
media::ResolutionChangePolicy resolution_change_policy) { | ||
switch (resolution_change_policy) { | ||
case media::RESOLUTION_POLICY_FIXED_RESOLUTION: | ||
return max_frame_size; | ||
case media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO: { | ||
// TODO(miu): This is a place-holder until "min constraints" are plumbed- | ||
// in from the MediaStream framework. http://crbug.com/473336 | ||
const int kMinLines = 180; | ||
if (max_frame_size.height() <= kMinLines) | ||
return max_frame_size; | ||
const gfx::Size result( | ||
kMinLines * max_frame_size.width() / max_frame_size.height(), | ||
kMinLines); | ||
if (result.width() <= 0 || result.width() > media::limits::kMaxDimension) | ||
return max_frame_size; | ||
return result; | ||
} | ||
case media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT: | ||
return gfx::Size(1, 1); | ||
case media::RESOLUTION_POLICY_LAST: | ||
break; | ||
} | ||
NOTREACHED(); | ||
return gfx::Size(1, 1); | ||
} | ||
|
||
// Returns |size|, unless it exceeds |max_size| or is under |min_size|. When | ||
// the bounds are exceeded, computes and returns an alternate size of similar | ||
// aspect ratio that is within the bounds. | ||
gfx::Size ComputeBoundedCaptureSize(const gfx::Size& size, | ||
const gfx::Size& min_size, | ||
const gfx::Size& max_size) { | ||
if (size.width() > max_size.width() || size.height() > max_size.height()) { | ||
gfx::Size result = media::ScaleSizeToFitWithinTarget(size, max_size); | ||
result.SetToMax(min_size); | ||
return result; | ||
} else if (size.width() < min_size.width() || | ||
size.height() < min_size.height()) { | ||
gfx::Size result = media::ScaleSizeToEncompassTarget(size, min_size); | ||
result.SetToMin(max_size); | ||
return result; | ||
} else { | ||
return size; | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
CaptureResolutionChooser::CaptureResolutionChooser( | ||
const gfx::Size& max_frame_size, | ||
media::ResolutionChangePolicy resolution_change_policy) | ||
: max_frame_size_(max_frame_size), | ||
min_frame_size_(ComputeMinimumCaptureSize(max_frame_size, | ||
resolution_change_policy)), | ||
resolution_change_policy_(resolution_change_policy), | ||
constrained_size_(max_frame_size) { | ||
DCHECK_LT(0, max_frame_size_.width()); | ||
DCHECK_LT(0, max_frame_size_.height()); | ||
DCHECK_LE(min_frame_size_.width(), max_frame_size_.width()); | ||
DCHECK_LE(min_frame_size_.height(), max_frame_size_.height()); | ||
|
||
RecomputeCaptureSize(); | ||
} | ||
|
||
CaptureResolutionChooser::~CaptureResolutionChooser() {} | ||
|
||
void CaptureResolutionChooser::SetSourceSize(const gfx::Size& source_size) { | ||
if (source_size.IsEmpty()) | ||
return; | ||
|
||
switch (resolution_change_policy_) { | ||
case media::RESOLUTION_POLICY_FIXED_RESOLUTION: | ||
// Source size changes do not affect the frame resolution. Frame | ||
// resolution is always fixed to |max_frame_size_|. | ||
break; | ||
|
||
case media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO: | ||
constrained_size_ = ComputeBoundedCaptureSize( | ||
media::PadToMatchAspectRatio(source_size, max_frame_size_), | ||
min_frame_size_, | ||
max_frame_size_); | ||
RecomputeCaptureSize(); | ||
break; | ||
|
||
case media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT: | ||
constrained_size_ = ComputeBoundedCaptureSize( | ||
source_size, min_frame_size_, max_frame_size_); | ||
RecomputeCaptureSize(); | ||
break; | ||
|
||
case media::RESOLUTION_POLICY_LAST: | ||
NOTREACHED(); | ||
} | ||
} | ||
|
||
void CaptureResolutionChooser::RecomputeCaptureSize() { | ||
// TODO(miu): An upcoming change will introduce the ability to find the best | ||
// capture resolution, given the current capabilities of the system. | ||
// http://crbug.com/156767 | ||
capture_size_ = constrained_size_; | ||
} | ||
|
||
} // namespace content |
59 changes: 59 additions & 0 deletions
59
content/browser/media/capture/capture_resolution_chooser.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_CAPTURE_RESOLUTION_CHOOSER_H_ | ||
#define CONTENT_BROWSER_MEDIA_CAPTURE_CAPTURE_RESOLUTION_CHOOSER_H_ | ||
|
||
#include "content/common/content_export.h" | ||
#include "media/base/video_capture_types.h" | ||
#include "ui/gfx/geometry/size.h" | ||
|
||
namespace content { | ||
|
||
// Encapsulates the logic that determines the capture frame resolution based on: | ||
// 1. The configured maximum frame resolution and resolution change policy. | ||
// 2. The resolution of the source content. | ||
// 3. The current capabilities of the end-to-end system, in terms of the | ||
// maximum number of pixels per frame. | ||
class CONTENT_EXPORT CaptureResolutionChooser { | ||
public: | ||
// media::ResolutionChangePolicy determines whether the variable frame | ||
// resolutions being computed must adhere to a fixed aspect ratio or not, or | ||
// that there must only be a single fixed resolution. | ||
CaptureResolutionChooser( | ||
const gfx::Size& max_frame_size, | ||
media::ResolutionChangePolicy resolution_change_policy); | ||
~CaptureResolutionChooser(); | ||
|
||
// Returns the current capture frame resolution to use. | ||
gfx::Size capture_size() const { | ||
return capture_size_; | ||
} | ||
|
||
// Updates the capture size based on a change in the resolution of the source | ||
// content. | ||
void SetSourceSize(const gfx::Size& source_size); | ||
|
||
private: | ||
// Called after any update that requires |capture_size_| be re-computed. | ||
void RecomputeCaptureSize(); | ||
|
||
// Hard constraints. | ||
const gfx::Size max_frame_size_; | ||
const gfx::Size min_frame_size_; // Computed from the ctor arguments. | ||
|
||
// Specifies the set of heuristics to use. | ||
const media::ResolutionChangePolicy resolution_change_policy_; | ||
|
||
// The capture frame resolution to use, ignoring the limitations imposed by | ||
// the capability metric. | ||
gfx::Size constrained_size_; | ||
|
||
// The current computed capture frame resolution. | ||
gfx::Size capture_size_; | ||
}; | ||
|
||
} // namespace content | ||
|
||
#endif // CONTENT_BROWSER_MEDIA_CAPTURE_RESOLUTION_CHOOSER_H_ |
169 changes: 169 additions & 0 deletions
169
content/browser/media/capture/capture_resolution_chooser_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// Copyright 2015 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "content/browser/media/capture/capture_resolution_chooser.h" | ||
|
||
#include "base/location.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
using tracked_objects::Location; | ||
|
||
namespace content { | ||
|
||
namespace { | ||
|
||
// 16:9 maximum and minimum frame sizes. | ||
const int kMaxFrameWidth = 3840; | ||
const int kMaxFrameHeight = 2160; | ||
const int kMinFrameWidth = 320; | ||
const int kMinFrameHeight = 180; | ||
|
||
// Checks whether |size| is strictly between (inclusive) |min_size| and | ||
// |max_size| and has the same aspect ratio as |max_size|. | ||
void ExpectIsWithinBoundsAndSameAspectRatio(const Location& location, | ||
const gfx::Size& min_size, | ||
const gfx::Size& max_size, | ||
const gfx::Size& size) { | ||
SCOPED_TRACE(::testing::Message() << "From here: " << location.ToString()); | ||
EXPECT_LE(min_size.width(), size.width()); | ||
EXPECT_LE(min_size.height(), size.height()); | ||
EXPECT_GE(max_size.width(), size.width()); | ||
EXPECT_GE(max_size.height(), size.height()); | ||
EXPECT_NEAR(static_cast<double>(max_size.width()) / max_size.height(), | ||
static_cast<double>(size.width()) / size.height(), | ||
0.01); | ||
} | ||
|
||
} // namespace | ||
|
||
TEST(CaptureResolutionChooserTest, | ||
FixedResolutionPolicy_CaptureSizeAlwaysFixed) { | ||
const gfx::Size the_one_frame_size(kMaxFrameWidth, kMaxFrameHeight); | ||
CaptureResolutionChooser chooser(the_one_frame_size, | ||
media::RESOLUTION_POLICY_FIXED_RESOLUTION); | ||
EXPECT_EQ(the_one_frame_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(the_one_frame_size); | ||
EXPECT_EQ(the_one_frame_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth + 424, kMaxFrameHeight - 101)); | ||
EXPECT_EQ(the_one_frame_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth - 202, kMaxFrameHeight + 56)); | ||
EXPECT_EQ(the_one_frame_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMinFrameWidth, kMinFrameHeight)); | ||
EXPECT_EQ(the_one_frame_size, chooser.capture_size()); | ||
} | ||
|
||
TEST(CaptureResolutionChooserTest, | ||
FixedAspectRatioPolicy_CaptureSizeHasSameAspectRatio) { | ||
CaptureResolutionChooser chooser( | ||
gfx::Size(kMaxFrameWidth, kMaxFrameHeight), | ||
media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO); | ||
|
||
// Starting condition. | ||
const gfx::Size min_size(kMinFrameWidth, kMinFrameHeight); | ||
const gfx::Size max_size(kMaxFrameWidth, kMaxFrameHeight); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
// Max size in --> max size out. | ||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth, kMaxFrameHeight)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
// Various source sizes within bounds. | ||
chooser.SetSourceSize(gfx::Size(640, 480)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(480, 640)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(640, 640)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
// Bad source size results in no update. | ||
const gfx::Size unchanged_size = chooser.capture_size(); | ||
chooser.SetSourceSize(gfx::Size(0, 0)); | ||
EXPECT_EQ(unchanged_size, chooser.capture_size()); | ||
|
||
// Downscaling size (preserving aspect ratio) when source size exceeds the | ||
// upper bounds. | ||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth * 2, kMaxFrameHeight * 2)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth * 2, kMaxFrameHeight)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth, kMaxFrameHeight * 2)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
// Upscaling size (preserving aspect ratio) when source size is under the | ||
// lower bounds. | ||
chooser.SetSourceSize(gfx::Size(kMinFrameWidth / 2, kMinFrameHeight / 2)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMinFrameWidth / 2, kMaxFrameHeight)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMinFrameWidth, kMinFrameHeight / 2)); | ||
ExpectIsWithinBoundsAndSameAspectRatio( | ||
FROM_HERE, min_size, max_size, chooser.capture_size()); | ||
} | ||
|
||
TEST(CaptureResolutionChooserTest, | ||
AnyWithinLimitPolicy_CaptureSizeIsAnythingWithinLimits) { | ||
const gfx::Size max_size(kMaxFrameWidth, kMaxFrameHeight); | ||
CaptureResolutionChooser chooser( | ||
max_size, media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT); | ||
|
||
// Starting condition. | ||
EXPECT_EQ(max_size, chooser.capture_size()); | ||
|
||
// Max size in --> max size out. | ||
chooser.SetSourceSize(max_size); | ||
EXPECT_EQ(max_size, chooser.capture_size()); | ||
|
||
// Various source sizes within bounds. | ||
chooser.SetSourceSize(gfx::Size(640, 480)); | ||
EXPECT_EQ(gfx::Size(640, 480), chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(480, 640)); | ||
EXPECT_EQ(gfx::Size(480, 640), chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(640, 640)); | ||
EXPECT_EQ(gfx::Size(640, 640), chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(2, 2)); | ||
EXPECT_EQ(gfx::Size(2, 2), chooser.capture_size()); | ||
|
||
// Bad source size results in no update. | ||
const gfx::Size unchanged_size = chooser.capture_size(); | ||
chooser.SetSourceSize(gfx::Size(0, 0)); | ||
EXPECT_EQ(unchanged_size, chooser.capture_size()); | ||
|
||
// Downscaling size (preserving aspect ratio) when source size exceeds the | ||
// upper bounds. | ||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth * 2, kMaxFrameHeight * 2)); | ||
EXPECT_EQ(max_size, chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth * 2, kMaxFrameHeight)); | ||
EXPECT_EQ(gfx::Size(kMaxFrameWidth, kMaxFrameHeight / 2), | ||
chooser.capture_size()); | ||
|
||
chooser.SetSourceSize(gfx::Size(kMaxFrameWidth, kMaxFrameHeight * 2)); | ||
EXPECT_EQ(gfx::Size(kMaxFrameWidth / 2, kMaxFrameHeight), | ||
chooser.capture_size()); | ||
} | ||
|
||
} // namespace content |
Oops, something went wrong.