blob: 1d209370a6373b7fa0f58159bbc5801fe6c58b66 [file] [log] [blame]
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +00001/*
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 */
perkj0489e492016-10-20 00:24:01 -070010#include "webrtc/common_video/include/video_frame_buffer.h"
11
12#include <string.h>
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000013
Niels Möller718a7632016-06-13 13:06:01 +020014#include <algorithm>
15
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000016#include "webrtc/base/checks.h"
perkj14f41442015-11-30 22:15:45 -080017#include "webrtc/base/keep_ref_until_done.h"
nisse7cc9cc02016-03-29 23:44:19 -070018#include "libyuv/convert.h"
fbarchardd1f83cf2016-07-08 17:33:20 -070019#include "libyuv/planar_functions.h"
Niels Möller718a7632016-06-13 13:06:01 +020020#include "libyuv/scale.h"
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000021
22// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
23static const int kBufferAlignment = 64;
24
25namespace webrtc {
26
hbos900f9752016-02-05 08:08:34 -080027namespace {
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
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000035VideoFrameBuffer::~VideoFrameBuffer() {}
36
37I420Buffer::I420Buffer(int width, int height)
38 : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {
39}
40
41I420Buffer::I420Buffer(int width,
42 int height,
43 int stride_y,
44 int stride_u,
45 int stride_v)
46 : width_(width),
47 height_(height),
48 stride_y_(stride_y),
49 stride_u_(stride_u),
50 stride_v_(stride_v),
51 data_(static_cast<uint8_t*>(AlignedMalloc(
hbos900f9752016-02-05 08:08:34 -080052 I420DataSize(height, stride_y, stride_u, stride_v),
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000053 kBufferAlignment))) {
henrikg91d6ede2015-09-17 00:24:34 -070054 RTC_DCHECK_GT(width, 0);
55 RTC_DCHECK_GT(height, 0);
56 RTC_DCHECK_GE(stride_y, width);
57 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
58 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000059}
60
61I420Buffer::~I420Buffer() {
62}
63
nisseac62bd42016-06-20 03:38:52 -070064rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
65 return new rtc::RefCountedObject<I420Buffer>(width, height);
66}
67
68rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width,
69 int height,
70 int stride_y,
71 int stride_u,
72 int stride_v) {
73 return new rtc::RefCountedObject<I420Buffer>(
74 width, height, stride_y, stride_u, stride_v);
75}
76
hbos900f9752016-02-05 08:08:34 -080077void I420Buffer::InitializeData() {
78 memset(data_.get(), 0,
79 I420DataSize(height_, stride_y_, stride_u_, stride_v_));
80}
81
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000082int I420Buffer::width() const {
83 return width_;
84}
85
86int I420Buffer::height() const {
87 return height_;
88}
89
nisse06176e42016-04-18 05:34:40 -070090const uint8_t* I420Buffer::DataY() const {
91 return data_.get();
92}
93const uint8_t* I420Buffer::DataU() const {
94 return data_.get() + stride_y_ * height_;
95}
96const uint8_t* I420Buffer::DataV() const {
97 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000098}
99
nisse06176e42016-04-18 05:34:40 -0700100uint8_t* I420Buffer::MutableDataY() {
nisse06176e42016-04-18 05:34:40 -0700101 return const_cast<uint8_t*>(DataY());
102}
103uint8_t* I420Buffer::MutableDataU() {
nisse06176e42016-04-18 05:34:40 -0700104 return const_cast<uint8_t*>(DataU());
105}
106uint8_t* I420Buffer::MutableDataV() {
nisse06176e42016-04-18 05:34:40 -0700107 return const_cast<uint8_t*>(DataV());
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000108}
109
nisse06176e42016-04-18 05:34:40 -0700110int I420Buffer::StrideY() const {
111 return stride_y_;
112}
113int I420Buffer::StrideU() const {
114 return stride_u_;
115}
116int I420Buffer::StrideV() const {
117 return stride_v_;
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000118}
119
Per9b3f56e2015-04-09 13:44:16 +0200120void* I420Buffer::native_handle() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000121 return nullptr;
122}
123
Peter Boströmeb66e802015-06-05 11:08:03 +0200124rtc::scoped_refptr<VideoFrameBuffer> I420Buffer::NativeToI420Buffer() {
125 RTC_NOTREACHED();
126 return nullptr;
127}
128
nisse7cc9cc02016-03-29 23:44:19 -0700129rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
Niels Möller6af2e862016-06-17 09:12:44 +0200130 const rtc::scoped_refptr<VideoFrameBuffer>& source) {
131 int width = source->width();
132 int height = source->height();
nisseac62bd42016-06-20 03:38:52 -0700133 rtc::scoped_refptr<I420Buffer> target = I420Buffer::Create(width, height);
Niels Möller6af2e862016-06-17 09:12:44 +0200134 RTC_CHECK(libyuv::I420Copy(source->DataY(), source->StrideY(),
135 source->DataU(), source->StrideU(),
136 source->DataV(), source->StrideV(),
137 target->MutableDataY(), target->StrideY(),
138 target->MutableDataU(), target->StrideU(),
139 target->MutableDataV(), target->StrideV(),
nisse7cc9cc02016-03-29 23:44:19 -0700140 width, height) == 0);
141
Niels Möller6af2e862016-06-17 09:12:44 +0200142 return target;
nisse7cc9cc02016-03-29 23:44:19 -0700143}
144
nisseefec5902016-06-09 00:31:39 -0700145void I420Buffer::SetToBlack() {
146 RTC_CHECK(libyuv::I420Rect(MutableDataY(), StrideY(),
147 MutableDataU(), StrideU(),
148 MutableDataV(), StrideV(),
149 0, 0, width(), height(),
150 0, 128, 128) == 0);
151}
152
Niels Möller718a7632016-06-13 13:06:01 +0200153void I420Buffer::CropAndScaleFrom(
154 const rtc::scoped_refptr<VideoFrameBuffer>& src,
155 int offset_x,
156 int offset_y,
157 int crop_width,
158 int crop_height) {
159 RTC_CHECK_LE(crop_width, src->width());
160 RTC_CHECK_LE(crop_height, src->height());
161 RTC_CHECK_LE(crop_width + offset_x, src->width());
162 RTC_CHECK_LE(crop_height + offset_y, src->height());
163 RTC_CHECK_GE(offset_x, 0);
164 RTC_CHECK_GE(offset_y, 0);
165
166 // Make sure offset is even so that u/v plane becomes aligned.
167 const int uv_offset_x = offset_x / 2;
168 const int uv_offset_y = offset_y / 2;
169 offset_x = uv_offset_x * 2;
170 offset_y = uv_offset_y * 2;
171
172 const uint8_t* y_plane =
173 src->DataY() + src->StrideY() * offset_y + offset_x;
174 const uint8_t* u_plane =
175 src->DataU() + src->StrideU() * uv_offset_y + uv_offset_x;
176 const uint8_t* v_plane =
177 src->DataV() + src->StrideV() * uv_offset_y + uv_offset_x;
178 int res = libyuv::I420Scale(y_plane, src->StrideY(),
179 u_plane, src->StrideU(),
180 v_plane, src->StrideV(),
181 crop_width, crop_height,
182 MutableDataY(), StrideY(),
183 MutableDataU(), StrideU(),
184 MutableDataV(), StrideV(),
185 width(), height(), libyuv::kFilterBox);
186
187 RTC_DCHECK_EQ(res, 0);
188}
189
190void I420Buffer::CropAndScaleFrom(
191 const rtc::scoped_refptr<VideoFrameBuffer>& src) {
192 const int crop_width =
193 std::min(src->width(), width() * src->height() / height());
194 const int crop_height =
195 std::min(src->height(), height() * src->width() / width());
196
197 CropAndScaleFrom(
198 src,
199 (src->width() - crop_width) / 2, (src->height() - crop_height) / 2,
200 crop_width, crop_height);
201}
202
203void I420Buffer::ScaleFrom(const rtc::scoped_refptr<VideoFrameBuffer>& src) {
204 CropAndScaleFrom(src, 0, 0, src->width(), src->height());
205}
206
nissed5074722016-08-30 08:45:44 -0700207// static
Niels Möller6af2e862016-06-17 09:12:44 +0200208rtc::scoped_refptr<I420Buffer> I420Buffer::CopyKeepStride(
209 const rtc::scoped_refptr<VideoFrameBuffer>& source) {
210 int width = source->width();
211 int height = source->height();
212 int stride_y = source->StrideY();
213 int stride_u = source->StrideU();
214 int stride_v = source->StrideV();
215 rtc::scoped_refptr<I420Buffer> target =
nisseac62bd42016-06-20 03:38:52 -0700216 I420Buffer::Create(width, height, stride_y, stride_u, stride_v);
Niels Möller6af2e862016-06-17 09:12:44 +0200217 RTC_CHECK(libyuv::I420Copy(source->DataY(), stride_y,
218 source->DataU(), stride_u,
219 source->DataV(), stride_v,
220 target->MutableDataY(), stride_y,
221 target->MutableDataU(), stride_u,
222 target->MutableDataV(), stride_v,
223 width, height) == 0);
224
225 return target;
226}
227
nissed5074722016-08-30 08:45:44 -0700228// static
229rtc::scoped_refptr<VideoFrameBuffer> I420Buffer::Rotate(
230 const rtc::scoped_refptr<VideoFrameBuffer>& src,
231 VideoRotation rotation) {
232 RTC_DCHECK(src->DataY());
233 RTC_DCHECK(src->DataU());
234 RTC_DCHECK(src->DataV());
235
236 if (rotation == webrtc::kVideoRotation_0) {
237 return src;
238 }
239
240 int rotated_width = src->width();
241 int rotated_height = src->height();
242 if (rotation == webrtc::kVideoRotation_90 ||
243 rotation == webrtc::kVideoRotation_270) {
244 std::swap(rotated_width, rotated_height);
245 }
246
247 rtc::scoped_refptr<webrtc::I420Buffer> buffer =
248 I420Buffer::Create(rotated_width, rotated_height);
249
250 int res = libyuv::I420Rotate(
251 src->DataY(), src->StrideY(),
252 src->DataU(), src->StrideU(),
253 src->DataV(), src->StrideV(),
254 buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
255 buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
256 src->width(), src->height(),
257 static_cast<libyuv::RotationMode>(rotation));
258 RTC_DCHECK_EQ(res, 0);
259
260 return buffer;
261}
262
Peter Boströmeb66e802015-06-05 11:08:03 +0200263NativeHandleBuffer::NativeHandleBuffer(void* native_handle,
264 int width,
265 int height)
266 : native_handle_(native_handle), width_(width), height_(height) {
henrikg91d6ede2015-09-17 00:24:34 -0700267 RTC_DCHECK(native_handle != nullptr);
268 RTC_DCHECK_GT(width, 0);
269 RTC_DCHECK_GT(height, 0);
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000270}
271
Peter Boströmeb66e802015-06-05 11:08:03 +0200272int NativeHandleBuffer::width() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000273 return width_;
274}
275
Peter Boströmeb66e802015-06-05 11:08:03 +0200276int NativeHandleBuffer::height() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000277 return height_;
278}
279
nisse06176e42016-04-18 05:34:40 -0700280const uint8_t* NativeHandleBuffer::DataY() const {
281 RTC_NOTREACHED(); // Should not be called.
282 return nullptr;
283}
284const uint8_t* NativeHandleBuffer::DataU() const {
285 RTC_NOTREACHED(); // Should not be called.
286 return nullptr;
287}
288const uint8_t* NativeHandleBuffer::DataV() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000289 RTC_NOTREACHED(); // Should not be called.
290 return nullptr;
291}
292
nisse06176e42016-04-18 05:34:40 -0700293int NativeHandleBuffer::StrideY() const {
294 RTC_NOTREACHED(); // Should not be called.
295 return 0;
296}
297int NativeHandleBuffer::StrideU() const {
298 RTC_NOTREACHED(); // Should not be called.
299 return 0;
300}
301int NativeHandleBuffer::StrideV() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000302 RTC_NOTREACHED(); // Should not be called.
303 return 0;
304}
305
Peter Boströmeb66e802015-06-05 11:08:03 +0200306void* NativeHandleBuffer::native_handle() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000307 return native_handle_;
308}
309
Magnus Jedvertc464f502015-08-25 23:22:08 +0200310WrappedI420Buffer::WrappedI420Buffer(int width,
Per33544192015-04-02 12:30:51 +0200311 int height,
312 const uint8_t* y_plane,
313 int y_stride,
314 const uint8_t* u_plane,
315 int u_stride,
316 const uint8_t* v_plane,
317 int v_stride,
318 const rtc::Callback0<void>& no_longer_used)
Magnus Jedvertc464f502015-08-25 23:22:08 +0200319 : width_(width),
320 height_(height),
321 y_plane_(y_plane),
322 u_plane_(u_plane),
323 v_plane_(v_plane),
324 y_stride_(y_stride),
325 u_stride_(u_stride),
326 v_stride_(v_stride),
327 no_longer_used_cb_(no_longer_used) {
Per33544192015-04-02 12:30:51 +0200328}
329
330WrappedI420Buffer::~WrappedI420Buffer() {
331 no_longer_used_cb_();
332}
333
Per33544192015-04-02 12:30:51 +0200334int WrappedI420Buffer::width() const {
335 return width_;
336}
337
338int WrappedI420Buffer::height() const {
339 return height_;
340}
341
nisse06176e42016-04-18 05:34:40 -0700342const uint8_t* WrappedI420Buffer::DataY() const {
343 return y_plane_;
344}
345const uint8_t* WrappedI420Buffer::DataU() const {
346 return u_plane_;
347}
348const uint8_t* WrappedI420Buffer::DataV() const {
349 return v_plane_;
Per33544192015-04-02 12:30:51 +0200350}
351
nisse06176e42016-04-18 05:34:40 -0700352int WrappedI420Buffer::StrideY() const {
353 return y_stride_;
354}
355int WrappedI420Buffer::StrideU() const {
356 return u_stride_;
357}
358int WrappedI420Buffer::StrideV() const {
359 return v_stride_;
Per33544192015-04-02 12:30:51 +0200360}
361
Per9b3f56e2015-04-09 13:44:16 +0200362void* WrappedI420Buffer::native_handle() const {
Per33544192015-04-02 12:30:51 +0200363 return nullptr;
364}
365
Peter Boströmeb66e802015-06-05 11:08:03 +0200366rtc::scoped_refptr<VideoFrameBuffer> WrappedI420Buffer::NativeToI420Buffer() {
367 RTC_NOTREACHED();
368 return nullptr;
369}
370
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000371} // namespace webrtc