blob: 62d14b9f4ac87851e7830a4078ced02febe20fd6 [file] [log] [blame]
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +00001/*
2 * Copyright (c) 2012 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
11#include <math.h>
12#include <string.h>
13
Per9b3f56e2015-04-09 13:44:16 +020014#include "webrtc/base/bind.h"
Peter Boströmeb66e802015-06-05 11:08:03 +020015#include "webrtc/test/fake_texture_frame.h"
Niels Möller739fcb92016-02-29 13:11:45 +010016#include "webrtc/test/frame_utils.h"
kwibergac9f8762016-09-30 22:29:43 -070017#include "webrtc/test/gtest.h"
jbauch0f2e9392015-12-10 03:11:42 -080018#include "webrtc/video_frame.h"
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +000019
20namespace webrtc {
21
Niels Möller718a7632016-06-13 13:06:01 +020022namespace {
23
Niels Möller718a7632016-06-13 13:06:01 +020024rtc::scoped_refptr<I420Buffer> CreateGradient(int width, int height) {
25 rtc::scoped_refptr<I420Buffer> buffer(
nisseac62bd42016-06-20 03:38:52 -070026 I420Buffer::Create(width, height));
Niels Möller718a7632016-06-13 13:06:01 +020027 // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h
28 for (int x = 0; x < width; x++) {
29 for (int y = 0; y < height; y++) {
30 buffer->MutableDataY()[x + y * width] =
31 128 * (x * height + y * width) / (width * height);
32 }
33 }
34 int chroma_width = (width + 1) / 2;
35 int chroma_height = (height + 1) / 2;
36 for (int x = 0; x < chroma_width; x++) {
37 for (int y = 0; y < chroma_height; y++) {
38 buffer->MutableDataU()[x + y * chroma_width] =
39 255 * x / (chroma_width - 1);
40 buffer->MutableDataV()[x + y * chroma_width] =
41 255 * y / (chroma_height - 1);
42 }
43 }
44 return buffer;
45}
46
47// The offsets and sizes describe the rectangle extracted from the
48// original (gradient) frame, in relative coordinates where the
49// original frame correspond to the unit square, 0.0 <= x, y < 1.0.
nissef33970b2016-10-18 06:01:34 -070050void CheckCrop(const webrtc::VideoFrameBuffer& frame,
Niels Möller718a7632016-06-13 13:06:01 +020051 double offset_x,
52 double offset_y,
53 double rel_width,
54 double rel_height) {
nissef33970b2016-10-18 06:01:34 -070055 int width = frame.width();
56 int height = frame.height();
Niels Möller718a7632016-06-13 13:06:01 +020057 // Check that pixel values in the corners match the gradient used
58 // for initialization.
59 for (int i = 0; i < 2; i++) {
60 for (int j = 0; j < 2; j++) {
61 // Pixel coordinates of the corner.
62 int x = i * (width - 1);
63 int y = j * (height - 1);
64 // Relative coordinates, range 0.0 - 1.0 correspond to the
65 // size of the uncropped input frame.
66 double orig_x = offset_x + i * rel_width;
67 double orig_y = offset_y + j * rel_height;
68
nissef33970b2016-10-18 06:01:34 -070069 EXPECT_NEAR(frame.DataY()[x + y * frame.StrideY()] / 256.0,
Niels Möller718a7632016-06-13 13:06:01 +020070 (orig_x + orig_y) / 2, 0.02);
nissef33970b2016-10-18 06:01:34 -070071 EXPECT_NEAR(frame.DataU()[x / 2 + (y / 2) * frame.StrideU()] / 256.0,
Niels Möller718a7632016-06-13 13:06:01 +020072 orig_x, 0.02);
nissef33970b2016-10-18 06:01:34 -070073 EXPECT_NEAR(frame.DataV()[x / 2 + (y / 2) * frame.StrideV()] / 256.0,
Niels Möller718a7632016-06-13 13:06:01 +020074 orig_y, 0.02);
75 }
76 }
77}
78
nissef33970b2016-10-18 06:01:34 -070079void CheckRotate(int width, int height, webrtc::VideoRotation rotation,
80 const webrtc::VideoFrameBuffer& rotated) {
81 int rotated_width = width;
82 int rotated_height = height;
83
84 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) {
85 std::swap(rotated_width, rotated_height);
86 }
87 EXPECT_EQ(rotated_width, rotated.width());
88 EXPECT_EQ(rotated_height, rotated.height());
89
90 // Clock-wise order (with 0,0 at top-left)
91 const struct { int x; int y; } corners[] = {
92 { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 }
93 };
94 // Corresponding corner colors of the frame produced by CreateGradient.
95 const struct { int y; int u; int v; } colors[] = {
96 {0, 0, 0}, { 127, 255, 0}, { 255, 255, 255 }, {127, 0, 255}
97 };
98 int corner_offset = static_cast<int>(rotation) / 90;
99
100 for (int i = 0; i < 4; i++) {
101 int j = (i + corner_offset) % 4;
102 int x = corners[j].x * (rotated_width - 1);
103 int y = corners[j].y * (rotated_height - 1);
104 EXPECT_EQ(colors[i].y, rotated.DataY()[x + y * rotated.StrideY()]);
105 EXPECT_EQ(colors[i].u,
106 rotated.DataU()[(x / 2) + (y / 2) * rotated.StrideU()]);
107 EXPECT_EQ(colors[i].v,
108 rotated.DataV()[(x / 2) + (y / 2) * rotated.StrideV()]);
109 }
110}
111
Niels Möller718a7632016-06-13 13:06:01 +0200112} // namespace
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000113
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700114TEST(TestVideoFrame, InitialValues) {
115 VideoFrame frame;
mikhal@webrtc.orgdfc6b572012-10-15 16:12:09 +0000116 EXPECT_TRUE(frame.IsZeroSize());
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000117 EXPECT_EQ(kVideoRotation_0, frame.rotation());
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000118}
119
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700120TEST(TestVideoFrame, WidthHeightValues) {
nissef122a852016-10-04 23:27:30 -0700121 VideoFrame frame(I420Buffer::Create(10, 10, 10, 14, 90),
122 webrtc::kVideoRotation_0,
123 789 * rtc::kNumMicrosecsPerMillisec);
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000124 const int valid_value = 10;
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000125 EXPECT_EQ(valid_value, frame.width());
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000126 EXPECT_EQ(valid_value, frame.height());
wu@webrtc.org6c75c982014-04-15 17:46:33 +0000127 frame.set_timestamp(123u);
128 EXPECT_EQ(123u, frame.timestamp());
129 frame.set_ntp_time_ms(456);
130 EXPECT_EQ(456, frame.ntp_time_ms());
wu@webrtc.org6c75c982014-04-15 17:46:33 +0000131 EXPECT_EQ(789, frame.render_time_ms());
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000132}
133
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700134TEST(TestVideoFrame, ShallowCopy) {
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000135 uint32_t timestamp = 1;
136 int64_t ntp_time_ms = 2;
137 int64_t render_time_ms = 3;
138 int stride_y = 15;
139 int stride_u = 10;
140 int stride_v = 10;
141 int width = 15;
142 int height = 15;
143
144 const int kSizeY = 400;
145 const int kSizeU = 100;
146 const int kSizeV = 100;
147 const VideoRotation kRotation = kVideoRotation_270;
148 uint8_t buffer_y[kSizeY];
149 uint8_t buffer_u[kSizeU];
150 uint8_t buffer_v[kSizeV];
151 memset(buffer_y, 16, kSizeY);
152 memset(buffer_u, 8, kSizeU);
153 memset(buffer_v, 4, kSizeV);
nisse2f58ec82016-11-28 03:47:52 -0800154
nissef0a7c5a2016-10-31 05:48:07 -0700155 VideoFrame frame1(
nisse2f58ec82016-11-28 03:47:52 -0800156 I420Buffer::Copy(width, height,
157 buffer_y, stride_y,
158 buffer_u, stride_u,
159 buffer_v, stride_v),
nissef0a7c5a2016-10-31 05:48:07 -0700160 kRotation, 0);
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000161 frame1.set_timestamp(timestamp);
162 frame1.set_ntp_time_ms(ntp_time_ms);
163 frame1.set_render_time_ms(render_time_ms);
nissef0a7c5a2016-10-31 05:48:07 -0700164 VideoFrame frame2(frame1);
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000165
nissef0a7c5a2016-10-31 05:48:07 -0700166 EXPECT_EQ(frame1.video_frame_buffer(), frame2.video_frame_buffer());
167 EXPECT_EQ(frame1.video_frame_buffer()->DataY(),
168 frame2.video_frame_buffer()->DataY());
169 EXPECT_EQ(frame1.video_frame_buffer()->DataU(),
170 frame2.video_frame_buffer()->DataU());
171 EXPECT_EQ(frame1.video_frame_buffer()->DataV(),
172 frame2.video_frame_buffer()->DataV());
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000173
174 EXPECT_EQ(frame2.timestamp(), frame1.timestamp());
175 EXPECT_EQ(frame2.ntp_time_ms(), frame1.ntp_time_ms());
176 EXPECT_EQ(frame2.render_time_ms(), frame1.render_time_ms());
177 EXPECT_EQ(frame2.rotation(), frame1.rotation());
178
179 frame2.set_timestamp(timestamp + 1);
180 frame2.set_ntp_time_ms(ntp_time_ms + 1);
181 frame2.set_render_time_ms(render_time_ms + 1);
182 frame2.set_rotation(kVideoRotation_90);
183
184 EXPECT_NE(frame2.timestamp(), frame1.timestamp());
185 EXPECT_NE(frame2.ntp_time_ms(), frame1.ntp_time_ms());
186 EXPECT_NE(frame2.render_time_ms(), frame1.render_time_ms());
187 EXPECT_NE(frame2.rotation(), frame1.rotation());
188}
189
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700190TEST(TestVideoFrame, TextureInitialValues) {
Peter Boströmeb66e802015-06-05 11:08:03 +0200191 test::FakeNativeHandle* handle = new test::FakeNativeHandle();
Peter Boström13f61df2016-01-04 22:36:38 +0100192 VideoFrame frame = test::FakeNativeHandle::CreateFrame(
Peter Boströmeb66e802015-06-05 11:08:03 +0200193 handle, 640, 480, 100, 10, webrtc::kVideoRotation_0);
magjed@webrtc.org45cdcce2015-03-06 10:41:00 +0000194 EXPECT_EQ(640, frame.width());
195 EXPECT_EQ(480, frame.height());
196 EXPECT_EQ(100u, frame.timestamp());
197 EXPECT_EQ(10, frame.render_time_ms());
nisse26acec42016-04-15 03:43:39 -0700198 ASSERT_TRUE(frame.video_frame_buffer() != nullptr);
199 EXPECT_EQ(handle, frame.video_frame_buffer()->native_handle());
magjed@webrtc.org45cdcce2015-03-06 10:41:00 +0000200
201 frame.set_timestamp(200);
202 EXPECT_EQ(200u, frame.timestamp());
203 frame.set_render_time_ms(20);
204 EXPECT_EQ(20, frame.render_time_ms());
205}
206
nisse7cc9cc02016-03-29 23:44:19 -0700207TEST(TestI420FrameBuffer, Copy) {
208 rtc::scoped_refptr<I420Buffer> buf1(
nisseac62bd42016-06-20 03:38:52 -0700209 I420Buffer::Create(20, 10));
nisse06176e42016-04-18 05:34:40 -0700210 memset(buf1->MutableDataY(), 1, 200);
211 memset(buf1->MutableDataU(), 2, 50);
212 memset(buf1->MutableDataV(), 3, 50);
nissee3fe4a72016-11-10 08:44:38 -0800213 rtc::scoped_refptr<I420Buffer> buf2 = I420Buffer::Copy(*buf1);
nisse7cc9cc02016-03-29 23:44:19 -0700214 EXPECT_TRUE(test::FrameBufsEqual(buf1, buf2));
215}
216
Niels Möller718a7632016-06-13 13:06:01 +0200217TEST(TestI420FrameBuffer, Scale) {
218 rtc::scoped_refptr<I420Buffer> buf = CreateGradient(200, 100);
219
220 // Pure scaling, no cropping.
221 rtc::scoped_refptr<I420Buffer> scaled_buffer(
nisseac62bd42016-06-20 03:38:52 -0700222 I420Buffer::Create(150, 75));
Niels Möller718a7632016-06-13 13:06:01 +0200223
nissee3fe4a72016-11-10 08:44:38 -0800224 scaled_buffer->ScaleFrom(*buf);
nissef33970b2016-10-18 06:01:34 -0700225 CheckCrop(*scaled_buffer, 0.0, 0.0, 1.0, 1.0);
Niels Möller718a7632016-06-13 13:06:01 +0200226}
227
228TEST(TestI420FrameBuffer, CropXCenter) {
229 rtc::scoped_refptr<I420Buffer> buf = CreateGradient(200, 100);
230
231 // Pure center cropping, no scaling.
232 rtc::scoped_refptr<I420Buffer> scaled_buffer(
nisseac62bd42016-06-20 03:38:52 -0700233 I420Buffer::Create(100, 100));
Niels Möller718a7632016-06-13 13:06:01 +0200234
nissee3fe4a72016-11-10 08:44:38 -0800235 scaled_buffer->CropAndScaleFrom(*buf, 50, 0, 100, 100);
nissef33970b2016-10-18 06:01:34 -0700236 CheckCrop(*scaled_buffer, 0.25, 0.0, 0.5, 1.0);
Niels Möller718a7632016-06-13 13:06:01 +0200237}
238
239TEST(TestI420FrameBuffer, CropXNotCenter) {
240 rtc::scoped_refptr<I420Buffer> buf = CreateGradient(200, 100);
241
242 // Non-center cropping, no scaling.
243 rtc::scoped_refptr<I420Buffer> scaled_buffer(
nisseac62bd42016-06-20 03:38:52 -0700244 I420Buffer::Create(100, 100));
Niels Möller718a7632016-06-13 13:06:01 +0200245
nissee3fe4a72016-11-10 08:44:38 -0800246 scaled_buffer->CropAndScaleFrom(*buf, 25, 0, 100, 100);
nissef33970b2016-10-18 06:01:34 -0700247 CheckCrop(*scaled_buffer, 0.125, 0.0, 0.5, 1.0);
Niels Möller718a7632016-06-13 13:06:01 +0200248}
249
250TEST(TestI420FrameBuffer, CropYCenter) {
251 rtc::scoped_refptr<I420Buffer> buf = CreateGradient(100, 200);
252
253 // Pure center cropping, no scaling.
254 rtc::scoped_refptr<I420Buffer> scaled_buffer(
nisseac62bd42016-06-20 03:38:52 -0700255 I420Buffer::Create(100, 100));
Niels Möller718a7632016-06-13 13:06:01 +0200256
nissee3fe4a72016-11-10 08:44:38 -0800257 scaled_buffer->CropAndScaleFrom(*buf, 0, 50, 100, 100);
nissef33970b2016-10-18 06:01:34 -0700258 CheckCrop(*scaled_buffer, 0.0, 0.25, 1.0, 0.5);
Niels Möller718a7632016-06-13 13:06:01 +0200259}
260
261TEST(TestI420FrameBuffer, CropYNotCenter) {
262 rtc::scoped_refptr<I420Buffer> buf = CreateGradient(100, 200);
263
264 // Non-center cropping, no scaling.
265 rtc::scoped_refptr<I420Buffer> scaled_buffer(
nisseac62bd42016-06-20 03:38:52 -0700266 I420Buffer::Create(100, 100));
Niels Möller718a7632016-06-13 13:06:01 +0200267
nissee3fe4a72016-11-10 08:44:38 -0800268 scaled_buffer->CropAndScaleFrom(*buf, 0, 25, 100, 100);
nissef33970b2016-10-18 06:01:34 -0700269 CheckCrop(*scaled_buffer, 0.0, 0.125, 1.0, 0.5);
Niels Möller718a7632016-06-13 13:06:01 +0200270}
271
272TEST(TestI420FrameBuffer, CropAndScale16x9) {
273 rtc::scoped_refptr<I420Buffer> buf = CreateGradient(640, 480);
274
275 // Center crop to 640 x 360 (16/9 aspect), then scale down by 2.
276 rtc::scoped_refptr<I420Buffer> scaled_buffer(
nisseac62bd42016-06-20 03:38:52 -0700277 I420Buffer::Create(320, 180));
Niels Möller718a7632016-06-13 13:06:01 +0200278
nissee3fe4a72016-11-10 08:44:38 -0800279 scaled_buffer->CropAndScaleFrom(*buf);
nissef33970b2016-10-18 06:01:34 -0700280 CheckCrop(*scaled_buffer, 0.0, 0.125, 1.0, 0.75);
Niels Möller718a7632016-06-13 13:06:01 +0200281}
282
nissef33970b2016-10-18 06:01:34 -0700283class TestI420BufferRotate
284 : public ::testing::TestWithParam<webrtc::VideoRotation> {};
285
286TEST_P(TestI420BufferRotate, Rotates) {
287 rtc::scoped_refptr<VideoFrameBuffer> buffer = CreateGradient(640, 480);
288 rtc::scoped_refptr<VideoFrameBuffer> rotated_buffer =
289 I420Buffer::Rotate(buffer, GetParam());
290 CheckRotate(640, 480, GetParam(), *rotated_buffer);
291}
292
293INSTANTIATE_TEST_CASE_P(Rotate, TestI420BufferRotate,
294 ::testing::Values(kVideoRotation_0,
295 kVideoRotation_90,
296 kVideoRotation_180,
297 kVideoRotation_270));
298
mikhal@webrtc.org043ed9e2012-09-18 16:14:26 +0000299} // namespace webrtc