Skip to content
This repository was archived by the owner on Oct 25, 2024. It is now read-only.

Commit c79f1d8

Browse files
Ilya NikolaevskiyCommit Bot
authored andcommitted
Add scaling interface to VideoFrameBuffer
This can be overriden for kNative frame types to perform scaling efficiently. Default implementations for existing buffer types require actual buffer implementation, thus this CL also merges "video_frame" with "video_frame_I420" build targets. Bug: webrtc:11976, chromium:1132299 Change-Id: I3bf5f6bf179db5e7ab165b1c2301980043a08765 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/186303 Commit-Queue: Ilya Nikolaevskiy <[email protected]> Reviewed-by: Mirko Bonadei <[email protected]> Reviewed-by: Stefan Holmer <[email protected]> Reviewed-by: Sami Kalliomäki <[email protected]> Reviewed-by: Niels Moller <[email protected]> Reviewed-by: Evan Shrubsole <[email protected]> Cr-Commit-Position: refs/heads/master@{#32352}
1 parent 2d05878 commit c79f1d8

File tree

26 files changed

+162
-116
lines changed

26 files changed

+162
-116
lines changed

api/video/BUILD.gn

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ rtc_library("video_rtp_headers") {
4242
rtc_library("video_frame") {
4343
visibility = [ "*" ]
4444
sources = [
45+
"i420_buffer.cc",
46+
"i420_buffer.h",
4547
"video_codec_type.h",
4648
"video_frame.cc",
4749
"video_frame.h",
@@ -59,7 +61,9 @@ rtc_library("video_frame") {
5961
"..:scoped_refptr",
6062
"../../rtc_base:checks",
6163
"../../rtc_base:rtc_base_approved",
64+
"../../rtc_base/memory:aligned_malloc",
6265
"../../rtc_base/system:rtc_export",
66+
"//third_party/libyuv",
6367
]
6468
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
6569
}
@@ -70,42 +74,11 @@ if (is_android) {
7074
}
7175
}
7276

73-
rtc_source_set("recordable_encoded_frame") {
77+
# Deprecated empty target. Use "video_frame" instead.
78+
rtc_source_set("video_frame_i420") {
7479
visibility = [ "*" ]
75-
sources = [ "recordable_encoded_frame.h" ]
76-
77-
deps = [
78-
":encoded_image",
79-
":video_frame",
80-
":video_rtp_headers",
81-
"..:array_view",
82-
"..:scoped_refptr",
83-
"../../rtc_base:refcount",
84-
"../units:timestamp",
85-
]
86-
}
87-
88-
rtc_source_set("video_frame_type") {
89-
visibility = [ "*" ]
90-
sources = [ "video_frame_type.h" ]
91-
}
92-
93-
rtc_library("video_frame_i420") {
94-
visibility = [ "*" ]
95-
sources = [
96-
"i420_buffer.cc",
97-
"i420_buffer.h",
98-
]
99-
deps = [
100-
":video_frame",
101-
":video_rtp_headers",
102-
"..:scoped_refptr",
103-
"../../rtc_base",
104-
"../../rtc_base:checks",
105-
"../../rtc_base/memory:aligned_malloc",
106-
"../../rtc_base/system:rtc_export",
107-
"//third_party/libyuv",
108-
]
80+
sources = []
81+
deps = [ ":video_frame" ]
10982
}
11083

11184
rtc_library("video_frame_i010") {
@@ -116,7 +89,6 @@ rtc_library("video_frame_i010") {
11689
]
11790
deps = [
11891
":video_frame",
119-
":video_frame_i420",
12092
":video_rtp_headers",
12193
"..:scoped_refptr",
12294
"../../rtc_base",
@@ -134,7 +106,6 @@ rtc_library("video_frame_nv12") {
134106
]
135107
deps = [
136108
":video_frame",
137-
":video_frame_i420",
138109
"..:scoped_refptr",
139110
"../../rtc_base",
140111
"../../rtc_base:checks",
@@ -144,6 +115,26 @@ rtc_library("video_frame_nv12") {
144115
]
145116
}
146117

118+
rtc_source_set("recordable_encoded_frame") {
119+
visibility = [ "*" ]
120+
sources = [ "recordable_encoded_frame.h" ]
121+
122+
deps = [
123+
":encoded_image",
124+
":video_frame",
125+
":video_rtp_headers",
126+
"..:array_view",
127+
"..:scoped_refptr",
128+
"../../rtc_base:refcount",
129+
"../units:timestamp",
130+
]
131+
}
132+
133+
rtc_source_set("video_frame_type") {
134+
visibility = [ "*" ]
135+
sources = [ "video_frame_type.h" ]
136+
}
137+
147138
rtc_library("encoded_image") {
148139
visibility = [ "*" ]
149140
sources = [

api/video/nv12_buffer.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "rtc_base/checks.h"
1515
#include "rtc_base/ref_counted_object.h"
1616
#include "third_party/libyuv/include/libyuv/convert.h"
17+
#include "third_party/libyuv/include/libyuv/scale.h"
1718

1819
namespace webrtc {
1920

@@ -122,4 +123,35 @@ void NV12Buffer::InitializeData() {
122123
memset(data_.get(), 0, NV12DataSize(height_, stride_y_, stride_uv_));
123124
}
124125

126+
void NV12Buffer::CropAndScaleFrom(const NV12BufferInterface& src,
127+
int offset_x,
128+
int offset_y,
129+
int crop_width,
130+
int crop_height) {
131+
RTC_CHECK_LE(crop_width, src.width());
132+
RTC_CHECK_LE(crop_height, src.height());
133+
RTC_CHECK_LE(crop_width + offset_x, src.width());
134+
RTC_CHECK_LE(crop_height + offset_y, src.height());
135+
RTC_CHECK_GE(offset_x, 0);
136+
RTC_CHECK_GE(offset_y, 0);
137+
138+
// Make sure offset is even so that u/v plane becomes aligned.
139+
const int uv_offset_x = offset_x / 2;
140+
const int uv_offset_y = offset_y / 2;
141+
offset_x = uv_offset_x * 2;
142+
offset_y = uv_offset_y * 2;
143+
144+
const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
145+
const uint8_t* uv_plane =
146+
src.DataUV() + src.StrideUV() * uv_offset_y + uv_offset_x * 2;
147+
148+
// kFilterBox is unsupported in libyuv, so using kFilterBilinear instead.
149+
int res = libyuv::NV12Scale(y_plane, src.StrideY(), uv_plane, src.StrideUV(),
150+
crop_width, crop_height, MutableDataY(),
151+
StrideY(), MutableDataUV(), StrideUV(), width(),
152+
height(), libyuv::kFilterBilinear);
153+
154+
RTC_DCHECK_EQ(res, 0);
155+
}
156+
125157
} // namespace webrtc

api/video/nv12_buffer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ class RTC_EXPORT NV12Buffer : public NV12BufferInterface {
5656
// are resolved in a better way. Or in the mean time, use SetBlack.
5757
void InitializeData();
5858

59+
// Scale the cropped area of |src| to the size of |this| buffer, and
60+
// write the result into |this|.
61+
void CropAndScaleFrom(const NV12BufferInterface& src,
62+
int offset_x,
63+
int offset_y,
64+
int crop_width,
65+
int crop_height);
66+
5967
protected:
6068
NV12Buffer(int width, int height);
6169
NV12Buffer(int width, int height, int stride_y, int stride_uv);

api/video/test/BUILD.gn

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ rtc_library("rtc_api_video_unittests") {
2020
"..:video_adaptation",
2121
"..:video_bitrate_allocation",
2222
"..:video_frame",
23-
"..:video_frame_i420",
2423
"..:video_frame_nv12",
2524
"..:video_rtp_headers",
2625
"../../../test:frame_utils",

api/video/video_frame_buffer.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,25 @@
1010

1111
#include "api/video/video_frame_buffer.h"
1212

13+
#include "api/video/i420_buffer.h"
1314
#include "rtc_base/checks.h"
1415

1516
namespace webrtc {
1617

18+
rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBuffer::CropAndScale(
19+
int offset_x,
20+
int offset_y,
21+
int crop_width,
22+
int crop_height,
23+
int scaled_width,
24+
int scaled_height) {
25+
rtc::scoped_refptr<I420Buffer> result =
26+
I420Buffer::Create(scaled_width, scaled_height);
27+
result->CropAndScaleFrom(*this->ToI420(), offset_x, offset_y, crop_width,
28+
crop_height);
29+
return result;
30+
}
31+
1732
const I420BufferInterface* VideoFrameBuffer::GetI420() const {
1833
// Overridden by subclasses that can return an I420 buffer without any
1934
// conversion, in particular, I420BufferInterface.
@@ -99,5 +114,4 @@ int NV12BufferInterface::ChromaWidth() const {
99114
int NV12BufferInterface::ChromaHeight() const {
100115
return (height() + 1) / 2;
101116
}
102-
103117
} // namespace webrtc

api/video/video_frame_buffer.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,24 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface {
7676
// doesn't affect binary data at all. Another example is any I420A buffer.
7777
virtual const I420BufferInterface* GetI420() const;
7878

79+
// A format specific scale function. Default implementation works by
80+
// converting to I420. But more efficient implementations may override it,
81+
// especially for kNative.
82+
// First, the image is cropped to |crop_width| and |crop_height| and then
83+
// scaled to |scaled_width| and |scaled_height|.
84+
virtual rtc::scoped_refptr<VideoFrameBuffer> CropAndScale(int offset_x,
85+
int offset_y,
86+
int crop_width,
87+
int crop_height,
88+
int scaled_width,
89+
int scaled_height);
90+
91+
// Alias for common use case.
92+
rtc::scoped_refptr<VideoFrameBuffer> Scale(int scaled_width,
93+
int scaled_height) {
94+
return CropAndScale(0, 0, width(), height(), scaled_width, scaled_height);
95+
}
96+
7997
// These functions should only be called if type() is of the correct type.
8098
// Calling with a different type will result in a crash.
8199
const I420ABufferInterface* GetI420A() const;

api/video_codecs/BUILD.gn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ rtc_library("rtc_software_fallback_wrappers") {
137137
deps = [
138138
":video_codecs_api",
139139
"..:fec_controller_api",
140-
"../../api/video:video_frame_i420",
140+
"../../api/video:video_frame",
141141
"../../media:rtc_h264_profile_id",
142142
"../../media:rtc_media_base",
143143
"../../modules/video_coding:video_codec_interface",

api/video_codecs/test/BUILD.gn

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ if (rtc_include_tests) {
3636
"../../video:encoded_image",
3737
"../../video:video_bitrate_allocation",
3838
"../../video:video_frame",
39-
"../../video:video_frame_i420",
4039
"../../video:video_rtp_headers",
4140
"//testing/gtest",
4241
]

common_video/BUILD.gn

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ rtc_library("common_video") {
4949
"../api/video:video_bitrate_allocation",
5050
"../api/video:video_bitrate_allocator",
5151
"../api/video:video_frame",
52-
"../api/video:video_frame_i420",
5352
"../api/video:video_frame_nv12",
5453
"../api/video:video_rtp_headers",
5554
"../api/video_codecs:bitstream_parser_api",
@@ -105,7 +104,7 @@ if (rtc_include_tests) {
105104
"../api/units:time_delta",
106105
"../api/video:video_frame",
107106
"../api/video:video_frame_i010",
108-
"../api/video:video_frame_i420",
107+
"../api/video:video_frame_nv12",
109108
"../api/video:video_rtp_headers",
110109
"../media:rtc_h264_profile_id",
111110
"../rtc_base",

common_video/video_frame_unittest.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "api/video/i010_buffer.h"
1717
#include "api/video/i420_buffer.h"
18+
#include "api/video/nv12_buffer.h"
1819
#include "rtc_base/bind.h"
1920
#include "rtc_base/time_utils.h"
2021
#include "test/fake_texture_frame.h"
@@ -157,6 +158,29 @@ rtc::scoped_refptr<PlanarYuvBuffer> CreateGradient(VideoFrameBuffer::Type type,
157158
return I010Buffer::Copy(*buffer);
158159
}
159160

161+
rtc::scoped_refptr<NV12BufferInterface> CreateNV12Gradient(int width,
162+
int height) {
163+
rtc::scoped_refptr<NV12Buffer> buffer(NV12Buffer::Create(width, height));
164+
// Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h
165+
for (int x = 0; x < width; x++) {
166+
for (int y = 0; y < height; y++) {
167+
buffer->MutableDataY()[x + y * width] =
168+
128 * (x * height + y * width) / (width * height);
169+
}
170+
}
171+
int chroma_width = buffer->ChromaWidth();
172+
int chroma_height = buffer->ChromaHeight();
173+
for (int x = 0; x < chroma_width; x++) {
174+
for (int y = 0; y < chroma_height; y++) {
175+
buffer->MutableDataUV()[x * 2 + y * buffer->StrideUV()] =
176+
255 * x / (chroma_width - 1);
177+
buffer->MutableDataUV()[x * 2 + 1 + y * buffer->StrideUV()] =
178+
255 * y / (chroma_height - 1);
179+
}
180+
}
181+
return buffer;
182+
}
183+
160184
// The offsets and sizes describe the rectangle extracted from the
161185
// original (gradient) frame, in relative coordinates where the
162186
// original frame correspond to the unit square, 0.0 <= x, y < 1.0.
@@ -495,6 +519,35 @@ INSTANTIATE_TEST_SUITE_P(All,
495519
::testing::Values(VideoFrameBuffer::Type::kI420,
496520
VideoFrameBuffer::Type::kI010));
497521

522+
TEST(TestNV12Buffer, CropAndScale) {
523+
const int kSourceWidth = 640;
524+
const int kSourceHeight = 480;
525+
const int kScaledWidth = 320;
526+
const int kScaledHeight = 240;
527+
const int kCropLeft = 40;
528+
const int kCropTop = 30;
529+
const int kCropRight = 0;
530+
const int kCropBottom = 30;
531+
532+
rtc::scoped_refptr<VideoFrameBuffer> buf =
533+
CreateNV12Gradient(kSourceWidth, kSourceHeight);
534+
535+
rtc::scoped_refptr<VideoFrameBuffer> scaled_buffer = buf->CropAndScale(
536+
kCropLeft, kCropTop, kSourceWidth - kCropLeft - kCropRight,
537+
kSourceHeight - kCropTop - kCropBottom, kScaledWidth, kScaledHeight);
538+
539+
// Parameters to CheckCrop indicate what part of the source frame is in the
540+
// scaled frame.
541+
const float kOffsetX = (kCropLeft + 0.0) / kSourceWidth;
542+
const float kOffsetY = (kCropTop + 0.0) / kSourceHeight;
543+
const float kRelativeWidth =
544+
(kSourceWidth - kCropLeft - kCropRight + 0.0) / kSourceWidth;
545+
const float kRelativeHeight =
546+
(kSourceHeight - kCropTop - kCropBottom + 0.0) / kSourceHeight;
547+
CheckCrop(*scaled_buffer->ToI420(), kOffsetX, kOffsetY, kRelativeWidth,
548+
kRelativeHeight);
549+
}
550+
498551
class TestPlanarYuvBufferRotate
499552
: public ::testing::TestWithParam<
500553
std::tuple<webrtc::VideoRotation, VideoFrameBuffer::Type>> {};

0 commit comments

Comments
 (0)