1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "api/video/nv12_buffer.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include "api/make_ref_counted.h"
14*d9f75844SAndroid Build Coastguard Worker #include "api/video/i420_buffer.h"
15*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
16*d9f75844SAndroid Build Coastguard Worker #include "third_party/libyuv/include/libyuv/convert.h"
17*d9f75844SAndroid Build Coastguard Worker #include "third_party/libyuv/include/libyuv/scale.h"
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
20*d9f75844SAndroid Build Coastguard Worker
21*d9f75844SAndroid Build Coastguard Worker namespace {
22*d9f75844SAndroid Build Coastguard Worker
23*d9f75844SAndroid Build Coastguard Worker static const int kBufferAlignment = 64;
24*d9f75844SAndroid Build Coastguard Worker
NV12DataSize(int height,int stride_y,int stride_uv)25*d9f75844SAndroid Build Coastguard Worker int NV12DataSize(int height, int stride_y, int stride_uv) {
26*d9f75844SAndroid Build Coastguard Worker return stride_y * height + stride_uv * ((height + 1) / 2);
27*d9f75844SAndroid Build Coastguard Worker }
28*d9f75844SAndroid Build Coastguard Worker
29*d9f75844SAndroid Build Coastguard Worker } // namespace
30*d9f75844SAndroid Build Coastguard Worker
NV12Buffer(int width,int height)31*d9f75844SAndroid Build Coastguard Worker NV12Buffer::NV12Buffer(int width, int height)
32*d9f75844SAndroid Build Coastguard Worker : NV12Buffer(width, height, width, width + width % 2) {}
33*d9f75844SAndroid Build Coastguard Worker
NV12Buffer(int width,int height,int stride_y,int stride_uv)34*d9f75844SAndroid Build Coastguard Worker NV12Buffer::NV12Buffer(int width, int height, int stride_y, int stride_uv)
35*d9f75844SAndroid Build Coastguard Worker : width_(width),
36*d9f75844SAndroid Build Coastguard Worker height_(height),
37*d9f75844SAndroid Build Coastguard Worker stride_y_(stride_y),
38*d9f75844SAndroid Build Coastguard Worker stride_uv_(stride_uv),
39*d9f75844SAndroid Build Coastguard Worker data_(static_cast<uint8_t*>(
40*d9f75844SAndroid Build Coastguard Worker AlignedMalloc(NV12DataSize(height_, stride_y_, stride_uv),
41*d9f75844SAndroid Build Coastguard Worker kBufferAlignment))) {
42*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(width, 0);
43*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(height, 0);
44*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(stride_y, width);
45*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(stride_uv, (width + width % 2));
46*d9f75844SAndroid Build Coastguard Worker }
47*d9f75844SAndroid Build Coastguard Worker
48*d9f75844SAndroid Build Coastguard Worker NV12Buffer::~NV12Buffer() = default;
49*d9f75844SAndroid Build Coastguard Worker
50*d9f75844SAndroid Build Coastguard Worker // static
Create(int width,int height)51*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<NV12Buffer> NV12Buffer::Create(int width, int height) {
52*d9f75844SAndroid Build Coastguard Worker return rtc::make_ref_counted<NV12Buffer>(width, height);
53*d9f75844SAndroid Build Coastguard Worker }
54*d9f75844SAndroid Build Coastguard Worker
55*d9f75844SAndroid Build Coastguard Worker // static
Create(int width,int height,int stride_y,int stride_uv)56*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<NV12Buffer> NV12Buffer::Create(int width,
57*d9f75844SAndroid Build Coastguard Worker int height,
58*d9f75844SAndroid Build Coastguard Worker int stride_y,
59*d9f75844SAndroid Build Coastguard Worker int stride_uv) {
60*d9f75844SAndroid Build Coastguard Worker return rtc::make_ref_counted<NV12Buffer>(width, height, stride_y, stride_uv);
61*d9f75844SAndroid Build Coastguard Worker }
62*d9f75844SAndroid Build Coastguard Worker
63*d9f75844SAndroid Build Coastguard Worker // static
Copy(const I420BufferInterface & i420_buffer)64*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<NV12Buffer> NV12Buffer::Copy(
65*d9f75844SAndroid Build Coastguard Worker const I420BufferInterface& i420_buffer) {
66*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<NV12Buffer> buffer =
67*d9f75844SAndroid Build Coastguard Worker NV12Buffer::Create(i420_buffer.width(), i420_buffer.height());
68*d9f75844SAndroid Build Coastguard Worker libyuv::I420ToNV12(
69*d9f75844SAndroid Build Coastguard Worker i420_buffer.DataY(), i420_buffer.StrideY(), i420_buffer.DataU(),
70*d9f75844SAndroid Build Coastguard Worker i420_buffer.StrideU(), i420_buffer.DataV(), i420_buffer.StrideV(),
71*d9f75844SAndroid Build Coastguard Worker buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataUV(),
72*d9f75844SAndroid Build Coastguard Worker buffer->StrideUV(), buffer->width(), buffer->height());
73*d9f75844SAndroid Build Coastguard Worker return buffer;
74*d9f75844SAndroid Build Coastguard Worker }
75*d9f75844SAndroid Build Coastguard Worker
ToI420()76*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420BufferInterface> NV12Buffer::ToI420() {
77*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420Buffer> i420_buffer =
78*d9f75844SAndroid Build Coastguard Worker I420Buffer::Create(width(), height());
79*d9f75844SAndroid Build Coastguard Worker libyuv::NV12ToI420(DataY(), StrideY(), DataUV(), StrideUV(),
80*d9f75844SAndroid Build Coastguard Worker i420_buffer->MutableDataY(), i420_buffer->StrideY(),
81*d9f75844SAndroid Build Coastguard Worker i420_buffer->MutableDataU(), i420_buffer->StrideU(),
82*d9f75844SAndroid Build Coastguard Worker i420_buffer->MutableDataV(), i420_buffer->StrideV(),
83*d9f75844SAndroid Build Coastguard Worker width(), height());
84*d9f75844SAndroid Build Coastguard Worker return i420_buffer;
85*d9f75844SAndroid Build Coastguard Worker }
86*d9f75844SAndroid Build Coastguard Worker
width() const87*d9f75844SAndroid Build Coastguard Worker int NV12Buffer::width() const {
88*d9f75844SAndroid Build Coastguard Worker return width_;
89*d9f75844SAndroid Build Coastguard Worker }
height() const90*d9f75844SAndroid Build Coastguard Worker int NV12Buffer::height() const {
91*d9f75844SAndroid Build Coastguard Worker return height_;
92*d9f75844SAndroid Build Coastguard Worker }
93*d9f75844SAndroid Build Coastguard Worker
StrideY() const94*d9f75844SAndroid Build Coastguard Worker int NV12Buffer::StrideY() const {
95*d9f75844SAndroid Build Coastguard Worker return stride_y_;
96*d9f75844SAndroid Build Coastguard Worker }
StrideUV() const97*d9f75844SAndroid Build Coastguard Worker int NV12Buffer::StrideUV() const {
98*d9f75844SAndroid Build Coastguard Worker return stride_uv_;
99*d9f75844SAndroid Build Coastguard Worker }
100*d9f75844SAndroid Build Coastguard Worker
DataY() const101*d9f75844SAndroid Build Coastguard Worker const uint8_t* NV12Buffer::DataY() const {
102*d9f75844SAndroid Build Coastguard Worker return data_.get();
103*d9f75844SAndroid Build Coastguard Worker }
104*d9f75844SAndroid Build Coastguard Worker
DataUV() const105*d9f75844SAndroid Build Coastguard Worker const uint8_t* NV12Buffer::DataUV() const {
106*d9f75844SAndroid Build Coastguard Worker return data_.get() + UVOffset();
107*d9f75844SAndroid Build Coastguard Worker }
108*d9f75844SAndroid Build Coastguard Worker
MutableDataY()109*d9f75844SAndroid Build Coastguard Worker uint8_t* NV12Buffer::MutableDataY() {
110*d9f75844SAndroid Build Coastguard Worker return data_.get();
111*d9f75844SAndroid Build Coastguard Worker }
112*d9f75844SAndroid Build Coastguard Worker
MutableDataUV()113*d9f75844SAndroid Build Coastguard Worker uint8_t* NV12Buffer::MutableDataUV() {
114*d9f75844SAndroid Build Coastguard Worker return data_.get() + UVOffset();
115*d9f75844SAndroid Build Coastguard Worker }
116*d9f75844SAndroid Build Coastguard Worker
UVOffset() const117*d9f75844SAndroid Build Coastguard Worker size_t NV12Buffer::UVOffset() const {
118*d9f75844SAndroid Build Coastguard Worker return stride_y_ * height_;
119*d9f75844SAndroid Build Coastguard Worker }
120*d9f75844SAndroid Build Coastguard Worker
InitializeData()121*d9f75844SAndroid Build Coastguard Worker void NV12Buffer::InitializeData() {
122*d9f75844SAndroid Build Coastguard Worker memset(data_.get(), 0, NV12DataSize(height_, stride_y_, stride_uv_));
123*d9f75844SAndroid Build Coastguard Worker }
124*d9f75844SAndroid Build Coastguard Worker
CropAndScaleFrom(const NV12BufferInterface & src,int offset_x,int offset_y,int crop_width,int crop_height)125*d9f75844SAndroid Build Coastguard Worker void NV12Buffer::CropAndScaleFrom(const NV12BufferInterface& src,
126*d9f75844SAndroid Build Coastguard Worker int offset_x,
127*d9f75844SAndroid Build Coastguard Worker int offset_y,
128*d9f75844SAndroid Build Coastguard Worker int crop_width,
129*d9f75844SAndroid Build Coastguard Worker int crop_height) {
130*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_LE(crop_width, src.width());
131*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_LE(crop_height, src.height());
132*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_LE(crop_width + offset_x, src.width());
133*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_LE(crop_height + offset_y, src.height());
134*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_GE(offset_x, 0);
135*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_GE(offset_y, 0);
136*d9f75844SAndroid Build Coastguard Worker
137*d9f75844SAndroid Build Coastguard Worker // Make sure offset is even so that u/v plane becomes aligned.
138*d9f75844SAndroid Build Coastguard Worker const int uv_offset_x = offset_x / 2;
139*d9f75844SAndroid Build Coastguard Worker const int uv_offset_y = offset_y / 2;
140*d9f75844SAndroid Build Coastguard Worker offset_x = uv_offset_x * 2;
141*d9f75844SAndroid Build Coastguard Worker offset_y = uv_offset_y * 2;
142*d9f75844SAndroid Build Coastguard Worker
143*d9f75844SAndroid Build Coastguard Worker const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
144*d9f75844SAndroid Build Coastguard Worker const uint8_t* uv_plane =
145*d9f75844SAndroid Build Coastguard Worker src.DataUV() + src.StrideUV() * uv_offset_y + uv_offset_x * 2;
146*d9f75844SAndroid Build Coastguard Worker
147*d9f75844SAndroid Build Coastguard Worker int res = libyuv::NV12Scale(y_plane, src.StrideY(), uv_plane, src.StrideUV(),
148*d9f75844SAndroid Build Coastguard Worker crop_width, crop_height, MutableDataY(),
149*d9f75844SAndroid Build Coastguard Worker StrideY(), MutableDataUV(), StrideUV(), width(),
150*d9f75844SAndroid Build Coastguard Worker height(), libyuv::kFilterBox);
151*d9f75844SAndroid Build Coastguard Worker
152*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(res, 0);
153*d9f75844SAndroid Build Coastguard Worker }
154*d9f75844SAndroid Build Coastguard Worker
155*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
156