mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-19 16:47:50 +01:00
Add scale and compare methods to VideoFrame::UpdateRect
Add tests for different UpdateRect methods as they are no longer trivial This change will enable providing useful update rects after scaling is done. Bug: webrtc:11058 Change-Id: I2311dbbbb5eca5cfaf845306674e6890050f80c6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/159820 Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Stefan Holmer <stefan@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29835}
This commit is contained in:
parent
332274dfef
commit
0660ceef0e
3 changed files with 264 additions and 0 deletions
|
@ -60,6 +60,103 @@ bool VideoFrame::UpdateRect::IsEmpty() const {
|
|||
return width == 0 && height == 0;
|
||||
}
|
||||
|
||||
VideoFrame::UpdateRect VideoFrame::UpdateRect::ScaleWithFrame(
|
||||
int frame_width,
|
||||
int frame_height,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height) const {
|
||||
RTC_DCHECK_GT(frame_width, 0);
|
||||
RTC_DCHECK_GT(frame_height, 0);
|
||||
|
||||
RTC_DCHECK_GT(crop_width, 0);
|
||||
RTC_DCHECK_GT(crop_height, 0);
|
||||
|
||||
RTC_DCHECK_LE(crop_width + crop_x, frame_width);
|
||||
RTC_DCHECK_LE(crop_height + crop_y, frame_height);
|
||||
|
||||
RTC_DCHECK_GT(scaled_width, 0);
|
||||
RTC_DCHECK_GT(scaled_height, 0);
|
||||
|
||||
// Check if update rect is out of the cropped area.
|
||||
if (offset_x + width < crop_x || offset_x > crop_x + crop_width ||
|
||||
offset_y + height < crop_y || offset_y > crop_y + crop_width) {
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
int x = offset_x - crop_x;
|
||||
int w = width;
|
||||
if (x < 0) {
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
int y = offset_y - crop_y;
|
||||
int h = height;
|
||||
if (y < 0) {
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// Lower corner is rounded down.
|
||||
x = x * scaled_width / crop_width;
|
||||
y = y * scaled_height / crop_height;
|
||||
// Upper corner is rounded up.
|
||||
w = (w * scaled_width + crop_width - 1) / crop_width;
|
||||
h = (h * scaled_height + crop_height - 1) / crop_height;
|
||||
|
||||
// Round to full 2x2 blocks due to possible subsampling in the pixel data.
|
||||
if (x % 2) {
|
||||
--x;
|
||||
++w;
|
||||
}
|
||||
if (y % 2) {
|
||||
--y;
|
||||
++h;
|
||||
}
|
||||
if (w % 2) {
|
||||
++w;
|
||||
}
|
||||
if (h % 2) {
|
||||
++h;
|
||||
}
|
||||
|
||||
// Expand the update rect by 2 pixels in each direction to include any
|
||||
// possible scaling artifacts.
|
||||
if (scaled_width != crop_width || scaled_height != crop_height) {
|
||||
if (x > 0) {
|
||||
x -= 2;
|
||||
w += 2;
|
||||
}
|
||||
if (y > 0) {
|
||||
y -= 2;
|
||||
h += 2;
|
||||
}
|
||||
w += 2;
|
||||
h += 2;
|
||||
}
|
||||
|
||||
// Ensure update rect is inside frame dimensions.
|
||||
if (x + w > scaled_width) {
|
||||
w = scaled_width - x;
|
||||
}
|
||||
if (y + h > scaled_height) {
|
||||
h = scaled_height - y;
|
||||
}
|
||||
RTC_DCHECK_GE(w, 0);
|
||||
RTC_DCHECK_GE(h, 0);
|
||||
if (w == 0 || h == 0) {
|
||||
w = 0;
|
||||
h = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
return {x, y, w, h};
|
||||
}
|
||||
|
||||
VideoFrame::Builder::Builder() = default;
|
||||
|
||||
VideoFrame::Builder::~Builder() = default;
|
||||
|
|
|
@ -49,6 +49,31 @@ class RTC_EXPORT VideoFrame {
|
|||
void MakeEmptyUpdate();
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
||||
// Per-member equality check. Empty rectangles with different offsets would
|
||||
// be considered different.
|
||||
bool operator==(const UpdateRect& other) const {
|
||||
return other.offset_x == offset_x && other.offset_y == offset_y &&
|
||||
other.width == width && other.height == height;
|
||||
}
|
||||
|
||||
bool operator!=(const UpdateRect& other) const { return !(*this == other); }
|
||||
|
||||
// Scales update_rect given original frame dimensions.
|
||||
// Cropping is applied first, then rect is scaled down.
|
||||
// Update rect is snapped to 2x2 grid due to possible UV subsampling and
|
||||
// then expanded by additional 2 pixels in each direction to accommodate any
|
||||
// possible scaling artifacts.
|
||||
// Note, close but not equal update_rects on original frame may result in
|
||||
// the same scaled update rects.
|
||||
UpdateRect ScaleWithFrame(int frame_width,
|
||||
int frame_height,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height) const;
|
||||
};
|
||||
|
||||
// Interface for accessing elements of the encoded frame that was the base for
|
||||
|
|
|
@ -551,4 +551,146 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
::testing::Values(VideoFrameBuffer::Type::kI420,
|
||||
VideoFrameBuffer::Type::kI010)));
|
||||
|
||||
TEST(TestUpdateRect, CanCompare) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 100, 200};
|
||||
VideoFrame::UpdateRect b = {0, 0, 100, 200};
|
||||
VideoFrame::UpdateRect c = {1, 0, 100, 200};
|
||||
VideoFrame::UpdateRect d = {0, 1, 100, 200};
|
||||
EXPECT_TRUE(a == b);
|
||||
EXPECT_FALSE(a == c);
|
||||
EXPECT_FALSE(a == d);
|
||||
}
|
||||
|
||||
TEST(TestUpdateRect, ComputesIsEmpty) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 0, 0};
|
||||
VideoFrame::UpdateRect b = {0, 0, 100, 200};
|
||||
VideoFrame::UpdateRect c = {1, 100, 0, 0};
|
||||
VideoFrame::UpdateRect d = {1, 100, 100, 200};
|
||||
EXPECT_TRUE(a.IsEmpty());
|
||||
EXPECT_FALSE(b.IsEmpty());
|
||||
EXPECT_TRUE(c.IsEmpty());
|
||||
EXPECT_FALSE(d.IsEmpty());
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectUnion, NonIntersecting) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 10, 20};
|
||||
VideoFrame::UpdateRect b = {100, 200, 10, 20};
|
||||
a.Union(b);
|
||||
EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 110, 220}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectUnion, Intersecting) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 10, 10};
|
||||
VideoFrame::UpdateRect b = {5, 5, 30, 20};
|
||||
a.Union(b);
|
||||
EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 35, 25}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectUnion, OneInsideAnother) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 100, 100};
|
||||
VideoFrame::UpdateRect b = {5, 5, 30, 20};
|
||||
a.Union(b);
|
||||
EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 100, 100}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectIntersect, NonIntersecting) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 10, 20};
|
||||
VideoFrame::UpdateRect b = {100, 200, 10, 20};
|
||||
a.Intersect(b);
|
||||
EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectIntersect, Intersecting) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 10, 10};
|
||||
VideoFrame::UpdateRect b = {5, 5, 30, 20};
|
||||
a.Intersect(b);
|
||||
EXPECT_EQ(a, VideoFrame::UpdateRect({5, 5, 5, 5}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectIntersect, OneInsideAnother) {
|
||||
VideoFrame::UpdateRect a = {0, 0, 100, 100};
|
||||
VideoFrame::UpdateRect b = {5, 5, 30, 20};
|
||||
a.Intersect(b);
|
||||
EXPECT_EQ(a, VideoFrame::UpdateRect({5, 5, 30, 20}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, NoScale) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {100, 50, 100, 200};
|
||||
VideoFrame::UpdateRect scaled =
|
||||
a.ScaleWithFrame(width, height, 0, 0, width, height, width, height);
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({100, 50, 100, 200}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, CropOnly) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {100, 50, 100, 200};
|
||||
VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
|
||||
width, height, 10, 10, width - 20, height - 20, width - 20, height - 20);
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({90, 40, 100, 200}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, CropOnlyToOddOffset) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {100, 50, 100, 200};
|
||||
VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
|
||||
width, height, 5, 5, width - 10, height - 10, width - 10, height - 10);
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({94, 44, 102, 202}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, ScaleByHalf) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {100, 60, 100, 200};
|
||||
VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
|
||||
width, height, 0, 0, width, height, width / 2, height / 2);
|
||||
// Scaled by half and +2 pixels in all directions.
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({48, 28, 54, 104}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, CropToUnchangedRegionBelowUpdateRect) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {100, 60, 100, 200};
|
||||
VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
|
||||
width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10);
|
||||
// Update is out of the cropped frame.
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, CropToUnchangedRegionAboveUpdateRect) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {600, 400, 10, 10};
|
||||
VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
|
||||
width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10);
|
||||
// Update is out of the cropped frame.
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, CropInsideUpdate) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {300, 200, 100, 100};
|
||||
VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
|
||||
width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10);
|
||||
// Cropped frame is inside the update rect.
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 10, 10}));
|
||||
}
|
||||
|
||||
TEST(TestUpdateRectScale, CropAndScaleByHalf) {
|
||||
const int width = 640;
|
||||
const int height = 480;
|
||||
VideoFrame::UpdateRect a = {100, 60, 100, 200};
|
||||
VideoFrame::UpdateRect scaled =
|
||||
a.ScaleWithFrame(width, height, 10, 10, width - 20, height - 20,
|
||||
(width - 20) / 2, (height - 20) / 2);
|
||||
// Scaled by half and +3 pixels in all directions, because of odd offset after
|
||||
// crop and scale.
|
||||
EXPECT_EQ(scaled, VideoFrame::UpdateRect({42, 22, 56, 106}));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
Loading…
Reference in a new issue