Line data Source code
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 "webrtc/api/video/i420_buffer.h"
11 :
12 : #include <string.h>
13 :
14 : #include <algorithm>
15 : #include <utility>
16 :
17 : #include "webrtc/base/checks.h"
18 : #include "webrtc/base/keep_ref_until_done.h"
19 : #include "libyuv/convert.h"
20 : #include "libyuv/planar_functions.h"
21 : #include "libyuv/scale.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 :
30 0 : int I420DataSize(int height, int stride_y, int stride_u, int stride_v) {
31 0 : return stride_y * height + (stride_u + stride_v) * ((height + 1) / 2);
32 : }
33 :
34 : } // namespace
35 :
36 0 : I420Buffer::I420Buffer(int width, int height)
37 0 : : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {
38 0 : }
39 :
40 0 : I420Buffer::I420Buffer(int width,
41 : int height,
42 : int stride_y,
43 : int stride_u,
44 0 : 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 0 : data_(static_cast<uint8_t*>(AlignedMalloc(
51 0 : I420DataSize(height, stride_y, stride_u, stride_v),
52 0 : kBufferAlignment))) {
53 0 : RTC_DCHECK_GT(width, 0);
54 0 : RTC_DCHECK_GT(height, 0);
55 0 : RTC_DCHECK_GE(stride_y, width);
56 0 : RTC_DCHECK_GE(stride_u, (width + 1) / 2);
57 0 : RTC_DCHECK_GE(stride_v, (width + 1) / 2);
58 0 : }
59 :
60 0 : I420Buffer::~I420Buffer() {
61 0 : }
62 :
63 : // static
64 0 : rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
65 0 : return new rtc::RefCountedObject<I420Buffer>(width, height);
66 : }
67 :
68 : // static
69 0 : 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 0 : width, height, stride_y, stride_u, stride_v);
76 : }
77 :
78 : // static
79 0 : rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
80 : const VideoFrameBuffer& source) {
81 0 : return Copy(source.width(), source.height(),
82 0 : source.DataY(), source.StrideY(),
83 0 : source.DataU(), source.StrideU(),
84 0 : source.DataV(), source.StrideV());
85 : }
86 :
87 : // static
88 0 : 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 0 : rtc::scoped_refptr<I420Buffer> buffer = Create(width, height);
95 0 : 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 0 : width, height));
102 0 : return buffer;
103 : }
104 :
105 : // static
106 0 : rtc::scoped_refptr<I420Buffer> I420Buffer::Rotate(
107 : const VideoFrameBuffer& src, VideoRotation rotation) {
108 0 : RTC_CHECK(src.DataY());
109 0 : RTC_CHECK(src.DataU());
110 0 : RTC_CHECK(src.DataV());
111 :
112 0 : int rotated_width = src.width();
113 0 : int rotated_height = src.height();
114 0 : if (rotation == webrtc::kVideoRotation_90 ||
115 : rotation == webrtc::kVideoRotation_270) {
116 0 : std::swap(rotated_width, rotated_height);
117 : }
118 :
119 : rtc::scoped_refptr<webrtc::I420Buffer> buffer =
120 0 : I420Buffer::Create(rotated_width, rotated_height);
121 :
122 0 : RTC_CHECK_EQ(0, libyuv::I420Rotate(
123 : src.DataY(), src.StrideY(),
124 : src.DataU(), src.StrideU(),
125 : src.DataV(), src.StrideV(),
126 : buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
127 : buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
128 : src.width(), src.height(),
129 0 : static_cast<libyuv::RotationMode>(rotation)));
130 :
131 0 : return buffer;
132 : }
133 :
134 : // static
135 0 : rtc::scoped_refptr<VideoFrameBuffer> I420Buffer::Rotate(
136 : rtc::scoped_refptr<VideoFrameBuffer> src,
137 : VideoRotation rotation) {
138 0 : if (rotation == webrtc::kVideoRotation_0) {
139 0 : return src;
140 : } else {
141 0 : return Rotate(*src, rotation);
142 : }
143 : }
144 :
145 0 : void I420Buffer::InitializeData() {
146 0 : memset(data_.get(), 0,
147 0 : I420DataSize(height_, stride_y_, stride_u_, stride_v_));
148 0 : }
149 :
150 0 : int I420Buffer::width() const {
151 0 : return width_;
152 : }
153 :
154 0 : int I420Buffer::height() const {
155 0 : return height_;
156 : }
157 :
158 0 : const uint8_t* I420Buffer::DataY() const {
159 0 : return data_.get();
160 : }
161 0 : const uint8_t* I420Buffer::DataU() const {
162 0 : return data_.get() + stride_y_ * height_;
163 : }
164 0 : const uint8_t* I420Buffer::DataV() const {
165 0 : return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
166 : }
167 :
168 0 : int I420Buffer::StrideY() const {
169 0 : return stride_y_;
170 : }
171 0 : int I420Buffer::StrideU() const {
172 0 : return stride_u_;
173 : }
174 0 : int I420Buffer::StrideV() const {
175 0 : return stride_v_;
176 : }
177 :
178 0 : void* I420Buffer::native_handle() const {
179 0 : return nullptr;
180 : }
181 :
182 0 : rtc::scoped_refptr<VideoFrameBuffer> I420Buffer::NativeToI420Buffer() {
183 0 : RTC_NOTREACHED();
184 0 : return nullptr;
185 : }
186 :
187 0 : uint8_t* I420Buffer::MutableDataY() {
188 0 : return const_cast<uint8_t*>(DataY());
189 : }
190 0 : uint8_t* I420Buffer::MutableDataU() {
191 0 : return const_cast<uint8_t*>(DataU());
192 : }
193 0 : uint8_t* I420Buffer::MutableDataV() {
194 0 : return const_cast<uint8_t*>(DataV());
195 : }
196 :
197 : // static
198 0 : void I420Buffer::SetBlack(I420Buffer* buffer) {
199 0 : RTC_CHECK(libyuv::I420Rect(buffer->MutableDataY(), buffer->StrideY(),
200 : buffer->MutableDataU(), buffer->StrideU(),
201 : buffer->MutableDataV(), buffer->StrideV(),
202 : 0, 0, buffer->width(), buffer->height(),
203 0 : 0, 128, 128) == 0);
204 0 : }
205 :
206 0 : void I420Buffer::CropAndScaleFrom(
207 : const VideoFrameBuffer& src,
208 : int offset_x,
209 : int offset_y,
210 : int crop_width,
211 : int crop_height) {
212 0 : RTC_CHECK_LE(crop_width, src.width());
213 0 : RTC_CHECK_LE(crop_height, src.height());
214 0 : RTC_CHECK_LE(crop_width + offset_x, src.width());
215 0 : RTC_CHECK_LE(crop_height + offset_y, src.height());
216 0 : RTC_CHECK_GE(offset_x, 0);
217 0 : RTC_CHECK_GE(offset_y, 0);
218 :
219 : // Make sure offset is even so that u/v plane becomes aligned.
220 0 : const int uv_offset_x = offset_x / 2;
221 0 : const int uv_offset_y = offset_y / 2;
222 0 : offset_x = uv_offset_x * 2;
223 0 : offset_y = uv_offset_y * 2;
224 :
225 : const uint8_t* y_plane =
226 0 : src.DataY() + src.StrideY() * offset_y + offset_x;
227 : const uint8_t* u_plane =
228 0 : src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x;
229 : const uint8_t* v_plane =
230 0 : src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x;
231 0 : int res = libyuv::I420Scale(y_plane, src.StrideY(),
232 0 : u_plane, src.StrideU(),
233 0 : v_plane, src.StrideV(),
234 : crop_width, crop_height,
235 0 : MutableDataY(), StrideY(),
236 0 : MutableDataU(), StrideU(),
237 0 : MutableDataV(), StrideV(),
238 0 : width(), height(), libyuv::kFilterBox);
239 :
240 0 : RTC_DCHECK_EQ(res, 0);
241 0 : }
242 :
243 0 : void I420Buffer::CropAndScaleFrom(
244 : const VideoFrameBuffer& src) {
245 : const int crop_width =
246 0 : std::min(src.width(), width() * src.height() / height());
247 : const int crop_height =
248 0 : std::min(src.height(), height() * src.width() / width());
249 :
250 0 : CropAndScaleFrom(
251 : src,
252 0 : (src.width() - crop_width) / 2, (src.height() - crop_height) / 2,
253 0 : crop_width, crop_height);
254 0 : }
255 :
256 0 : void I420Buffer::ScaleFrom(const VideoFrameBuffer& src) {
257 0 : CropAndScaleFrom(src, 0, 0, src.width(), src.height());
258 0 : }
259 :
260 : } // namespace webrtc
|