blob: 87cd03714c22d5836e418956e80c43f702fb87fd [file] [log] [blame]
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +00001/*
stefan@webrtc.org0fe21712012-02-22 11:21:18 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +00003 *
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
kwibergc891eb42016-03-02 03:41:34 -080014#include <memory>
15
pbos@webrtc.orgc69ae692013-06-04 09:02:37 +000016#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
nisse115bd152016-09-30 04:14:07 -070017#include "webrtc/test/frame_utils.h"
magjed5a872452016-10-20 03:34:29 -070018#include "webrtc/test/gmock.h"
kwibergac9f8762016-09-30 22:29:43 -070019#include "webrtc/test/gtest.h"
pbos@webrtc.orgc69ae692013-06-04 09:02:37 +000020#include "webrtc/test/testsupport/fileutils.h"
Thiago Farina9bfe3da2015-04-10 12:52:13 +020021#include "webrtc/video_frame.h"
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000022
23namespace webrtc {
24
nisse69adc9c2016-06-02 00:58:35 -070025namespace {
26void Calc16ByteAlignedStride(int width, int* stride_y, int* stride_uv) {
27 *stride_y = 16 * ((width + 15) / 16);
28 *stride_uv = 16 * ((width + 31) / 32);
29}
30
31} // Anonymous namespace
32
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000033class TestLibYuv : public ::testing::Test {
34 protected:
35 TestLibYuv();
36 virtual void SetUp();
37 virtual void TearDown();
38
39 FILE* source_file_;
nisse115bd152016-09-30 04:14:07 -070040 std::unique_ptr<VideoFrame> orig_frame_;
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000041 const int width_;
42 const int height_;
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +000043 const int size_y_;
44 const int size_uv_;
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000045 const size_t frame_length_;
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000046};
47
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000048TestLibYuv::TestLibYuv()
49 : source_file_(NULL),
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +000050 orig_frame_(),
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000051 width_(352),
52 height_(288),
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +000053 size_y_(width_ * height_),
jbauch0f2e9392015-12-10 03:11:42 -080054 size_uv_(((width_ + 1) / 2) * ((height_ + 1) / 2)),
nisse115bd152016-09-30 04:14:07 -070055 frame_length_(CalcBufferSize(kI420, 352, 288)) {}
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000056
57void TestLibYuv::SetUp() {
kjellander02060002016-02-16 22:06:12 -080058 const std::string input_file_name = webrtc::test::ResourcePath("foreman_cif",
59 "yuv");
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000060 source_file_ = fopen(input_file_name.c_str(), "rb");
61 ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<<
62 input_file_name << "\n";
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +000063
nisse115bd152016-09-30 04:14:07 -070064 rtc::scoped_refptr<VideoFrameBuffer> buffer(
65 test::ReadI420Buffer(width_, height_, source_file_));
66
67 orig_frame_.reset(new VideoFrame(buffer, kVideoRotation_0, 0));
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000068}
69
70void TestLibYuv::TearDown() {
71 if (source_file_ != NULL) {
72 ASSERT_EQ(0, fclose(source_file_));
73 }
74 source_file_ = NULL;
75}
76
77TEST_F(TestLibYuv, ConvertSanityTest) {
78 // TODO(mikhal)
79}
80
81TEST_F(TestLibYuv, ConvertTest) {
82 // Reading YUV frame - testing on the first frame of the foreman sequence
83 int j = 0;
84 std::string output_file_name = webrtc::test::OutputPath() +
85 "LibYuvTest_conversion.yuv";
86 FILE* output_file = fopen(output_file_name.c_str(), "wb");
87 ASSERT_TRUE(output_file != NULL);
88
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +000089 double psnr = 0.0;
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +000090
nisse64ec8f82016-09-27 00:17:25 -070091 rtc::scoped_refptr<I420Buffer> res_i420_buffer = I420Buffer::Create(
92 width_, height_, width_, (width_ + 1) / 2, (width_ + 1) / 2);
93
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +000094 printf("\nConvert #%d I420 <-> I420 \n", j);
kwibergc891eb42016-03-02 03:41:34 -080095 std::unique_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]);
nisse115bd152016-09-30 04:14:07 -070096 EXPECT_EQ(0, ConvertFromI420(*orig_frame_, kI420, 0, out_i420_buffer.get()));
guoweis@webrtc.org59140d62015-03-09 17:07:31 +000097 EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -070098 height_, 0, kVideoRotation_0,
99 res_i420_buffer.get()));
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000100
nisse64ec8f82016-09-27 00:17:25 -0700101 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000102 return;
103 }
nisse115bd152016-09-30 04:14:07 -0700104 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000105 EXPECT_EQ(48.0, psnr);
106 j++;
107
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000108 printf("\nConvert #%d I420 <-> RGB24\n", j);
kwibergc891eb42016-03-02 03:41:34 -0800109 std::unique_ptr<uint8_t[]> res_rgb_buffer2(new uint8_t[width_ * height_ * 3]);
mikhal@webrtc.org91a03402012-10-30 19:19:32 +0000110 // Align the stride values for the output frame.
111 int stride_y = 0;
112 int stride_uv = 0;
113 Calc16ByteAlignedStride(width_, &stride_y, &stride_uv);
nisse64ec8f82016-09-27 00:17:25 -0700114 res_i420_buffer =
115 I420Buffer::Create(width_, height_, stride_y, stride_uv, stride_uv);
nisse115bd152016-09-30 04:14:07 -0700116 EXPECT_EQ(0, ConvertFromI420(*orig_frame_, kRGB24, 0, res_rgb_buffer2.get()));
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000117
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000118 EXPECT_EQ(0, ConvertToI420(kRGB24, res_rgb_buffer2.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -0700119 height_, 0, kVideoRotation_0,
120 res_i420_buffer.get()));
mikhal@webrtc.orge2642492011-12-28 21:21:40 +0000121
nisse64ec8f82016-09-27 00:17:25 -0700122 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
leozwang@webrtc.org2fc6e382012-05-29 17:33:13 +0000123 return;
124 }
nisse115bd152016-09-30 04:14:07 -0700125 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000126
mikhal@webrtc.orge2642492011-12-28 21:21:40 +0000127 // Optimization Speed- quality trade-off => 45 dB only (platform dependant).
stefan@webrtc.org0fe21712012-02-22 11:21:18 +0000128 EXPECT_GT(ceil(psnr), 44);
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000129 j++;
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000130
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000131 printf("\nConvert #%d I420 <-> UYVY\n", j);
kwibergc891eb42016-03-02 03:41:34 -0800132 std::unique_ptr<uint8_t[]> out_uyvy_buffer(new uint8_t[width_ * height_ * 2]);
nisse115bd152016-09-30 04:14:07 -0700133 EXPECT_EQ(0, ConvertFromI420(*orig_frame_, kUYVY, 0, out_uyvy_buffer.get()));
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000134 EXPECT_EQ(0, ConvertToI420(kUYVY, out_uyvy_buffer.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -0700135 height_, 0, kVideoRotation_0,
136 res_i420_buffer.get()));
nisse115bd152016-09-30 04:14:07 -0700137 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000138 EXPECT_EQ(48.0, psnr);
nisse64ec8f82016-09-27 00:17:25 -0700139 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000140 return;
141 }
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000142 j++;
143
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000144 printf("\nConvert #%d I420 <-> YUY2\n", j);
kwibergc891eb42016-03-02 03:41:34 -0800145 std::unique_ptr<uint8_t[]> out_yuy2_buffer(new uint8_t[width_ * height_ * 2]);
nisse115bd152016-09-30 04:14:07 -0700146 EXPECT_EQ(0, ConvertFromI420(*orig_frame_, kYUY2, 0, out_yuy2_buffer.get()));
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000147
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000148 EXPECT_EQ(0, ConvertToI420(kYUY2, out_yuy2_buffer.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -0700149 height_, 0,
150 kVideoRotation_0, res_i420_buffer.get()));
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000151
nisse64ec8f82016-09-27 00:17:25 -0700152 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000153 return;
leozwang@webrtc.org2fc6e382012-05-29 17:33:13 +0000154 }
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000155
nisse115bd152016-09-30 04:14:07 -0700156 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000157 EXPECT_EQ(48.0, psnr);
nisse64ec8f82016-09-27 00:17:25 -0700158
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000159 printf("\nConvert #%d I420 <-> RGB565\n", j);
kwibergc891eb42016-03-02 03:41:34 -0800160 std::unique_ptr<uint8_t[]> out_rgb565_buffer(
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000161 new uint8_t[width_ * height_ * 2]);
nisse64ec8f82016-09-27 00:17:25 -0700162 EXPECT_EQ(0,
nisse115bd152016-09-30 04:14:07 -0700163 ConvertFromI420(*orig_frame_, kRGB565, 0, out_rgb565_buffer.get()));
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000164
165 EXPECT_EQ(0, ConvertToI420(kRGB565, out_rgb565_buffer.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -0700166 height_, 0,
167 kVideoRotation_0, res_i420_buffer.get()));
168 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000169 return;
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000170 }
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000171 j++;
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000172
nisse115bd152016-09-30 04:14:07 -0700173 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
leozwang@webrtc.org1ea25b42012-05-04 17:55:57 +0000174 // TODO(leozwang) Investigate the right psnr should be set for I420ToRGB565,
175 // Another example is I420ToRGB24, the psnr is 44
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000176 // TODO(mikhal): Add psnr for RGB565, 1555, 4444, convert to ARGB.
leozwang@webrtc.org1ea25b42012-05-04 17:55:57 +0000177 EXPECT_GT(ceil(psnr), 40);
leozwang@webrtc.org83958df2012-05-04 17:07:45 +0000178
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000179 printf("\nConvert #%d I420 <-> ARGB8888\n", j);
kwibergc891eb42016-03-02 03:41:34 -0800180 std::unique_ptr<uint8_t[]> out_argb8888_buffer(
kwiberg@webrtc.org00b8f6b2015-02-26 14:34:55 +0000181 new uint8_t[width_ * height_ * 4]);
nisse64ec8f82016-09-27 00:17:25 -0700182 EXPECT_EQ(0,
nisse115bd152016-09-30 04:14:07 -0700183 ConvertFromI420(*orig_frame_, kARGB, 0, out_argb8888_buffer.get()));
leozwang@webrtc.org83958df2012-05-04 17:07:45 +0000184
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000185 EXPECT_EQ(0, ConvertToI420(kARGB, out_argb8888_buffer.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -0700186 height_, 0, kVideoRotation_0,
187 res_i420_buffer.get()));
leozwang@webrtc.org83958df2012-05-04 17:07:45 +0000188
nisse64ec8f82016-09-27 00:17:25 -0700189 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
leozwang@webrtc.org2fc6e382012-05-29 17:33:13 +0000190 return;
191 }
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000192
nisse115bd152016-09-30 04:14:07 -0700193 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
nisse64ec8f82016-09-27 00:17:25 -0700194 // TODO(leozwang) Investigate the right psnr should be set for
195 // I420ToARGB8888,
leozwang@webrtc.org329fcbb2012-05-04 21:31:22 +0000196 EXPECT_GT(ceil(psnr), 42);
leozwang@webrtc.org83958df2012-05-04 17:07:45 +0000197
kjellander@webrtc.org94558d82012-01-04 13:51:50 +0000198 ASSERT_EQ(0, fclose(output_file));
mikhal@webrtc.org6f7fbc72011-12-20 17:38:28 +0000199}
200
mikhal@webrtc.org32b3f402012-11-20 23:52:50 +0000201TEST_F(TestLibYuv, ConvertAlignedFrame) {
202 // Reading YUV frame - testing on the first frame of the foreman sequence
203 std::string output_file_name = webrtc::test::OutputPath() +
204 "LibYuvTest_conversion.yuv";
205 FILE* output_file = fopen(output_file_name.c_str(), "wb");
206 ASSERT_TRUE(output_file != NULL);
207
208 double psnr = 0.0;
209
mikhal@webrtc.org32b3f402012-11-20 23:52:50 +0000210 int stride_y = 0;
211 int stride_uv = 0;
212 Calc16ByteAlignedStride(width_, &stride_y, &stride_uv);
nisse64ec8f82016-09-27 00:17:25 -0700213
214 rtc::scoped_refptr<I420Buffer> res_i420_buffer =
215 I420Buffer::Create(width_, height_, stride_y, stride_uv, stride_uv);
kwibergc891eb42016-03-02 03:41:34 -0800216 std::unique_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]);
nisse115bd152016-09-30 04:14:07 -0700217 EXPECT_EQ(0, ConvertFromI420(*orig_frame_, kI420, 0,
mikhal@webrtc.org32b3f402012-11-20 23:52:50 +0000218 out_i420_buffer.get()));
guoweis@webrtc.org59140d62015-03-09 17:07:31 +0000219 EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, width_,
nisse64ec8f82016-09-27 00:17:25 -0700220 height_, 0, kVideoRotation_0,
221 res_i420_buffer.get()));
mikhal@webrtc.org32b3f402012-11-20 23:52:50 +0000222
nisse64ec8f82016-09-27 00:17:25 -0700223 if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
mikhal@webrtc.org32b3f402012-11-20 23:52:50 +0000224 return;
225 }
nisse115bd152016-09-30 04:14:07 -0700226 psnr = I420PSNR(*orig_frame_->video_frame_buffer(), *res_i420_buffer);
mikhal@webrtc.org32b3f402012-11-20 23:52:50 +0000227 EXPECT_EQ(48.0, psnr);
228}
229
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000230TEST_F(TestLibYuv, RotateTest) {
nisse64ec8f82016-09-27 00:17:25 -0700231 // Use ConvertToI420 for multiple rotations - see that nothing breaks, all
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000232 // memory is properly allocated and end result is equal to the starting point.
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000233 int rotated_width = height_;
234 int rotated_height = width_;
jbauch0f2e9392015-12-10 03:11:42 -0800235 int stride_y;
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000236 int stride_uv;
nisse115bd152016-09-30 04:14:07 -0700237
238 // Assume compact layout, no padding.
239 const uint8_t *orig_buffer = orig_frame_->video_frame_buffer()->DataY();
240
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000241 Calc16ByteAlignedStride(rotated_width, &stride_y, &stride_uv);
nisse64ec8f82016-09-27 00:17:25 -0700242 rtc::scoped_refptr<I420Buffer> rotated_res_i420_buffer = I420Buffer::Create(
243 rotated_width, rotated_height, stride_y, stride_uv, stride_uv);
nisse115bd152016-09-30 04:14:07 -0700244 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer, 0, 0, width_, height_,
nisse64ec8f82016-09-27 00:17:25 -0700245 0, kVideoRotation_90,
246 rotated_res_i420_buffer.get()));
nisse115bd152016-09-30 04:14:07 -0700247 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer, 0, 0, width_, height_,
nisse64ec8f82016-09-27 00:17:25 -0700248 0, kVideoRotation_270,
249 rotated_res_i420_buffer.get()));
250 rotated_res_i420_buffer = I420Buffer::Create(
251 width_, height_, width_, (width_ + 1) / 2, (width_ + 1) / 2);
nisse115bd152016-09-30 04:14:07 -0700252 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer, 0, 0, width_, height_,
nisse64ec8f82016-09-27 00:17:25 -0700253 0, kVideoRotation_180,
254 rotated_res_i420_buffer.get()));
mikhal@webrtc.org737ed3b2012-11-01 15:45:38 +0000255}
256
magjed5a872452016-10-20 03:34:29 -0700257static uint8_t Average(int a, int b, int c, int d) {
258 return (a + b + c + d + 2) / 4;
259}
260
261TEST_F(TestLibYuv, NV12Scale2x2to2x2) {
262 const std::vector<uint8_t> src_y = {0, 1,
263 2, 3};
264 const std::vector<uint8_t> src_uv = {0, 1};
265 std::vector<uint8_t> dst_y(4);
266 std::vector<uint8_t> dst_uv(2);
267
268 std::vector<uint8_t> tmp_buffer;
269 NV12Scale(&tmp_buffer,
270 src_y.data(), 2,
271 src_uv.data(), 2,
272 2, 2,
273 dst_y.data(), 2,
274 dst_uv.data(), 2,
275 2, 2);
276
277 EXPECT_THAT(dst_y, ::testing::ContainerEq(src_y));
278 EXPECT_THAT(dst_uv, ::testing::ContainerEq(src_uv));
279}
280
281TEST_F(TestLibYuv, NV12Scale4x4to2x2) {
282 const uint8_t src_y[] = { 0, 1, 2, 3,
283 4, 5, 6, 7,
284 8, 9, 10, 11,
285 12, 13, 14, 15};
286 const uint8_t src_uv[] = {0, 1, 2, 3,
287 4, 5, 6, 7};
288 std::vector<uint8_t> dst_y(4);
289 std::vector<uint8_t> dst_uv(2);
290
291 std::vector<uint8_t> tmp_buffer;
292 NV12Scale(&tmp_buffer,
293 src_y, 4,
294 src_uv, 4,
295 4, 4,
296 dst_y.data(), 2,
297 dst_uv.data(), 2,
298 2, 2);
299
300 EXPECT_THAT(dst_y, ::testing::ElementsAre(
301 Average(0, 1, 4, 5), Average(2, 3, 6, 7),
302 Average(8, 9, 12, 13), Average(10, 11, 14, 15)));
303 EXPECT_THAT(dst_uv,
304 ::testing::ElementsAre(Average(0, 2, 4, 6), Average(1, 3, 5, 7)));
305}
306
jbauch0f2e9392015-12-10 03:11:42 -0800307} // namespace webrtc