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 <ilnik@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Stefan Holmer <stefan@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Evan Shrubsole <eshr@google.com> Cr-Commit-Position: refs/heads/master@{#32352}
This commit is contained in:

committed by
Commit Bot

parent
2d05878689
commit
c79f1d8cfb
@ -42,6 +42,8 @@ rtc_library("video_rtp_headers") {
|
||||
rtc_library("video_frame") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"i420_buffer.cc",
|
||||
"i420_buffer.h",
|
||||
"video_codec_type.h",
|
||||
"video_frame.cc",
|
||||
"video_frame.h",
|
||||
@ -59,7 +61,9 @@ rtc_library("video_frame") {
|
||||
"..:scoped_refptr",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../rtc_base/memory:aligned_malloc",
|
||||
"../../rtc_base/system:rtc_export",
|
||||
"//third_party/libyuv",
|
||||
]
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
@ -70,6 +74,47 @@ if (is_android) {
|
||||
}
|
||||
}
|
||||
|
||||
# Deprecated empty target. Use "video_frame" instead.
|
||||
rtc_source_set("video_frame_i420") {
|
||||
visibility = [ "*" ]
|
||||
sources = []
|
||||
deps = [ ":video_frame" ]
|
||||
}
|
||||
|
||||
rtc_library("video_frame_i010") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"i010_buffer.cc",
|
||||
"i010_buffer.h",
|
||||
]
|
||||
deps = [
|
||||
":video_frame",
|
||||
":video_rtp_headers",
|
||||
"..:scoped_refptr",
|
||||
"../../rtc_base",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base/memory:aligned_malloc",
|
||||
"//third_party/libyuv",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("video_frame_nv12") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"nv12_buffer.cc",
|
||||
"nv12_buffer.h",
|
||||
]
|
||||
deps = [
|
||||
":video_frame",
|
||||
"..:scoped_refptr",
|
||||
"../../rtc_base",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base/memory:aligned_malloc",
|
||||
"../../rtc_base/system:rtc_export",
|
||||
"//third_party/libyuv",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("recordable_encoded_frame") {
|
||||
visibility = [ "*" ]
|
||||
sources = [ "recordable_encoded_frame.h" ]
|
||||
@ -90,60 +135,6 @@ rtc_source_set("video_frame_type") {
|
||||
sources = [ "video_frame_type.h" ]
|
||||
}
|
||||
|
||||
rtc_library("video_frame_i420") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"i420_buffer.cc",
|
||||
"i420_buffer.h",
|
||||
]
|
||||
deps = [
|
||||
":video_frame",
|
||||
":video_rtp_headers",
|
||||
"..:scoped_refptr",
|
||||
"../../rtc_base",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base/memory:aligned_malloc",
|
||||
"../../rtc_base/system:rtc_export",
|
||||
"//third_party/libyuv",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("video_frame_i010") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"i010_buffer.cc",
|
||||
"i010_buffer.h",
|
||||
]
|
||||
deps = [
|
||||
":video_frame",
|
||||
":video_frame_i420",
|
||||
":video_rtp_headers",
|
||||
"..:scoped_refptr",
|
||||
"../../rtc_base",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base/memory:aligned_malloc",
|
||||
"//third_party/libyuv",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("video_frame_nv12") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"nv12_buffer.cc",
|
||||
"nv12_buffer.h",
|
||||
]
|
||||
deps = [
|
||||
":video_frame",
|
||||
":video_frame_i420",
|
||||
"..:scoped_refptr",
|
||||
"../../rtc_base",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base/memory:aligned_malloc",
|
||||
"../../rtc_base/system:rtc_export",
|
||||
"//third_party/libyuv",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("encoded_image") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "third_party/libyuv/include/libyuv/convert.h"
|
||||
#include "third_party/libyuv/include/libyuv/scale.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -122,4 +123,35 @@ void NV12Buffer::InitializeData() {
|
||||
memset(data_.get(), 0, NV12DataSize(height_, stride_y_, stride_uv_));
|
||||
}
|
||||
|
||||
void NV12Buffer::CropAndScaleFrom(const NV12BufferInterface& src,
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height) {
|
||||
RTC_CHECK_LE(crop_width, src.width());
|
||||
RTC_CHECK_LE(crop_height, src.height());
|
||||
RTC_CHECK_LE(crop_width + offset_x, src.width());
|
||||
RTC_CHECK_LE(crop_height + offset_y, src.height());
|
||||
RTC_CHECK_GE(offset_x, 0);
|
||||
RTC_CHECK_GE(offset_y, 0);
|
||||
|
||||
// Make sure offset is even so that u/v plane becomes aligned.
|
||||
const int uv_offset_x = offset_x / 2;
|
||||
const int uv_offset_y = offset_y / 2;
|
||||
offset_x = uv_offset_x * 2;
|
||||
offset_y = uv_offset_y * 2;
|
||||
|
||||
const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
|
||||
const uint8_t* uv_plane =
|
||||
src.DataUV() + src.StrideUV() * uv_offset_y + uv_offset_x * 2;
|
||||
|
||||
// kFilterBox is unsupported in libyuv, so using kFilterBilinear instead.
|
||||
int res = libyuv::NV12Scale(y_plane, src.StrideY(), uv_plane, src.StrideUV(),
|
||||
crop_width, crop_height, MutableDataY(),
|
||||
StrideY(), MutableDataUV(), StrideUV(), width(),
|
||||
height(), libyuv::kFilterBilinear);
|
||||
|
||||
RTC_DCHECK_EQ(res, 0);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -56,6 +56,14 @@ class RTC_EXPORT NV12Buffer : public NV12BufferInterface {
|
||||
// are resolved in a better way. Or in the mean time, use SetBlack.
|
||||
void InitializeData();
|
||||
|
||||
// Scale the cropped area of |src| to the size of |this| buffer, and
|
||||
// write the result into |this|.
|
||||
void CropAndScaleFrom(const NV12BufferInterface& src,
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height);
|
||||
|
||||
protected:
|
||||
NV12Buffer(int width, int height);
|
||||
NV12Buffer(int width, int height, int stride_y, int stride_uv);
|
||||
|
@ -20,7 +20,6 @@ rtc_library("rtc_api_video_unittests") {
|
||||
"..:video_adaptation",
|
||||
"..:video_bitrate_allocation",
|
||||
"..:video_frame",
|
||||
"..:video_frame_i420",
|
||||
"..:video_frame_nv12",
|
||||
"..:video_rtp_headers",
|
||||
"../../../test:frame_utils",
|
||||
|
@ -10,10 +10,25 @@
|
||||
|
||||
#include "api/video/video_frame_buffer.h"
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBuffer::CropAndScale(
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height) {
|
||||
rtc::scoped_refptr<I420Buffer> result =
|
||||
I420Buffer::Create(scaled_width, scaled_height);
|
||||
result->CropAndScaleFrom(*this->ToI420(), offset_x, offset_y, crop_width,
|
||||
crop_height);
|
||||
return result;
|
||||
}
|
||||
|
||||
const I420BufferInterface* VideoFrameBuffer::GetI420() const {
|
||||
// Overridden by subclasses that can return an I420 buffer without any
|
||||
// conversion, in particular, I420BufferInterface.
|
||||
@ -99,5 +114,4 @@ int NV12BufferInterface::ChromaWidth() const {
|
||||
int NV12BufferInterface::ChromaHeight() const {
|
||||
return (height() + 1) / 2;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -76,6 +76,24 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface {
|
||||
// doesn't affect binary data at all. Another example is any I420A buffer.
|
||||
virtual const I420BufferInterface* GetI420() const;
|
||||
|
||||
// A format specific scale function. Default implementation works by
|
||||
// converting to I420. But more efficient implementations may override it,
|
||||
// especially for kNative.
|
||||
// First, the image is cropped to |crop_width| and |crop_height| and then
|
||||
// scaled to |scaled_width| and |scaled_height|.
|
||||
virtual rtc::scoped_refptr<VideoFrameBuffer> CropAndScale(int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height);
|
||||
|
||||
// Alias for common use case.
|
||||
rtc::scoped_refptr<VideoFrameBuffer> Scale(int scaled_width,
|
||||
int scaled_height) {
|
||||
return CropAndScale(0, 0, width(), height(), scaled_width, scaled_height);
|
||||
}
|
||||
|
||||
// These functions should only be called if type() is of the correct type.
|
||||
// Calling with a different type will result in a crash.
|
||||
const I420ABufferInterface* GetI420A() const;
|
||||
|
@ -137,7 +137,7 @@ rtc_library("rtc_software_fallback_wrappers") {
|
||||
deps = [
|
||||
":video_codecs_api",
|
||||
"..:fec_controller_api",
|
||||
"../../api/video:video_frame_i420",
|
||||
"../../api/video:video_frame",
|
||||
"../../media:rtc_h264_profile_id",
|
||||
"../../media:rtc_media_base",
|
||||
"../../modules/video_coding:video_codec_interface",
|
||||
|
@ -36,7 +36,6 @@ if (rtc_include_tests) {
|
||||
"../../video:encoded_image",
|
||||
"../../video:video_bitrate_allocation",
|
||||
"../../video:video_frame",
|
||||
"../../video:video_frame_i420",
|
||||
"../../video:video_rtp_headers",
|
||||
"//testing/gtest",
|
||||
]
|
||||
|
Reference in New Issue
Block a user