1 /*
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  */
10 #include "api/video/i420_buffer.h"
11 
12 #include <string.h>
13 
14 #include <algorithm>
15 #include <utility>
16 
17 #include "libyuv/convert.h"
18 #include "libyuv/planar_functions.h"
19 #include "libyuv/scale.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/keep_ref_until_done.h"
22 
23 // Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
24 static const int kBufferAlignment = 64;
25 
26 namespace webrtc {
27 
28 namespace {
29 
I420DataSize(int height,int stride_y,int stride_u,int stride_v)30 int 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 
I420Buffer(int width,int height)36 I420Buffer::I420Buffer(int width, int height)
37     : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {
38 }
39 
I420Buffer(int width,int height,int stride_y,int stride_u,int stride_v)40 I420Buffer::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 
~I420Buffer()60 I420Buffer::~I420Buffer() {
61 }
62 
63 // static
Create(int width,int height)64 rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
65   return new rtc::RefCountedObject<I420Buffer>(width, height);
66 }
67 
68 // static
Create(int width,int height,int stride_y,int stride_u,int stride_v)69 rtc::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
Copy(const I420BufferInterface & source)79 rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
80     const I420BufferInterface& source) {
81   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
Copy(int width,int height,const uint8_t * data_y,int stride_y,const uint8_t * data_u,int stride_u,const uint8_t * data_v,int stride_v)88 rtc::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
Rotate(const I420BufferInterface & src,VideoRotation rotation)106 rtc::scoped_refptr<I420Buffer> I420Buffer::Rotate(
107     const I420BufferInterface& src,
108     VideoRotation rotation) {
109   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 
InitializeData()135 void I420Buffer::InitializeData() {
136   memset(data_.get(), 0,
137          I420DataSize(height_, stride_y_, stride_u_, stride_v_));
138 }
139 
width() const140 int I420Buffer::width() const {
141   return width_;
142 }
143 
height() const144 int I420Buffer::height() const {
145   return height_;
146 }
147 
DataY() const148 const uint8_t* I420Buffer::DataY() const {
149   return data_.get();
150 }
DataU() const151 const uint8_t* I420Buffer::DataU() const {
152   return data_.get() + stride_y_ * height_;
153 }
DataV() const154 const uint8_t* I420Buffer::DataV() const {
155   return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
156 }
157 
StrideY() const158 int I420Buffer::StrideY() const {
159   return stride_y_;
160 }
StrideU() const161 int I420Buffer::StrideU() const {
162   return stride_u_;
163 }
StrideV() const164 int I420Buffer::StrideV() const {
165   return stride_v_;
166 }
167 
MutableDataY()168 uint8_t* I420Buffer::MutableDataY() {
169   return const_cast<uint8_t*>(DataY());
170 }
MutableDataU()171 uint8_t* I420Buffer::MutableDataU() {
172   return const_cast<uint8_t*>(DataU());
173 }
MutableDataV()174 uint8_t* I420Buffer::MutableDataV() {
175   return const_cast<uint8_t*>(DataV());
176 }
177 
178 // static
SetBlack(I420Buffer * buffer)179 void 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 
CropAndScaleFrom(const I420BufferInterface & src,int offset_x,int offset_y,int crop_width,int crop_height)187 void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src,
188                                   int offset_x,
189                                   int offset_y,
190                                   int crop_width,
191                                   int crop_height) {
192   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 
CropAndScaleFrom(const I420BufferInterface & src)223 void I420Buffer::CropAndScaleFrom(const I420BufferInterface& src) {
224   const int crop_width = height() ?
225       std::min(src.width(), width() * src.height() / height()) : src.width();
226   const int crop_height = width() ?
227       std::min(src.height(), height() * src.width() / width()) : src.height();
228 
229   CropAndScaleFrom(
230       src,
231       (src.width() - crop_width) / 2, (src.height() - crop_height) / 2,
232       crop_width, crop_height);
233 }
234 
ScaleFrom(const I420BufferInterface & src)235 void I420Buffer::ScaleFrom(const I420BufferInterface& src) {
236   CropAndScaleFrom(src, 0, 0, src.width(), src.height());
237 }
238 
239 }  // namespace webrtc
240