xref: /aosp_15_r20/external/webrtc/api/video/i210_buffer.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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