blob: d387a4c4e9d4fa2651cd02c4079122aef7c53a47 [file] [log] [blame]
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +01001/*
2 * Copyright (c) 2021 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/i422_buffer.h"
11
12#include <string.h>
13
14#include <algorithm>
15#include <utility>
16
Niels Möller7c8c4db2022-06-13 10:36:38 +020017#include "api/make_ref_counted.h"
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +010018#include "api/video/i420_buffer.h"
19#include "rtc_base/checks.h"
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +010020#include "third_party/libyuv/include/libyuv/convert.h"
21#include "third_party/libyuv/include/libyuv/planar_functions.h"
22#include "third_party/libyuv/include/libyuv/scale.h"
23
24// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
25static const int kBufferAlignment = 64;
26
27namespace webrtc {
28
29namespace {
30
31int I422DataSize(int height, int stride_y, int stride_u, int stride_v) {
32 return stride_y * height + stride_u * height + stride_v * height;
33}
34
Sergio Garcia Murillo00112742022-04-04 12:41:24 +020035// TODO(sergio.garcia.murillo@gmail.com): Remove as soon it is available in
36// libyuv. Due to the rotate&scale required, this function may not be merged in
37// to libyuv inmediatelly.
38// https://bugs.chromium.org/p/libyuv/issues/detail?id=926
39int webrtcI422Rotate(const uint8_t* src_y,
40 int src_stride_y,
41 const uint8_t* src_u,
42 int src_stride_u,
43 const uint8_t* src_v,
44 int src_stride_v,
45 uint8_t* dst_y,
46 int dst_stride_y,
47 uint8_t* dst_u,
48 int dst_stride_u,
49 uint8_t* dst_v,
50 int dst_stride_v,
51 int width,
52 int height,
53 enum libyuv::RotationMode mode) {
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +010054 int halfwidth = (width + 1) >> 1;
55 int halfheight = (height + 1) >> 1;
56 if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
57 !dst_u || !dst_v) {
58 return -1;
59 }
60 // Negative height means invert the image.
61 if (height < 0) {
62 height = -height;
63 src_y = src_y + (height - 1) * src_stride_y;
64 src_u = src_u + (height - 1) * src_stride_u;
65 src_v = src_v + (height - 1) * src_stride_v;
66 src_stride_y = -src_stride_y;
67 src_stride_u = -src_stride_u;
68 src_stride_v = -src_stride_v;
69 }
70
71 switch (mode) {
72 case libyuv::kRotate0:
73 // copy frame
74 libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width,
75 height);
76 libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
77 height);
78 libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
79 height);
80 return 0;
81 case libyuv::kRotate90:
82 // We need to rotate and rescale, we use plane Y as temporal storage.
83 libyuv::RotatePlane90(src_u, src_stride_u, dst_y, height, halfwidth,
84 height);
85 libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_u, halfheight,
86 halfheight, width, libyuv::kFilterBilinear);
87 libyuv::RotatePlane90(src_v, src_stride_v, dst_y, height, halfwidth,
88 height);
89 libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_v, halfheight,
90 halfheight, width, libyuv::kFilterLinear);
91 libyuv::RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width,
92 height);
93 return 0;
94 case libyuv::kRotate270:
95 // We need to rotate and rescale, we use plane Y as temporal storage.
96 libyuv::RotatePlane270(src_u, src_stride_u, dst_y, height, halfwidth,
97 height);
98 libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_u, halfheight,
99 halfheight, width, libyuv::kFilterBilinear);
100 libyuv::RotatePlane270(src_v, src_stride_v, dst_y, height, halfwidth,
101 height);
102 libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_v, halfheight,
103 halfheight, width, libyuv::kFilterLinear);
104 libyuv::RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width,
105 height);
106
107 return 0;
108 case libyuv::kRotate180:
109 libyuv::RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width,
110 height);
111 libyuv::RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u,
112 halfwidth, height);
113 libyuv::RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v,
114 halfwidth, height);
115 return 0;
116 default:
117 break;
118 }
119 return -1;
120}
121
Sergio Garcia Murillo00112742022-04-04 12:41:24 +0200122// TODO(sergio.garcia.murillo@gmail.com): Remove this function with libyuv one
123// as soon as the dependency is updated.
124int webrtcI422Scale(const uint8_t* src_y,
125 int src_stride_y,
126 const uint8_t* src_u,
127 int src_stride_u,
128 const uint8_t* src_v,
129 int src_stride_v,
130 int src_width,
131 int src_height,
132 uint8_t* dst_y,
133 int dst_stride_y,
134 uint8_t* dst_u,
135 int dst_stride_u,
136 uint8_t* dst_v,
137 int dst_stride_v,
138 int dst_width,
139 int dst_height,
140 enum libyuv::FilterMode filtering) {
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +0100141 if (!src_y || !src_u || !src_v || src_width <= 0 || src_height == 0 ||
142 src_width > 32768 || src_height > 32768 || !dst_y || !dst_u || !dst_v ||
143 dst_width <= 0 || dst_height <= 0) {
144 return -1;
145 }
146 int src_halfwidth = (src_width + 1) >> 1;
147 int dst_halfwidth = (dst_width + 1) >> 1;
148
149 libyuv::ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y,
150 dst_stride_y, dst_width, dst_height, filtering);
151 libyuv::ScalePlane(src_u, src_stride_u, src_halfwidth, src_height, dst_u,
152 dst_stride_u, dst_halfwidth, dst_height, filtering);
153 libyuv::ScalePlane(src_v, src_stride_v, src_halfwidth, src_height, dst_v,
154 dst_stride_v, dst_halfwidth, dst_height, filtering);
155 return 0;
156}
157} // namespace
158
159I422Buffer::I422Buffer(int width, int height)
160 : I422Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {}
161
162I422Buffer::I422Buffer(int width,
163 int height,
164 int stride_y,
165 int stride_u,
166 int stride_v)
167 : width_(width),
168 height_(height),
169 stride_y_(stride_y),
170 stride_u_(stride_u),
171 stride_v_(stride_v),
172 data_(static_cast<uint8_t*>(
173 AlignedMalloc(I422DataSize(height, stride_y, stride_u, stride_v),
174 kBufferAlignment))) {
175 RTC_DCHECK_GT(width, 0);
176 RTC_DCHECK_GT(height, 0);
177 RTC_DCHECK_GE(stride_y, width);
178 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
179 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
180}
181
182I422Buffer::~I422Buffer() {}
183
184// static
185rtc::scoped_refptr<I422Buffer> I422Buffer::Create(int width, int height) {
186 return rtc::make_ref_counted<I422Buffer>(width, height);
187}
188
189// static
190rtc::scoped_refptr<I422Buffer> I422Buffer::Create(int width,
191 int height,
192 int stride_y,
193 int stride_u,
194 int stride_v) {
195 return rtc::make_ref_counted<I422Buffer>(width, height, stride_y, stride_u,
196 stride_v);
197}
198
199// static
200rtc::scoped_refptr<I422Buffer> I422Buffer::Copy(
201 const I422BufferInterface& source) {
202 return Copy(source.width(), source.height(), source.DataY(), source.StrideY(),
203 source.DataU(), source.StrideU(), source.DataV(),
204 source.StrideV());
205}
206
207// static
208rtc::scoped_refptr<I422Buffer> I422Buffer::Copy(
209 const I420BufferInterface& source) {
210 const int width = source.width();
211 const int height = source.height();
212 rtc::scoped_refptr<I422Buffer> buffer = Create(width, height);
213 RTC_CHECK_EQ(
214 0, libyuv::I420ToI422(
215 source.DataY(), source.StrideY(), source.DataU(), source.StrideU(),
216 source.DataV(), source.StrideV(), buffer->MutableDataY(),
217 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
218 buffer->MutableDataV(), buffer->StrideV(), width, height));
219 return buffer;
220}
221
222// static
223rtc::scoped_refptr<I422Buffer> I422Buffer::Copy(int width,
224 int height,
225 const uint8_t* data_y,
226 int stride_y,
227 const uint8_t* data_u,
228 int stride_u,
229 const uint8_t* data_v,
230 int stride_v) {
231 // Note: May use different strides than the input data.
232 rtc::scoped_refptr<I422Buffer> buffer = Create(width, height);
233 RTC_CHECK_EQ(0, libyuv::I422Copy(data_y, stride_y, data_u, stride_u, data_v,
234 stride_v, buffer->MutableDataY(),
235 buffer->StrideY(), buffer->MutableDataU(),
236 buffer->StrideU(), buffer->MutableDataV(),
237 buffer->StrideV(), width, height));
238 return buffer;
239}
240
241// static
242rtc::scoped_refptr<I422Buffer> I422Buffer::Rotate(
243 const I422BufferInterface& src,
244 VideoRotation rotation) {
245 RTC_CHECK(src.DataY());
246 RTC_CHECK(src.DataU());
247 RTC_CHECK(src.DataV());
248
249 int rotated_width = src.width();
250 int rotated_height = src.height();
251 if (rotation == webrtc::kVideoRotation_90 ||
252 rotation == webrtc::kVideoRotation_270) {
253 std::swap(rotated_width, rotated_height);
254 }
255
256 rtc::scoped_refptr<webrtc::I422Buffer> buffer =
257 I422Buffer::Create(rotated_width, rotated_height);
258
Sergio Garcia Murillo00112742022-04-04 12:41:24 +0200259 RTC_CHECK_EQ(0,
260 webrtcI422Rotate(
261 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(),
262 src.DataV(), src.StrideV(), buffer->MutableDataY(),
263 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
264 buffer->MutableDataV(), buffer->StrideV(), src.width(),
265 src.height(), static_cast<libyuv::RotationMode>(rotation)));
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +0100266
267 return buffer;
268}
269
270rtc::scoped_refptr<I420BufferInterface> I422Buffer::ToI420() {
271 rtc::scoped_refptr<I420Buffer> i420_buffer =
272 I420Buffer::Create(width(), height());
273 libyuv::I422ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
274 i420_buffer->MutableDataY(), i420_buffer->StrideY(),
275 i420_buffer->MutableDataU(), i420_buffer->StrideU(),
276 i420_buffer->MutableDataV(), i420_buffer->StrideV(),
277 width(), height());
278 return i420_buffer;
279}
280
281void I422Buffer::InitializeData() {
282 memset(data_.get(), 0,
283 I422DataSize(height_, stride_y_, stride_u_, stride_v_));
284}
285
286int I422Buffer::width() const {
287 return width_;
288}
289
290int I422Buffer::height() const {
291 return height_;
292}
293
294const uint8_t* I422Buffer::DataY() const {
295 return data_.get();
296}
297const uint8_t* I422Buffer::DataU() const {
298 return data_.get() + stride_y_ * height_;
299}
300const uint8_t* I422Buffer::DataV() const {
301 return data_.get() + stride_y_ * height_ + stride_u_ * height_;
302}
303
304int I422Buffer::StrideY() const {
305 return stride_y_;
306}
307int I422Buffer::StrideU() const {
308 return stride_u_;
309}
310int I422Buffer::StrideV() const {
311 return stride_v_;
312}
313
314uint8_t* I422Buffer::MutableDataY() {
315 return const_cast<uint8_t*>(DataY());
316}
317uint8_t* I422Buffer::MutableDataU() {
318 return const_cast<uint8_t*>(DataU());
319}
320uint8_t* I422Buffer::MutableDataV() {
321 return const_cast<uint8_t*>(DataV());
322}
323
324void I422Buffer::CropAndScaleFrom(const I422BufferInterface& src,
325 int offset_x,
326 int offset_y,
327 int crop_width,
328 int crop_height) {
329 RTC_CHECK_LE(crop_width, src.width());
330 RTC_CHECK_LE(crop_height, src.height());
331 RTC_CHECK_LE(crop_width + offset_x, src.width());
332 RTC_CHECK_LE(crop_height + offset_y, src.height());
333 RTC_CHECK_GE(offset_x, 0);
334 RTC_CHECK_GE(offset_y, 0);
335
336 // Make sure offset is even so that u/v plane becomes aligned.
337 const int uv_offset_x = offset_x / 2;
338 const int uv_offset_y = offset_y;
339 offset_x = uv_offset_x * 2;
340
341 const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
342 const uint8_t* u_plane =
343 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
344 const uint8_t* v_plane =
345 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
Sergio Garcia Murillo00112742022-04-04 12:41:24 +0200346 int res =
347 webrtcI422Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane,
Sergio Garcia Murillob63536f2022-03-25 09:04:09 +0100348 src.StrideV(), crop_width, crop_height, MutableDataY(),
349 StrideY(), MutableDataU(), StrideU(), MutableDataV(),
350 StrideV(), width(), height(), libyuv::kFilterBox);
351
352 RTC_DCHECK_EQ(res, 0);
353}
354
355} // namespace webrtc