blob: d9f73085f67126325c636d4a9f8d572f10e321f4 [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>
13
14#include <algorithm>
15#include <utility>
16
nisseaf916892017-01-10 07:44:26 -080017#include "libyuv/convert.h"
18#include "libyuv/planar_functions.h"
19#include "libyuv/scale.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/checks.h"
21#include "rtc_base/keep_ref_until_done.h"
nisseaf916892017-01-10 07:44:26 -080022
23// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
24static const int kBufferAlignment = 64;
25
26namespace webrtc {
27
28namespace {
29
30int I420DataSize(int height, int stride_y, int stride_u, int stride_v) {
31 return stride_y * height + (stride_u + stride_v) * ((height + 1) / 2);
32}
33
34} // namespace
35
36I420Buffer::I420Buffer(int width, int height)
37 : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {
38}
39
40I420Buffer::I420Buffer(int width,
41 int height,
42 int stride_y,
43 int stride_u,
44 int stride_v)
45 : width_(width),
46 height_(height),
47 stride_y_(stride_y),
48 stride_u_(stride_u),
49 stride_v_(stride_v),
50 data_(static_cast<uint8_t*>(AlignedMalloc(
51 I420DataSize(height, stride_y, stride_u, stride_v),
52 kBufferAlignment))) {
53 RTC_DCHECK_GT(width, 0);
54 RTC_DCHECK_GT(height, 0);
55 RTC_DCHECK_GE(stride_y, width);
56 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
57 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
58}
59
60I420Buffer::~I420Buffer() {
61}
62
63// static
64rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
65 return new rtc::RefCountedObject<I420Buffer>(width, height);
66}
67
68// static
69rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width,
70 int height,
71 int stride_y,
72 int stride_u,
73 int stride_v) {
74 return new rtc::RefCountedObject<I420Buffer>(
75 width, height, stride_y, stride_u, stride_v);
76}
77
78// static
79rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
magjed3f075492017-06-01 10:02:26 -070080 const I420BufferInterface& source) {
nisseaf916892017-01-10 07:44:26 -080081 return Copy(source.width(), source.height(),
82 source.DataY(), source.StrideY(),
83 source.DataU(), source.StrideU(),
84 source.DataV(), source.StrideV());
85}
86
87// static
88rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
89 int width, int height,
90 const uint8_t* data_y, int stride_y,
91 const uint8_t* data_u, int stride_u,
92 const uint8_t* data_v, int stride_v) {
93 // Note: May use different strides than the input data.
94 rtc::scoped_refptr<I420Buffer> buffer = Create(width, height);
95 RTC_CHECK_EQ(0, libyuv::I420Copy(data_y, stride_y,
96 data_u, stride_u,
97 data_v, stride_v,
98 buffer->MutableDataY(), buffer->StrideY(),
99 buffer->MutableDataU(), buffer->StrideU(),
100 buffer->MutableDataV(), buffer->StrideV(),
101 width, height));
102 return buffer;
103}
104
105// static
106rtc::scoped_refptr<I420Buffer> I420Buffer::Rotate(
magjed3f075492017-06-01 10:02:26 -0700107 const I420BufferInterface& src,
108 VideoRotation rotation) {
nisseaf916892017-01-10 07:44:26 -0800109 RTC_CHECK(src.DataY());
110 RTC_CHECK(src.DataU());
111 RTC_CHECK(src.DataV());
112
113 int rotated_width = src.width();
114 int rotated_height = src.height();
115 if (rotation == webrtc::kVideoRotation_90 ||
116 rotation == webrtc::kVideoRotation_270) {
117 std::swap(rotated_width, rotated_height);
118 }
119
120 rtc::scoped_refptr<webrtc::I420Buffer> buffer =
121 I420Buffer::Create(rotated_width, rotated_height);
122
123 RTC_CHECK_EQ(0, libyuv::I420Rotate(
124 src.DataY(), src.StrideY(),
125 src.DataU(), src.StrideU(),
126 src.DataV(), src.StrideV(),
127 buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
128 buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
129 src.width(), src.height(),
130 static_cast<libyuv::RotationMode>(rotation)));
131
132 return buffer;
133}
134
nisseaf916892017-01-10 07:44:26 -0800135void I420Buffer::InitializeData() {
136 memset(data_.get(), 0,
137 I420DataSize(height_, stride_y_, stride_u_, stride_v_));
138}
139
140int I420Buffer::width() const {
141 return width_;
142}
143
144int I420Buffer::height() const {
145 return height_;
146}
147
148const uint8_t* I420Buffer::DataY() const {
149 return data_.get();
150}
151const uint8_t* I420Buffer::DataU() const {
152 return data_.get() + stride_y_ * height_;
153}
154const uint8_t* I420Buffer::DataV() const {
155 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
156}
157
158int I420Buffer::StrideY() const {
159 return stride_y_;
160}
161int I420Buffer::StrideU() const {
162 return stride_u_;
163}
164int I420Buffer::StrideV() const {
165 return stride_v_;
166}
167
nisseaf916892017-01-10 07:44:26 -0800168uint8_t* I420Buffer::MutableDataY() {
169 return const_cast<uint8_t*>(DataY());
170}
171uint8_t* I420Buffer::MutableDataU() {
172 return const_cast<uint8_t*>(DataU());
173}
174uint8_t* I420Buffer::MutableDataV() {
175 return const_cast<uint8_t*>(DataV());
176}
177
178// static
179void I420Buffer::SetBlack(I420Buffer* buffer) {
180 RTC_CHECK(libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(),
181 buffer->MutableDataU(), buffer->StrideU(),
182 buffer->MutableDataV(), buffer->StrideV(),
183 0, 0, buffer->width(), buffer->height(),
184 0, 128, 128) == 0);
185}
186
magjed3f075492017-06-01 10:02:26 -0700187void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src,
188 int offset_x,
189 int offset_y,
190 int crop_width,
191 int crop_height) {
nisseaf916892017-01-10 07:44:26 -0800192 RTC_CHECK_LE(crop_width, src.width());
193 RTC_CHECK_LE(crop_height, src.height());
194 RTC_CHECK_LE(crop_width + offset_x, src.width());
195 RTC_CHECK_LE(crop_height + offset_y, src.height());
196 RTC_CHECK_GE(offset_x, 0);
197 RTC_CHECK_GE(offset_y, 0);
198
199 // Make sure offset is even so that u/v plane becomes aligned.
200 const int uv_offset_x = offset_x / 2;
201 const int uv_offset_y = offset_y / 2;
202 offset_x = uv_offset_x * 2;
203 offset_y = uv_offset_y * 2;
204
205 const uint8_t* y_plane =
206 src.DataY() + src.StrideY() * offset_y + offset_x;
207 const uint8_t* u_plane =
208 src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
209 const uint8_t* v_plane =
210 src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
211 int res = libyuv::I420Scale(y_plane, src.StrideY(),
212 u_plane, src.StrideU(),
213 v_plane, src.StrideV(),
214 crop_width, crop_height,
215 MutableDataY(), StrideY(),
216 MutableDataU(), StrideU(),
217 MutableDataV(), StrideV(),
218 width(), height(), libyuv::kFilterBox);
219
220 RTC_DCHECK_EQ(res, 0);
221}
222
magjed3f075492017-06-01 10:02:26 -0700223void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src) {
nisseaf916892017-01-10 07:44:26 -0800224 const int crop_width =
225 std::min(src.width(), width() * src.height() / height());
226 const int crop_height =
227 std::min(src.height(), height() * src.width() / width());
228
229 CropAndScaleFrom(
230 src,
231 (src.width() - crop_width) / 2, (src.height() - crop_height) / 2,
232 crop_width, crop_height);
233}
234
magjed3f075492017-06-01 10:02:26 -0700235void I420Buffer::ScaleFrom(const I420BufferInterface& src) {
nisseaf916892017-01-10 07:44:26 -0800236 CropAndScaleFrom(src, 0, 0, src.width(), src.height());
237}
238
239} // namespace webrtc