blob: c468f51f97ec4bdacdb71324c24503662047a7a0 [file] [log] [blame]
nisseaf916892017-01-10 07:44:26 -08001/*
2 * Copyright (c) 2015 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 */
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020010#include "api/video/i420_buffer.h"
nisseaf916892017-01-10 07:44:26 -080011
12#include <string.h>
nisseaf916892017-01-10 07:44:26 -080013#include <algorithm>
14#include <utility>
15
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/checks.h"
Yves Gerey3e707812018-11-28 16:47:49 +010017#include "rtc_base/refcountedobject.h"
Mirko Bonadei65432062017-12-11 09:32:13 +010018#include "third_party/libyuv/include/libyuv/convert.h"
19#include "third_party/libyuv/include/libyuv/planar_functions.h"
20#include "third_party/libyuv/include/libyuv/scale.h"
nisseaf916892017-01-10 07:44:26 -080021
22// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
23static const int kBufferAlignment = 64;
24
25namespace webrtc {
26
27namespace {
28
29int I420DataSize(int height, int stride_y, int stride_u, int stride_v) {
30 return stride_y * height + (stride_u + stride_v) * ((height + 1) / 2);
31}
32
33} // namespace
34
35I420Buffer::I420Buffer(int width, int height)
Yves Gerey665174f2018-06-19 15:03:05 +020036 : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {}
nisseaf916892017-01-10 07:44:26 -080037
38I420Buffer::I420Buffer(int width,
39 int height,
40 int stride_y,
41 int stride_u,
42 int stride_v)
43 : width_(width),
44 height_(height),
45 stride_y_(stride_y),
46 stride_u_(stride_u),
47 stride_v_(stride_v),
Yves Gerey665174f2018-06-19 15:03:05 +020048 data_(static_cast<uint8_t*>(
49 AlignedMalloc(I420DataSize(height, stride_y, stride_u, stride_v),
50 kBufferAlignment))) {
nisseaf916892017-01-10 07:44:26 -080051 RTC_DCHECK_GT(width, 0);
52 RTC_DCHECK_GT(height, 0);
53 RTC_DCHECK_GE(stride_y, width);
54 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
55 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
56}
57
Yves Gerey665174f2018-06-19 15:03:05 +020058I420Buffer::~I420Buffer() {}
nisseaf916892017-01-10 07:44:26 -080059
60// static
61rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
62 return new rtc::RefCountedObject<I420Buffer>(width, height);
63}
64
65// static
66rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width,
67 int height,
68 int stride_y,
69 int stride_u,
70 int stride_v) {
Yves Gerey665174f2018-06-19 15:03:05 +020071 return new rtc::RefCountedObject<I420Buffer>(width, height, stride_y,
72 stride_u, stride_v);
nisseaf916892017-01-10 07:44:26 -080073}
74
75// static
76rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
magjed3f075492017-06-01 10:02:26 -070077 const I420BufferInterface& source) {
Yves Gerey665174f2018-06-19 15:03:05 +020078 return Copy(source.width(), source.height(), source.DataY(), source.StrideY(),
79 source.DataU(), source.StrideU(), source.DataV(),
80 source.StrideV());
nisseaf916892017-01-10 07:44:26 -080081}
82
83// static
Yves Gerey665174f2018-06-19 15:03:05 +020084rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(int width,
85 int height,
86 const uint8_t* data_y,
87 int stride_y,
88 const uint8_t* data_u,
89 int stride_u,
90 const uint8_t* data_v,
91 int stride_v) {
nisseaf916892017-01-10 07:44:26 -080092 // Note: May use different strides than the input data.
93 rtc::scoped_refptr<I420Buffer> buffer = Create(width, height);
Yves Gerey665174f2018-06-19 15:03:05 +020094 RTC_CHECK_EQ(0, libyuv::I420Copy(data_y, stride_y, data_u, stride_u, data_v,
95 stride_v, buffer->MutableDataY(),
96 buffer->StrideY(), buffer->MutableDataU(),
97 buffer->StrideU(), buffer->MutableDataV(),
98 buffer->StrideV(), width, height));
nisseaf916892017-01-10 07:44:26 -080099 return buffer;
100}
101
102// static
103rtc::scoped_refptr<I420Buffer> I420Buffer::Rotate(
magjed3f075492017-06-01 10:02:26 -0700104 const I420BufferInterface& src,
105 VideoRotation rotation) {
nisseaf916892017-01-10 07:44:26 -0800106 RTC_CHECK(src.DataY());
107 RTC_CHECK(src.DataU());
108 RTC_CHECK(src.DataV());
109
110 int rotated_width = src.width();
111 int rotated_height = src.height();
112 if (rotation == webrtc::kVideoRotation_90 ||
113 rotation == webrtc::kVideoRotation_270) {
114 std::swap(rotated_width, rotated_height);
115 }
116
117 rtc::scoped_refptr<webrtc::I420Buffer> buffer =
118 I420Buffer::Create(rotated_width, rotated_height);
119
Yves Gerey665174f2018-06-19 15:03:05 +0200120 RTC_CHECK_EQ(0,
121 libyuv::I420Rotate(
122 src.DataY(), src.StrideY(), src.DataU(), src.StrideU(),
123 src.DataV(), src.StrideV(), buffer->MutableDataY(),
124 buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
125 buffer->MutableDataV(), buffer->StrideV(), src.width(),
126 src.height(), static_cast<libyuv::RotationMode>(rotation)));
nisseaf916892017-01-10 07:44:26 -0800127
128 return buffer;
129}
130
nisseaf916892017-01-10 07:44:26 -0800131void I420Buffer::InitializeData() {
132 memset(data_.get(), 0,
133 I420DataSize(height_, stride_y_, stride_u_, stride_v_));
134}
135
136int I420Buffer::width() const {
137 return width_;
138}
139
140int I420Buffer::height() const {
141 return height_;
142}
143
144const uint8_t* I420Buffer::DataY() const {
145 return data_.get();
146}
147const uint8_t* I420Buffer::DataU() const {
148 return data_.get() + stride_y_ * height_;
149}
150const uint8_t* I420Buffer::DataV() const {
151 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
152}
153
154int I420Buffer::StrideY() const {
155 return stride_y_;
156}
157int I420Buffer::StrideU() const {
158 return stride_u_;
159}
160int I420Buffer::StrideV() const {
161 return stride_v_;
162}
163
nisseaf916892017-01-10 07:44:26 -0800164uint8_t* I420Buffer::MutableDataY() {
165 return const_cast<uint8_t*>(DataY());
166}
167uint8_t* I420Buffer::MutableDataU() {
168 return const_cast<uint8_t*>(DataU());
169}
170uint8_t* I420Buffer::MutableDataV() {
171 return const_cast<uint8_t*>(DataV());
172}
173
174// static
175void I420Buffer::SetBlack(I420Buffer* buffer) {
176 RTC_CHECK(libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(),
177 buffer->MutableDataU(), buffer->StrideU(),
Yves Gerey665174f2018-06-19 15:03:05 +0200178 buffer->MutableDataV(), buffer->StrideV(), 0, 0,
179 buffer->width(), buffer->height(), 0, 128,
180 128) == 0);
nisseaf916892017-01-10 07:44:26 -0800181}
182
magjed3f075492017-06-01 10:02:26 -0700183void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src,
184 int offset_x,
185 int offset_y,
186 int crop_width,
187 int crop_height) {
nisseaf916892017-01-10 07:44:26 -0800188 RTC_CHECK_LE(crop_width, src.width());
189 RTC_CHECK_LE(crop_height, src.height());
190 RTC_CHECK_LE(crop_width + offset_x, src.width());
191 RTC_CHECK_LE(crop_height + offset_y, src.height());
192 RTC_CHECK_GE(offset_x, 0);
193 RTC_CHECK_GE(offset_y, 0);
194
195 // Make sure offset is even so that u/v plane becomes aligned.
196 const int uv_offset_x = offset_x / 2;
197 const int uv_offset_y = offset_y / 2;
198 offset_x = uv_offset_x * 2;
199 offset_y = uv_offset_y * 2;
200
Yves Gerey665174f2018-06-19 15:03:05 +0200201 const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
nisseaf916892017-01-10 07:44:26 -0800202 const uint8_t* u_plane =
203 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
204 const uint8_t* v_plane =
205 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
Yves Gerey665174f2018-06-19 15:03:05 +0200206 int res =
207 libyuv::I420Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane,
208 src.StrideV(), crop_width, crop_height, MutableDataY(),
209 StrideY(), MutableDataU(), StrideU(), MutableDataV(),
210 StrideV(), width(), height(), libyuv::kFilterBox);
nisseaf916892017-01-10 07:44:26 -0800211
212 RTC_DCHECK_EQ(res, 0);
213}
214
magjed3f075492017-06-01 10:02:26 -0700215void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src) {
nisseaf916892017-01-10 07:44:26 -0800216 const int crop_width =
217 std::min(src.width(), width() * src.height() / height());
218 const int crop_height =
219 std::min(src.height(), height() * src.width() / width());
220
Yves Gerey665174f2018-06-19 15:03:05 +0200221 CropAndScaleFrom(src, (src.width() - crop_width) / 2,
222 (src.height() - crop_height) / 2, crop_width, crop_height);
nisseaf916892017-01-10 07:44:26 -0800223}
224
magjed3f075492017-06-01 10:02:26 -0700225void I420Buffer::ScaleFrom(const I420BufferInterface& src) {
nisseaf916892017-01-10 07:44:26 -0800226 CropAndScaleFrom(src, 0, 0, src.width(), src.height());
227}
228
Ilya Nikolaevskiya9216602018-12-21 14:21:08 +0100229void I420Buffer::PasteFrom(const I420BufferInterface& picture,
230 int offset_col,
231 int offset_row) {
232 RTC_CHECK_LE(picture.width() + offset_col, width());
233 RTC_CHECK_LE(picture.height() + offset_row, height());
234 RTC_CHECK_GE(offset_col, 0);
235 RTC_CHECK_GE(offset_row, 0);
236
237 // Pasted picture has to be aligned so subsumpled UV plane isn't corrupted.
238 RTC_CHECK(offset_col % 2 == 0);
239 RTC_CHECK(offset_row % 2 == 0);
240 RTC_CHECK(picture.width() % 2 == 0);
241 RTC_CHECK(picture.height() % 2 == 0);
242
243 libyuv::CopyPlane(picture.DataY(), picture.StrideY(),
244 MutableDataY() + StrideY() * offset_row + offset_col,
245 StrideY(), picture.width(), picture.height());
246
247 libyuv::CopyPlane(
248 picture.DataU(), picture.StrideU(),
249 MutableDataU() + StrideU() * offset_row / 2 + offset_col / 2, StrideU(),
250 picture.width() / 2, picture.height() / 2);
251
252 libyuv::CopyPlane(
253 picture.DataV(), picture.StrideV(),
254 MutableDataV() + StrideV() * offset_row / 2 + offset_col / 2, StrideV(),
255 picture.width() / 2, picture.height() / 2);
256}
257
nisseaf916892017-01-10 07:44:26 -0800258} // namespace webrtc