1 /*
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
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "webrtc/base/scoped_ptr.h"
16 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
17 #include "webrtc/system_wrappers/include/tick_util.h"
18 #include "webrtc/test/testsupport/fileutils.h"
19 #include "webrtc/video_frame.h"
20
21 namespace webrtc {
22
PrintBuffer(const uint8_t * buffer,int width,int height,int stride)23 int PrintBuffer(const uint8_t* buffer, int width, int height, int stride) {
24 if (buffer == NULL)
25 return -1;
26 int k;
27 const uint8_t* tmp_buffer = buffer;
28 for (int i = 0; i < height; i++) {
29 k = 0;
30 for (int j = 0; j < width; j++) {
31 printf("%d ", tmp_buffer[k++]);
32 }
33 tmp_buffer += stride;
34 printf(" \n");
35 }
36 printf(" \n");
37 return 0;
38 }
39
PrintFrame(const VideoFrame * frame,const char * str)40 int PrintFrame(const VideoFrame* frame, const char* str) {
41 if (frame == NULL)
42 return -1;
43 printf("%s %dx%d \n", str, frame->width(), frame->height());
44
45 int ret = 0;
46 for (int plane_num = 0; plane_num < kNumOfPlanes; ++plane_num) {
47 PlaneType plane_type = static_cast<PlaneType>(plane_num);
48 int width = (plane_num ? (frame->width() + 1) / 2 : frame->width());
49 int height = (plane_num ? (frame->height() + 1) / 2 : frame->height());
50 ret += PrintBuffer(frame->buffer(plane_type), width, height,
51 frame->stride(plane_type));
52 }
53 return ret;
54 }
55
56
57 // Create an image from on a YUV frame. Every plane value starts with a start
58 // value, and will be set to increasing values.
CreateImage(VideoFrame * frame,int plane_offset[kNumOfPlanes])59 void CreateImage(VideoFrame* frame, int plane_offset[kNumOfPlanes]) {
60 if (frame == NULL)
61 return;
62 for (int plane_num = 0; plane_num < kNumOfPlanes; ++plane_num) {
63 int width = (plane_num != kYPlane ? (frame->width() + 1) / 2 :
64 frame->width());
65 int height = (plane_num != kYPlane ? (frame->height() + 1) / 2 :
66 frame->height());
67 PlaneType plane_type = static_cast<PlaneType>(plane_num);
68 uint8_t *data = frame->buffer(plane_type);
69 for (int i = 0; i < height; i++) {
70 for (int j = 0; j < width; j++) {
71 data[j] = static_cast<uint8_t>(i + plane_offset[plane_num] + j);
72 }
73 data += frame->stride(plane_type);
74 }
75 }
76 }
77
78 class TestLibYuv : public ::testing::Test {
79 protected:
80 TestLibYuv();
81 virtual void SetUp();
82 virtual void TearDown();
83
84 FILE* source_file_;
85 VideoFrame orig_frame_;
86 rtc::scoped_ptr<uint8_t[]> orig_buffer_;
87 const int width_;
88 const int height_;
89 const int size_y_;
90 const int size_uv_;
91 const size_t frame_length_;
92 };
93
TestLibYuv()94 TestLibYuv::TestLibYuv()
95 : source_file_(NULL),
96 orig_frame_(),
97 width_(352),
98 height_(288),
99 size_y_(width_ * height_),
100 size_uv_(((width_ + 1) / 2) * ((height_ + 1) / 2)),
101 frame_length_(CalcBufferSize(kI420, 352, 288)) {
102 orig_buffer_.reset(new uint8_t[frame_length_]);
103 }
104
SetUp()105 void TestLibYuv::SetUp() {
106 const std::string input_file_name = webrtc::test::ProjectRootPath() +
107 "resources/foreman_cif.yuv";
108 source_file_ = fopen(input_file_name.c_str(), "rb");
109 ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<<
110 input_file_name << "\n";
111
112 EXPECT_EQ(frame_length_,
113 fread(orig_buffer_.get(), 1, frame_length_, source_file_));
114 EXPECT_EQ(0, orig_frame_.CreateFrame(orig_buffer_.get(),
115 orig_buffer_.get() + size_y_,
116 orig_buffer_.get() +
117 size_y_ + size_uv_,
118 width_, height_,
119 width_, (width_ + 1) / 2,
120 (width_ + 1) / 2));
121 }
122
TearDown()123 void TestLibYuv::TearDown() {
124 if (source_file_ != NULL) {
125 ASSERT_EQ(0, fclose(source_file_));
126 }
127 source_file_ = NULL;
128 }
129
TEST_F(TestLibYuv,ConvertSanityTest)130 TEST_F(TestLibYuv, ConvertSanityTest) {
131 // TODO(mikhal)
132 }
133
TEST_F(TestLibYuv,ConvertTest)134 TEST_F(TestLibYuv, ConvertTest) {
135 // Reading YUV frame - testing on the first frame of the foreman sequence
136 int j = 0;
137 std::string output_file_name = webrtc::test::OutputPath() +
138 "LibYuvTest_conversion.yuv";
139 FILE* output_file = fopen(output_file_name.c_str(), "wb");
140 ASSERT_TRUE(output_file != NULL);
141
142 double psnr = 0.0;
143
144 VideoFrame res_i420_frame;
145 EXPECT_EQ(0, res_i420_frame.CreateEmptyFrame(width_, height_, width_,
146 (width_ + 1) / 2,
147 (width_ + 1) / 2));
148 printf("\nConvert #%d I420 <-> I420 \n", j);
149 rtc::scoped_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]);
150 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kI420, 0,
151 out_i420_buffer.get()));
152 EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, width_,
153 height_, 0, kVideoRotation_0, &res_i420_frame));
154
155 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
156 return;
157 }
158 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
159 EXPECT_EQ(48.0, psnr);
160 j++;
161
162 printf("\nConvert #%d I420 <-> RGB24\n", j);
163 rtc::scoped_ptr<uint8_t[]> res_rgb_buffer2(new uint8_t[width_ * height_ * 3]);
164 // Align the stride values for the output frame.
165 int stride_y = 0;
166 int stride_uv = 0;
167 Calc16ByteAlignedStride(width_, &stride_y, &stride_uv);
168 res_i420_frame.CreateEmptyFrame(width_, height_, stride_y,
169 stride_uv, stride_uv);
170 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kRGB24, 0, res_rgb_buffer2.get()));
171
172 EXPECT_EQ(0, ConvertToI420(kRGB24, res_rgb_buffer2.get(), 0, 0, width_,
173 height_, 0, kVideoRotation_0, &res_i420_frame));
174
175 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
176 return;
177 }
178 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
179
180 // Optimization Speed- quality trade-off => 45 dB only (platform dependant).
181 EXPECT_GT(ceil(psnr), 44);
182 j++;
183
184 printf("\nConvert #%d I420 <-> UYVY\n", j);
185 rtc::scoped_ptr<uint8_t[]> out_uyvy_buffer(new uint8_t[width_ * height_ * 2]);
186 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kUYVY, 0, out_uyvy_buffer.get()));
187 EXPECT_EQ(0, ConvertToI420(kUYVY, out_uyvy_buffer.get(), 0, 0, width_,
188 height_, 0, kVideoRotation_0, &res_i420_frame));
189 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
190 EXPECT_EQ(48.0, psnr);
191 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
192 return;
193 }
194 j++;
195
196 printf("\nConvert #%d I420 <-> YV12\n", j);
197 rtc::scoped_ptr<uint8_t[]> outYV120Buffer(new uint8_t[frame_length_]);
198 rtc::scoped_ptr<uint8_t[]> res_i420_buffer(new uint8_t[frame_length_]);
199 VideoFrame yv12_frame;
200 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kYV12, 0, outYV120Buffer.get()));
201 yv12_frame.CreateFrame(outYV120Buffer.get(),
202 outYV120Buffer.get() + size_y_,
203 outYV120Buffer.get() + size_y_ + size_uv_,
204 width_, height_,
205 width_, (width_ + 1) / 2, (width_ + 1) / 2);
206 EXPECT_EQ(0, ConvertFromYV12(yv12_frame, kI420, 0, res_i420_buffer.get()));
207 if (fwrite(res_i420_buffer.get(), 1, frame_length_, output_file) !=
208 frame_length_) {
209 return;
210 }
211
212 ConvertToI420(kI420, res_i420_buffer.get(), 0, 0, width_, height_, 0,
213 kVideoRotation_0, &res_i420_frame);
214 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
215 EXPECT_EQ(48.0, psnr);
216 j++;
217
218 printf("\nConvert #%d I420 <-> YUY2\n", j);
219 rtc::scoped_ptr<uint8_t[]> out_yuy2_buffer(new uint8_t[width_ * height_ * 2]);
220 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kYUY2, 0, out_yuy2_buffer.get()));
221
222 EXPECT_EQ(0, ConvertToI420(kYUY2, out_yuy2_buffer.get(), 0, 0, width_,
223 height_, 0, kVideoRotation_0, &res_i420_frame));
224
225 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
226 return;
227 }
228
229 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
230 EXPECT_EQ(48.0, psnr);
231 printf("\nConvert #%d I420 <-> RGB565\n", j);
232 rtc::scoped_ptr<uint8_t[]> out_rgb565_buffer(
233 new uint8_t[width_ * height_ * 2]);
234 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kRGB565, 0,
235 out_rgb565_buffer.get()));
236
237 EXPECT_EQ(0, ConvertToI420(kRGB565, out_rgb565_buffer.get(), 0, 0, width_,
238 height_, 0, kVideoRotation_0, &res_i420_frame));
239
240 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
241 return;
242 }
243 j++;
244
245 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
246 // TODO(leozwang) Investigate the right psnr should be set for I420ToRGB565,
247 // Another example is I420ToRGB24, the psnr is 44
248 // TODO(mikhal): Add psnr for RGB565, 1555, 4444, convert to ARGB.
249 EXPECT_GT(ceil(psnr), 40);
250
251 printf("\nConvert #%d I420 <-> ARGB8888\n", j);
252 rtc::scoped_ptr<uint8_t[]> out_argb8888_buffer(
253 new uint8_t[width_ * height_ * 4]);
254 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kARGB, 0,
255 out_argb8888_buffer.get()));
256
257 EXPECT_EQ(0, ConvertToI420(kARGB, out_argb8888_buffer.get(), 0, 0, width_,
258 height_, 0, kVideoRotation_0, &res_i420_frame));
259
260 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
261 return;
262 }
263
264 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
265 // TODO(leozwang) Investigate the right psnr should be set for I420ToARGB8888,
266 EXPECT_GT(ceil(psnr), 42);
267
268 ASSERT_EQ(0, fclose(output_file));
269 }
270
TEST_F(TestLibYuv,ConvertAlignedFrame)271 TEST_F(TestLibYuv, ConvertAlignedFrame) {
272 // Reading YUV frame - testing on the first frame of the foreman sequence
273 std::string output_file_name = webrtc::test::OutputPath() +
274 "LibYuvTest_conversion.yuv";
275 FILE* output_file = fopen(output_file_name.c_str(), "wb");
276 ASSERT_TRUE(output_file != NULL);
277
278 double psnr = 0.0;
279
280 VideoFrame res_i420_frame;
281 int stride_y = 0;
282 int stride_uv = 0;
283 Calc16ByteAlignedStride(width_, &stride_y, &stride_uv);
284 EXPECT_EQ(0, res_i420_frame.CreateEmptyFrame(width_, height_,
285 stride_y, stride_uv, stride_uv));
286 rtc::scoped_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]);
287 EXPECT_EQ(0, ConvertFromI420(orig_frame_, kI420, 0,
288 out_i420_buffer.get()));
289 EXPECT_EQ(0, ConvertToI420(kI420, out_i420_buffer.get(), 0, 0, width_,
290 height_, 0, kVideoRotation_0, &res_i420_frame));
291
292 if (PrintVideoFrame(res_i420_frame, output_file) < 0) {
293 return;
294 }
295 psnr = I420PSNR(&orig_frame_, &res_i420_frame);
296 EXPECT_EQ(48.0, psnr);
297 }
298
299
TEST_F(TestLibYuv,RotateTest)300 TEST_F(TestLibYuv, RotateTest) {
301 // Use ConvertToI420 for multiple roatations - see that nothing breaks, all
302 // memory is properly allocated and end result is equal to the starting point.
303 VideoFrame rotated_res_i420_frame;
304 int rotated_width = height_;
305 int rotated_height = width_;
306 int stride_y;
307 int stride_uv;
308 Calc16ByteAlignedStride(rotated_width, &stride_y, &stride_uv);
309 EXPECT_EQ(0, rotated_res_i420_frame.CreateEmptyFrame(rotated_width,
310 rotated_height,
311 stride_y,
312 stride_uv,
313 stride_uv));
314 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer_.get(), 0, 0, width_, height_,
315 0, kVideoRotation_90, &rotated_res_i420_frame));
316 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer_.get(), 0, 0, width_, height_,
317 0, kVideoRotation_270, &rotated_res_i420_frame));
318 EXPECT_EQ(0, rotated_res_i420_frame.CreateEmptyFrame(width_, height_,
319 width_, (width_ + 1) / 2,
320 (width_ + 1) / 2));
321 EXPECT_EQ(0, ConvertToI420(kI420, orig_buffer_.get(), 0, 0, width_, height_,
322 0, kVideoRotation_180, &rotated_res_i420_frame));
323 }
324
TEST_F(TestLibYuv,alignment)325 TEST_F(TestLibYuv, alignment) {
326 int value = 0x3FF; // 1023
327 EXPECT_EQ(0x400, AlignInt(value, 128)); // Low 7 bits are zero.
328 EXPECT_EQ(0x400, AlignInt(value, 64)); // Low 6 bits are zero.
329 EXPECT_EQ(0x400, AlignInt(value, 32)); // Low 5 bits are zero.
330 }
331
TEST_F(TestLibYuv,StrideAlignment)332 TEST_F(TestLibYuv, StrideAlignment) {
333 int stride_y = 0;
334 int stride_uv = 0;
335 int width = 52;
336 Calc16ByteAlignedStride(width, &stride_y, &stride_uv);
337 EXPECT_EQ(64, stride_y);
338 EXPECT_EQ(32, stride_uv);
339 width = 128;
340 Calc16ByteAlignedStride(width, &stride_y, &stride_uv);
341 EXPECT_EQ(128, stride_y);
342 EXPECT_EQ(64, stride_uv);
343 width = 127;
344 Calc16ByteAlignedStride(width, &stride_y, &stride_uv);
345 EXPECT_EQ(128, stride_y);
346 EXPECT_EQ(64, stride_uv);
347 }
348
349 } // namespace webrtc
350