1 /*
2 * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "api/video/i210_buffer.h"
11
12 #include <utility>
13
14 #include "api/make_ref_counted.h"
15 #include "api/video/i420_buffer.h"
16 #include "api/video/i422_buffer.h"
17 #include "rtc_base/checks.h"
18 #include "third_party/libyuv/include/libyuv/convert.h"
19 #include "third_party/libyuv/include/libyuv/scale.h"
20
21 // Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
22 static const int kBufferAlignment = 64;
23 static const int kBytesPerPixel = 2;
24
25 namespace webrtc {
26
27 namespace {
28
I210DataSize(int height,int stride_y,int stride_u,int stride_v)29 int I210DataSize(int height, int stride_y, int stride_u, int stride_v) {
30 return kBytesPerPixel *
31 (stride_y * height + stride_u * height + stride_v * height);
32 }
33
webrtcRotatePlane90_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)34 void webrtcRotatePlane90_16(const uint16_t* src,
35 int src_stride,
36 uint16_t* dst,
37 int dst_stride,
38 int width,
39 int height) {
40 for (int x = 0; x < width; x++) {
41 for (int y = 0; y < height; y++) {
42 int dest_x = height - y - 1;
43 int dest_y = x;
44 dst[dest_x + dst_stride * dest_y] = src[x + src_stride * y];
45 }
46 }
47 }
48
webrtcRotatePlane180_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)49 void webrtcRotatePlane180_16(const uint16_t* src,
50 int src_stride,
51 uint16_t* dst,
52 int dst_stride,
53 int width,
54 int height) {
55 for (int x = 0; x < width; x++) {
56 for (int y = 0; y < height; y++) {
57 int dest_x = width - x - 1;
58 int dest_y = height - y - 1;
59 dst[dest_x + dst_stride * dest_y] = src[x + src_stride * y];
60 }
61 }
62 }
63
webrtcRotatePlane270_16(const uint16_t * src,int src_stride,uint16_t * dst,int dst_stride,int width,int height)64 void webrtcRotatePlane270_16(const uint16_t* src,
65 int src_stride,
66 uint16_t* dst,
67 int dst_stride,
68 int width,
69 int height) {
70 for (int x = 0; x < width; x++) {
71 for (int y = 0; y < height; y++) {
72 int dest_x = y;
73 int dest_y = width - x - 1;
74 dst[dest_x + dst_stride * dest_y] = src[x + src_stride * y];
75 }
76 }
77 }
78
79 // TODO([email protected]): Remove as soon it is available in
80 // libyuv. Due to the rotate&scale required, this function may not be merged in
81 // to libyuv inmediatelly.
82 // https://bugs.chromium.org/p/libyuv/issues/detail?id=926
83 // This method assumes continuous allocation of the y-plane, possibly clobbering
84 // any padding between pixel rows.
webrtcI210Rotate(const uint16_t * src_y,int src_stride_y,const uint16_t * src_u,int src_stride_u,const uint16_t * src_v,int src_stride_v,uint16_t * dst_y,int dst_stride_y,uint16_t * dst_u,int dst_stride_u,uint16_t * dst_v,int dst_stride_v,int width,int height,enum libyuv::RotationMode mode)85 int webrtcI210Rotate(const uint16_t* src_y,
86 int src_stride_y,
87 const uint16_t* src_u,
88 int src_stride_u,
89 const uint16_t* src_v,
90 int src_stride_v,
91 uint16_t* dst_y,
92 int dst_stride_y,
93 uint16_t* dst_u,
94 int dst_stride_u,
95 uint16_t* dst_v,
96 int dst_stride_v,
97 int width,
98 int height,
99 enum libyuv::RotationMode mode) {
100 int halfwidth = (width + 1) >> 1;
101 int halfheight = (height + 1) >> 1;
102 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
103 !dst_u || !dst_v || dst_stride_y < 0) {
104 return -1;
105 }
106 // Negative height means invert the image.
107 if (height < 0) {
108 height = -height;
109 src_y = src_y + (height - 1) * src_stride_y;
110 src_u = src_u + (height - 1) * src_stride_u;
111 src_v = src_v + (height - 1) * src_stride_v;
112 src_stride_y = -src_stride_y;
113 src_stride_u = -src_stride_u;
114 src_stride_v = -src_stride_v;
115 }
116
117 switch (mode) {
118 case libyuv::kRotate0:
119 // copy frame
120 libyuv::CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
121 height);
122 libyuv::CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
123 height);
124 libyuv::CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
125 height);
126 return 0;
127 case libyuv::kRotate90:
128 // We need to rotate and rescale, we use plane Y as temporal storage.
129 webrtcRotatePlane90_16(src_u, src_stride_u, dst_y, height, halfwidth,
130 height);
131 libyuv::ScalePlane_16(dst_y, height, height, halfwidth, dst_u, halfheight,
132 halfheight, width, libyuv::kFilterBilinear);
133 webrtcRotatePlane90_16(src_v, src_stride_v, dst_y, height, halfwidth,
134 height);
135 libyuv::ScalePlane_16(dst_y, height, height, halfwidth, dst_v, halfheight,
136 halfheight, width, libyuv::kFilterLinear);
137 webrtcRotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
138 height);
139 return 0;
140 case libyuv::kRotate270:
141 // We need to rotate and rescale, we use plane Y as temporal storage.
142 webrtcRotatePlane270_16(src_u, src_stride_u, dst_y, height, halfwidth,
143 height);
144 libyuv::ScalePlane_16(dst_y, height, height, halfwidth, dst_u, halfheight,
145 halfheight, width, libyuv::kFilterBilinear);
146 webrtcRotatePlane270_16(src_v, src_stride_v, dst_y, height, halfwidth,
147 height);
148 libyuv::ScalePlane_16(dst_y, height, height, halfwidth, dst_v, halfheight,
149 halfheight, width, libyuv::kFilterLinear);
150 webrtcRotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
151 height);
152
153 return 0;
154 case libyuv::kRotate180:
155 webrtcRotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
156 height);
157 webrtcRotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u,
158 halfwidth, height);
159 webrtcRotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v,
160 halfwidth, height);
161 return 0;
162 default:
163 break;
164 }
165 return -1;
166 }
167
168 } // namespace
169
I210Buffer(int width,int height,int stride_y,int stride_u,int stride_v)170 I210Buffer::I210Buffer(int width,
171 int height,
172 int stride_y,
173 int stride_u,
174 int stride_v)
175 : width_(width),
176 height_(height),
177 stride_y_(stride_y),
178 stride_u_(stride_u),
179 stride_v_(stride_v),
180 data_(static_cast<uint16_t*>(
181 AlignedMalloc(I210DataSize(height, stride_y, stride_u, stride_v),
182 kBufferAlignment))) {
183 RTC_DCHECK_GT(width, 0);
184 RTC_DCHECK_GT(height, 0);
185 RTC_DCHECK_GE(stride_y, width);
186 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
187 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
188 }
189
~I210Buffer()190 I210Buffer::~I210Buffer() {}
191
192 // static
Create(int width,int height)193 rtc::scoped_refptr<I210Buffer> I210Buffer::Create(int width, int height) {
194 return rtc::make_ref_counted<I210Buffer>(width, height, width,
195 (width + 1) / 2, (width + 1) / 2);
196 }
197
198 // static
Copy(const I210BufferInterface & source)199 rtc::scoped_refptr<I210Buffer> I210Buffer::Copy(
200 const I210BufferInterface& source) {
201 const int width = source.width();
202 const int height = source.height();
203 rtc::scoped_refptr<I210Buffer> buffer = Create(width, height);
204 RTC_CHECK_EQ(
205 0, libyuv::I210Copy(
206 source.DataY(), source.StrideY(), source.DataU(), source.StrideU(),
207 source.DataV(), source.StrideV(), buffer->MutableDataY(),
208 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
209 buffer->MutableDataV(), buffer->StrideV(), width, height));
210 return buffer;
211 }
212
213 // static
Copy(const I420BufferInterface & source)214 rtc::scoped_refptr<I210Buffer> I210Buffer::Copy(
215 const I420BufferInterface& source) {
216 const int width = source.width();
217 const int height = source.height();
218 auto i422buffer = I422Buffer::Copy(source);
219 rtc::scoped_refptr<I210Buffer> buffer = Create(width, height);
220 RTC_CHECK_EQ(0, libyuv::I422ToI210(i422buffer->DataY(), i422buffer->StrideY(),
221 i422buffer->DataU(), i422buffer->StrideU(),
222 i422buffer->DataV(), i422buffer->StrideV(),
223 buffer->MutableDataY(), buffer->StrideY(),
224 buffer->MutableDataU(), buffer->StrideU(),
225 buffer->MutableDataV(), buffer->StrideV(),
226 width, height));
227 return buffer;
228 }
229
230 // static
Rotate(const I210BufferInterface & src,VideoRotation rotation)231 rtc::scoped_refptr<I210Buffer> I210Buffer::Rotate(
232 const I210BufferInterface& src,
233 VideoRotation rotation) {
234 RTC_CHECK(src.DataY());
235 RTC_CHECK(src.DataU());
236 RTC_CHECK(src.DataV());
237
238 int rotated_width = src.width();
239 int rotated_height = src.height();
240 if (rotation == webrtc::kVideoRotation_90 ||
241 rotation == webrtc::kVideoRotation_270) {
242 std::swap(rotated_width, rotated_height);
243 }
244
245 rtc::scoped_refptr<webrtc::I210Buffer> buffer =
246 I210Buffer::Create(rotated_width, rotated_height);
247
248 RTC_CHECK_EQ(0,
249 webrtcI210Rotate(
250 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(),
251 src.DataV(), src.StrideV(), buffer->MutableDataY(),
252 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
253 buffer->MutableDataV(), buffer->StrideV(), src.width(),
254 src.height(), static_cast<libyuv::RotationMode>(rotation)));
255
256 return buffer;
257 }
258
ToI420()259 rtc::scoped_refptr<I420BufferInterface> I210Buffer::ToI420() {
260 rtc::scoped_refptr<I420Buffer> i420_buffer =
261 I420Buffer::Create(width(), height());
262 libyuv::I210ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
263 i420_buffer->MutableDataY(), i420_buffer->StrideY(),
264 i420_buffer->MutableDataU(), i420_buffer->StrideU(),
265 i420_buffer->MutableDataV(), i420_buffer->StrideV(),
266 width(), height());
267 return i420_buffer;
268 }
269
width() const270 int I210Buffer::width() const {
271 return width_;
272 }
273
height() const274 int I210Buffer::height() const {
275 return height_;
276 }
277
DataY() const278 const uint16_t* I210Buffer::DataY() const {
279 return data_.get();
280 }
DataU() const281 const uint16_t* I210Buffer::DataU() const {
282 return data_.get() + stride_y_ * height_;
283 }
DataV() const284 const uint16_t* I210Buffer::DataV() const {
285 return data_.get() + stride_y_ * height_ + stride_u_ * height_;
286 }
287
StrideY() const288 int I210Buffer::StrideY() const {
289 return stride_y_;
290 }
StrideU() const291 int I210Buffer::StrideU() const {
292 return stride_u_;
293 }
StrideV() const294 int I210Buffer::StrideV() const {
295 return stride_v_;
296 }
297
MutableDataY()298 uint16_t* I210Buffer::MutableDataY() {
299 return const_cast<uint16_t*>(DataY());
300 }
MutableDataU()301 uint16_t* I210Buffer::MutableDataU() {
302 return const_cast<uint16_t*>(DataU());
303 }
MutableDataV()304 uint16_t* I210Buffer::MutableDataV() {
305 return const_cast<uint16_t*>(DataV());
306 }
307
CropAndScaleFrom(const I210BufferInterface & src,int offset_x,int offset_y,int crop_width,int crop_height)308 void I210Buffer::CropAndScaleFrom(const I210BufferInterface& src,
309 int offset_x,
310 int offset_y,
311 int crop_width,
312 int crop_height) {
313 RTC_CHECK_LE(crop_width, src.width());
314 RTC_CHECK_LE(crop_height, src.height());
315 RTC_CHECK_LE(crop_width + offset_x, src.width());
316 RTC_CHECK_LE(crop_height + offset_y, src.height());
317 RTC_CHECK_GE(offset_x, 0);
318 RTC_CHECK_GE(offset_y, 0);
319 RTC_CHECK_GE(crop_width, 0);
320 RTC_CHECK_GE(crop_height, 0);
321
322 // Make sure offset is even so that u/v plane becomes aligned.
323 const int uv_offset_x = offset_x / 2;
324 const int uv_offset_y = offset_y;
325 offset_x = uv_offset_x * 2;
326
327 const uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
328 const uint16_t* u_plane =
329 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
330 const uint16_t* v_plane =
331 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
332 int res = libyuv::I422Scale_16(
333 y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, src.StrideV(),
334 crop_width, crop_height, MutableDataY(), StrideY(), MutableDataU(),
335 StrideU(), MutableDataV(), StrideV(), width(), height(),
336 libyuv::kFilterBox);
337
338 RTC_DCHECK_EQ(res, 0);
339 }
340
ScaleFrom(const I210BufferInterface & src)341 void I210Buffer::ScaleFrom(const I210BufferInterface& src) {
342 CropAndScaleFrom(src, 0, 0, src.width(), src.height());
343 }
344
345 } // namespace webrtc
346