blob: 2d7e7f45653f98585026adc3044b1478d3ee1f87 [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 */
10
Niels Möller718a7632016-06-13 13:06:01 +020011#include <algorithm>
12
kjellander6f8ce062015-11-16 13:52:24 -080013#include "webrtc/common_video/include/video_frame_buffer.h"
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000014
15#include "webrtc/base/checks.h"
perkj14f41442015-11-30 22:15:45 -080016#include "webrtc/base/keep_ref_until_done.h"
nisse7cc9cc02016-03-29 23:44:19 -070017#include "libyuv/convert.h"
fbarchardd1f83cf2016-07-08 17:33:20 -070018#include "libyuv/planar_functions.h"
Niels Möller718a7632016-06-13 13:06:01 +020019#include "libyuv/scale.h"
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000020
21// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
22static const int kBufferAlignment = 64;
23
24namespace webrtc {
25
hbos900f9752016-02-05 08:08:34 -080026namespace {
27
28int I420DataSize(int height, int stride_y, int stride_u, int stride_v) {
29 return stride_y * height + (stride_u + stride_v) * ((height + 1) / 2);
30}
31
32} // namespace
33
nisse06176e42016-04-18 05:34:40 -070034uint8_t* VideoFrameBuffer::MutableDataY() {
Magnus Jedvert3318f982015-08-26 16:06:21 +020035 RTC_NOTREACHED();
36 return nullptr;
37}
nisse06176e42016-04-18 05:34:40 -070038uint8_t* VideoFrameBuffer::MutableDataU() {
39 RTC_NOTREACHED();
40 return nullptr;
41}
42uint8_t* VideoFrameBuffer::MutableDataV() {
43 RTC_NOTREACHED();
44 return nullptr;
45}
46
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000047VideoFrameBuffer::~VideoFrameBuffer() {}
48
49I420Buffer::I420Buffer(int width, int height)
50 : I420Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {
51}
52
53I420Buffer::I420Buffer(int width,
54 int height,
55 int stride_y,
56 int stride_u,
57 int stride_v)
58 : width_(width),
59 height_(height),
60 stride_y_(stride_y),
61 stride_u_(stride_u),
62 stride_v_(stride_v),
63 data_(static_cast<uint8_t*>(AlignedMalloc(
hbos900f9752016-02-05 08:08:34 -080064 I420DataSize(height, stride_y, stride_u, stride_v),
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000065 kBufferAlignment))) {
henrikg91d6ede2015-09-17 00:24:34 -070066 RTC_DCHECK_GT(width, 0);
67 RTC_DCHECK_GT(height, 0);
68 RTC_DCHECK_GE(stride_y, width);
69 RTC_DCHECK_GE(stride_u, (width + 1) / 2);
70 RTC_DCHECK_GE(stride_v, (width + 1) / 2);
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000071}
72
73I420Buffer::~I420Buffer() {
74}
75
nisseac62bd42016-06-20 03:38:52 -070076rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width, int height) {
77 return new rtc::RefCountedObject<I420Buffer>(width, height);
78}
79
80rtc::scoped_refptr<I420Buffer> I420Buffer::Create(int width,
81 int height,
82 int stride_y,
83 int stride_u,
84 int stride_v) {
85 return new rtc::RefCountedObject<I420Buffer>(
86 width, height, stride_y, stride_u, stride_v);
87}
88
hbos900f9752016-02-05 08:08:34 -080089void I420Buffer::InitializeData() {
90 memset(data_.get(), 0,
91 I420DataSize(height_, stride_y_, stride_u_, stride_v_));
92}
93
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +000094int I420Buffer::width() const {
95 return width_;
96}
97
98int I420Buffer::height() const {
99 return height_;
100}
101
nisse06176e42016-04-18 05:34:40 -0700102const uint8_t* I420Buffer::DataY() const {
103 return data_.get();
104}
105const uint8_t* I420Buffer::DataU() const {
106 return data_.get() + stride_y_ * height_;
107}
108const uint8_t* I420Buffer::DataV() const {
109 return data_.get() + stride_y_ * height_ + stride_u_ * ((height_ + 1) / 2);
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000110}
111
nisse06176e42016-04-18 05:34:40 -0700112uint8_t* I420Buffer::MutableDataY() {
nisse06176e42016-04-18 05:34:40 -0700113 return const_cast<uint8_t*>(DataY());
114}
115uint8_t* I420Buffer::MutableDataU() {
nisse06176e42016-04-18 05:34:40 -0700116 return const_cast<uint8_t*>(DataU());
117}
118uint8_t* I420Buffer::MutableDataV() {
nisse06176e42016-04-18 05:34:40 -0700119 return const_cast<uint8_t*>(DataV());
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000120}
121
nisse06176e42016-04-18 05:34:40 -0700122int I420Buffer::StrideY() const {
123 return stride_y_;
124}
125int I420Buffer::StrideU() const {
126 return stride_u_;
127}
128int I420Buffer::StrideV() const {
129 return stride_v_;
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000130}
131
Per9b3f56e2015-04-09 13:44:16 +0200132void* I420Buffer::native_handle() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000133 return nullptr;
134}
135
Peter Boströmeb66e802015-06-05 11:08:03 +0200136rtc::scoped_refptr<VideoFrameBuffer> I420Buffer::NativeToI420Buffer() {
137 RTC_NOTREACHED();
138 return nullptr;
139}
140
nisse7cc9cc02016-03-29 23:44:19 -0700141rtc::scoped_refptr<I420Buffer> I420Buffer::Copy(
Niels Möller6af2e862016-06-17 09:12:44 +0200142 const rtc::scoped_refptr<VideoFrameBuffer>& source) {
143 int width = source->width();
144 int height = source->height();
nisseac62bd42016-06-20 03:38:52 -0700145 rtc::scoped_refptr<I420Buffer> target = I420Buffer::Create(width, height);
Niels Möller6af2e862016-06-17 09:12:44 +0200146 RTC_CHECK(libyuv::I420Copy(source->DataY(), source->StrideY(),
147 source->DataU(), source->StrideU(),
148 source->DataV(), source->StrideV(),
149 target->MutableDataY(), target->StrideY(),
150 target->MutableDataU(), target->StrideU(),
151 target->MutableDataV(), target->StrideV(),
nisse7cc9cc02016-03-29 23:44:19 -0700152 width, height) == 0);
153
Niels Möller6af2e862016-06-17 09:12:44 +0200154 return target;
nisse7cc9cc02016-03-29 23:44:19 -0700155}
156
nisseefec5902016-06-09 00:31:39 -0700157void I420Buffer::SetToBlack() {
158 RTC_CHECK(libyuv::I420Rect(MutableDataY(), StrideY(),
159 MutableDataU(), StrideU(),
160 MutableDataV(), StrideV(),
161 0, 0, width(), height(),
162 0, 128, 128) == 0);
163}
164
Niels Möller718a7632016-06-13 13:06:01 +0200165void I420Buffer::CropAndScaleFrom(
166 const rtc::scoped_refptr<VideoFrameBuffer>& src,
167 int offset_x,
168 int offset_y,
169 int crop_width,
170 int crop_height) {
171 RTC_CHECK_LE(crop_width, src->width());
172 RTC_CHECK_LE(crop_height, src->height());
173 RTC_CHECK_LE(crop_width + offset_x, src->width());
174 RTC_CHECK_LE(crop_height + offset_y, src->height());
175 RTC_CHECK_GE(offset_x, 0);
176 RTC_CHECK_GE(offset_y, 0);
177
178 // Make sure offset is even so that u/v plane becomes aligned.
179 const int uv_offset_x = offset_x / 2;
180 const int uv_offset_y = offset_y / 2;
181 offset_x = uv_offset_x * 2;
182 offset_y = uv_offset_y * 2;
183
184 const uint8_t* y_plane =
185 src->DataY() + src->StrideY() * offset_y + offset_x;
186 const uint8_t* u_plane =
187 src->DataU() + src->StrideU() * uv_offset_y + uv_offset_x;
188 const uint8_t* v_plane =
189 src->DataV() + src->StrideV() * uv_offset_y + uv_offset_x;
190 int res = libyuv::I420Scale(y_plane, src->StrideY(),
191 u_plane, src->StrideU(),
192 v_plane, src->StrideV(),
193 crop_width, crop_height,
194 MutableDataY(), StrideY(),
195 MutableDataU(), StrideU(),
196 MutableDataV(), StrideV(),
197 width(), height(), libyuv::kFilterBox);
198
199 RTC_DCHECK_EQ(res, 0);
200}
201
202void I420Buffer::CropAndScaleFrom(
203 const rtc::scoped_refptr<VideoFrameBuffer>& src) {
204 const int crop_width =
205 std::min(src->width(), width() * src->height() / height());
206 const int crop_height =
207 std::min(src->height(), height() * src->width() / width());
208
209 CropAndScaleFrom(
210 src,
211 (src->width() - crop_width) / 2, (src->height() - crop_height) / 2,
212 crop_width, crop_height);
213}
214
215void I420Buffer::ScaleFrom(const rtc::scoped_refptr<VideoFrameBuffer>& src) {
216 CropAndScaleFrom(src, 0, 0, src->width(), src->height());
217}
218
nissed5074722016-08-30 08:45:44 -0700219// static
Niels Möller6af2e862016-06-17 09:12:44 +0200220rtc::scoped_refptr<I420Buffer> I420Buffer::CopyKeepStride(
221 const rtc::scoped_refptr<VideoFrameBuffer>& source) {
222 int width = source->width();
223 int height = source->height();
224 int stride_y = source->StrideY();
225 int stride_u = source->StrideU();
226 int stride_v = source->StrideV();
227 rtc::scoped_refptr<I420Buffer> target =
nisseac62bd42016-06-20 03:38:52 -0700228 I420Buffer::Create(width, height, stride_y, stride_u, stride_v);
Niels Möller6af2e862016-06-17 09:12:44 +0200229 RTC_CHECK(libyuv::I420Copy(source->DataY(), stride_y,
230 source->DataU(), stride_u,
231 source->DataV(), stride_v,
232 target->MutableDataY(), stride_y,
233 target->MutableDataU(), stride_u,
234 target->MutableDataV(), stride_v,
235 width, height) == 0);
236
237 return target;
238}
239
nissed5074722016-08-30 08:45:44 -0700240// static
241rtc::scoped_refptr<VideoFrameBuffer> I420Buffer::Rotate(
242 const rtc::scoped_refptr<VideoFrameBuffer>& src,
243 VideoRotation rotation) {
244 RTC_DCHECK(src->DataY());
245 RTC_DCHECK(src->DataU());
246 RTC_DCHECK(src->DataV());
247
248 if (rotation == webrtc::kVideoRotation_0) {
249 return src;
250 }
251
252 int rotated_width = src->width();
253 int rotated_height = src->height();
254 if (rotation == webrtc::kVideoRotation_90 ||
255 rotation == webrtc::kVideoRotation_270) {
256 std::swap(rotated_width, rotated_height);
257 }
258
259 rtc::scoped_refptr<webrtc::I420Buffer> buffer =
260 I420Buffer::Create(rotated_width, rotated_height);
261
262 int res = libyuv::I420Rotate(
263 src->DataY(), src->StrideY(),
264 src->DataU(), src->StrideU(),
265 src->DataV(), src->StrideV(),
266 buffer->MutableDataY(), buffer->StrideY(), buffer->MutableDataU(),
267 buffer->StrideU(), buffer->MutableDataV(), buffer->StrideV(),
268 src->width(), src->height(),
269 static_cast<libyuv::RotationMode>(rotation));
270 RTC_DCHECK_EQ(res, 0);
271
272 return buffer;
273}
274
Peter Boströmeb66e802015-06-05 11:08:03 +0200275NativeHandleBuffer::NativeHandleBuffer(void* native_handle,
276 int width,
277 int height)
278 : native_handle_(native_handle), width_(width), height_(height) {
henrikg91d6ede2015-09-17 00:24:34 -0700279 RTC_DCHECK(native_handle != nullptr);
280 RTC_DCHECK_GT(width, 0);
281 RTC_DCHECK_GT(height, 0);
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000282}
283
Peter Boströmeb66e802015-06-05 11:08:03 +0200284int NativeHandleBuffer::width() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000285 return width_;
286}
287
Peter Boströmeb66e802015-06-05 11:08:03 +0200288int NativeHandleBuffer::height() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000289 return height_;
290}
291
nisse06176e42016-04-18 05:34:40 -0700292const uint8_t* NativeHandleBuffer::DataY() const {
293 RTC_NOTREACHED(); // Should not be called.
294 return nullptr;
295}
296const uint8_t* NativeHandleBuffer::DataU() const {
297 RTC_NOTREACHED(); // Should not be called.
298 return nullptr;
299}
300const uint8_t* NativeHandleBuffer::DataV() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000301 RTC_NOTREACHED(); // Should not be called.
302 return nullptr;
303}
304
nisse06176e42016-04-18 05:34:40 -0700305int NativeHandleBuffer::StrideY() const {
306 RTC_NOTREACHED(); // Should not be called.
307 return 0;
308}
309int NativeHandleBuffer::StrideU() const {
310 RTC_NOTREACHED(); // Should not be called.
311 return 0;
312}
313int NativeHandleBuffer::StrideV() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000314 RTC_NOTREACHED(); // Should not be called.
315 return 0;
316}
317
Peter Boströmeb66e802015-06-05 11:08:03 +0200318void* NativeHandleBuffer::native_handle() const {
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000319 return native_handle_;
320}
321
Magnus Jedvertc464f502015-08-25 23:22:08 +0200322WrappedI420Buffer::WrappedI420Buffer(int width,
Per33544192015-04-02 12:30:51 +0200323 int height,
324 const uint8_t* y_plane,
325 int y_stride,
326 const uint8_t* u_plane,
327 int u_stride,
328 const uint8_t* v_plane,
329 int v_stride,
330 const rtc::Callback0<void>& no_longer_used)
Magnus Jedvertc464f502015-08-25 23:22:08 +0200331 : width_(width),
332 height_(height),
333 y_plane_(y_plane),
334 u_plane_(u_plane),
335 v_plane_(v_plane),
336 y_stride_(y_stride),
337 u_stride_(u_stride),
338 v_stride_(v_stride),
339 no_longer_used_cb_(no_longer_used) {
Per33544192015-04-02 12:30:51 +0200340}
341
342WrappedI420Buffer::~WrappedI420Buffer() {
343 no_longer_used_cb_();
344}
345
Per33544192015-04-02 12:30:51 +0200346int WrappedI420Buffer::width() const {
347 return width_;
348}
349
350int WrappedI420Buffer::height() const {
351 return height_;
352}
353
nisse06176e42016-04-18 05:34:40 -0700354const uint8_t* WrappedI420Buffer::DataY() const {
355 return y_plane_;
356}
357const uint8_t* WrappedI420Buffer::DataU() const {
358 return u_plane_;
359}
360const uint8_t* WrappedI420Buffer::DataV() const {
361 return v_plane_;
Per33544192015-04-02 12:30:51 +0200362}
363
nisse06176e42016-04-18 05:34:40 -0700364int WrappedI420Buffer::StrideY() const {
365 return y_stride_;
366}
367int WrappedI420Buffer::StrideU() const {
368 return u_stride_;
369}
370int WrappedI420Buffer::StrideV() const {
371 return v_stride_;
Per33544192015-04-02 12:30:51 +0200372}
373
Per9b3f56e2015-04-09 13:44:16 +0200374void* WrappedI420Buffer::native_handle() const {
Per33544192015-04-02 12:30:51 +0200375 return nullptr;
376}
377
Peter Boströmeb66e802015-06-05 11:08:03 +0200378rtc::scoped_refptr<VideoFrameBuffer> WrappedI420Buffer::NativeToI420Buffer() {
379 RTC_NOTREACHED();
380 return nullptr;
381}
382
magjed@webrtc.org2386d6d2015-03-05 14:03:08 +0000383} // namespace webrtc