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 21namespace webrtc { 22 23int 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 40int 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. 59void 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 78class 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 94TestLibYuv::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 105void 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 123void TestLibYuv::TearDown() { 124 if (source_file_ != NULL) { 125 ASSERT_EQ(0, fclose(source_file_)); 126 } 127 source_file_ = NULL; 128} 129 130TEST_F(TestLibYuv, ConvertSanityTest) { 131 // TODO(mikhal) 132} 133 134TEST_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 271TEST_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 300TEST_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 325TEST_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 332TEST_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