blob: c83c8a0c0bcb33eda891e1b9ff3d2e3fcfda0375 [file] [log] [blame]
Sergio Garcia Murillo8545eba2022-06-17 11:48:14 +02001/*
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.
22static const int kBufferAlignment = 64;
23static const int kBytesPerPixel = 2;
24
25namespace webrtc {
26
27namespace {
28
29int 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
Sergio Garcia Murillo8545eba2022-06-17 11:48:14 +020034} // namespace
35
36I210Buffer::I210Buffer(int width,
37 int height,
38 int stride_y,
39 int stride_u,
40 int stride_v)
41 : width_(width),
42 height_(height),
43 stride_y_(stride_y),
44 stride_u_(stride_u),
45 stride_v_(stride_v),
46 data_(static_cast<uint16_t*>(
47 AlignedMalloc(I210DataSize(height, stride_y, stride_u, stride_v),
48 kBufferAlignment))) {
49 RTC_DCHECK_GT(width, 0);
50 RTC_DCHECK_GT(height, 0);
51 RTC_DCHECK_GE(stride_y, width);
52 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
53 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
54}
55
56I210Buffer::~I210Buffer() {}
57
58// static
59rtc::scoped_refptr<I210Buffer> I210Buffer::Create(int width, int height) {
60 return rtc::make_ref_counted<I210Buffer>(width, height, width,
61 (width + 1) / 2, (width + 1) / 2);
62}
63
64// static
65rtc::scoped_refptr<I210Buffer> I210Buffer::Copy(
66 const I210BufferInterface& source) {
67 const int width = source.width();
68 const int height = source.height();
69 rtc::scoped_refptr<I210Buffer> buffer = Create(width, height);
70 RTC_CHECK_EQ(
71 0, libyuv::I210Copy(
72 source.DataY(), source.StrideY(), source.DataU(), source.StrideU(),
73 source.DataV(), source.StrideV(), buffer->MutableDataY(),
74 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
75 buffer->MutableDataV(), buffer->StrideV(), width, height));
76 return buffer;
77}
78
79// static
80rtc::scoped_refptr<I210Buffer> I210Buffer::Copy(
81 const I420BufferInterface& source) {
82 const int width = source.width();
83 const int height = source.height();
84 auto i422buffer = I422Buffer::Copy(source);
85 rtc::scoped_refptr<I210Buffer> buffer = Create(width, height);
86 RTC_CHECK_EQ(0, libyuv::I422ToI210(i422buffer->DataY(), i422buffer->StrideY(),
87 i422buffer->DataU(), i422buffer->StrideU(),
88 i422buffer->DataV(), i422buffer->StrideV(),
89 buffer->MutableDataY(), buffer->StrideY(),
90 buffer->MutableDataU(), buffer->StrideU(),
91 buffer->MutableDataV(), buffer->StrideV(),
92 width, height));
93 return buffer;
94}
95
96// static
97rtc::scoped_refptr<I210Buffer> I210Buffer::Rotate(
98 const I210BufferInterface& src,
99 VideoRotation rotation) {
100 RTC_CHECK(src.DataY());
101 RTC_CHECK(src.DataU());
102 RTC_CHECK(src.DataV());
103
104 int rotated_width = src.width();
105 int rotated_height = src.height();
106 if (rotation == webrtc::kVideoRotation_90 ||
107 rotation == webrtc::kVideoRotation_270) {
108 std::swap(rotated_width, rotated_height);
109 }
110
111 rtc::scoped_refptr<webrtc::I210Buffer> buffer =
112 I210Buffer::Create(rotated_width, rotated_height);
113
114 RTC_CHECK_EQ(0,
Sergio Garcia Murillobfc26c62023-01-09 16:08:20 +0100115 libyuv::I210Rotate(
Sergio Garcia Murillo8545eba2022-06-17 11:48:14 +0200116 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(),
117 src.DataV(), src.StrideV(), buffer->MutableDataY(),
118 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
119 buffer->MutableDataV(), buffer->StrideV(), src.width(),
120 src.height(), static_cast<libyuv::RotationMode>(rotation)));
121
122 return buffer;
123}
124
125rtc::scoped_refptr<I420BufferInterface> I210Buffer::ToI420() {
126 rtc::scoped_refptr<I420Buffer> i420_buffer =
127 I420Buffer::Create(width(), height());
128 libyuv::I210ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
129 i420_buffer->MutableDataY(), i420_buffer->StrideY(),
130 i420_buffer->MutableDataU(), i420_buffer->StrideU(),
131 i420_buffer->MutableDataV(), i420_buffer->StrideV(),
132 width(), height());
133 return i420_buffer;
134}
135
136int I210Buffer::width() const {
137 return width_;
138}
139
140int I210Buffer::height() const {
141 return height_;
142}
143
144const uint16_t* I210Buffer::DataY() const {
145 return data_.get();
146}
147const uint16_t* I210Buffer::DataU() const {
148 return data_.get() + stride_y_ * height_;
149}
150const uint16_t* I210Buffer::DataV() const {
151 return data_.get() + stride_y_ * height_ + stride_u_ * height_;
152}
153
154int I210Buffer::StrideY() const {
155 return stride_y_;
156}
157int I210Buffer::StrideU() const {
158 return stride_u_;
159}
160int I210Buffer::StrideV() const {
161 return stride_v_;
162}
163
164uint16_t* I210Buffer::MutableDataY() {
165 return const_cast<uint16_t*>(DataY());
166}
167uint16_t* I210Buffer::MutableDataU() {
168 return const_cast<uint16_t*>(DataU());
169}
170uint16_t* I210Buffer::MutableDataV() {
171 return const_cast<uint16_t*>(DataV());
172}
173
174void I210Buffer::CropAndScaleFrom(const I210BufferInterface& src,
175 int offset_x,
176 int offset_y,
177 int crop_width,
178 int crop_height) {
179 RTC_CHECK_LE(crop_width, src.width());
180 RTC_CHECK_LE(crop_height, src.height());
181 RTC_CHECK_LE(crop_width + offset_x, src.width());
182 RTC_CHECK_LE(crop_height + offset_y, src.height());
183 RTC_CHECK_GE(offset_x, 0);
184 RTC_CHECK_GE(offset_y, 0);
185 RTC_CHECK_GE(crop_width, 0);
186 RTC_CHECK_GE(crop_height, 0);
187
188 // Make sure offset is even so that u/v plane becomes aligned.
189 const int uv_offset_x = offset_x / 2;
190 const int uv_offset_y = offset_y;
191 offset_x = uv_offset_x * 2;
192
193 const uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
194 const uint16_t* u_plane =
195 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
196 const uint16_t* v_plane =
197 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
198 int res = libyuv::I422Scale_16(
199 y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, src.StrideV(),
200 crop_width, crop_height, MutableDataY(), StrideY(), MutableDataU(),
201 StrideU(), MutableDataV(), StrideV(), width(), height(),
202 libyuv::kFilterBox);
203
204 RTC_DCHECK_EQ(res, 0);
205}
206
207void I210Buffer::ScaleFrom(const I210BufferInterface& src) {
208 CropAndScaleFrom(src, 0, 0, src.width(), src.height());
209}
210
211} // namespace webrtc