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/common_video/libyuv/include/scaler.h" 16 #include "webrtc/system_wrappers/interface/tick_util.h" 17 #include "webrtc/test/testsupport/fileutils.h" 18 #include "webrtc/test/testsupport/gtest_disable.h" 19 20 namespace webrtc { 21 22 class TestScaler : public ::testing::Test { 23 protected: 24 TestScaler(); 25 virtual void SetUp(); 26 virtual void TearDown(); 27 28 void ScaleSequence(ScaleMethod method, 29 FILE* source_file, std::string out_name, 30 int src_width, int src_height, 31 int dst_width, int dst_height); 32 // Computes the sequence average PSNR between an input sequence in 33 // |input_file| and an output sequence with filename |out_name|. |width| and 34 // |height| are the frame sizes of both sequences. 35 double ComputeAvgSequencePSNR(FILE* input_file, std::string out_name, 36 int width, int height); 37 38 Scaler test_scaler_; 39 FILE* source_file_; 40 I420VideoFrame test_frame_; 41 const int width_; 42 const int half_width_; 43 const int height_; 44 const int half_height_; 45 const int size_y_; 46 const int size_uv_; 47 const int frame_length_; 48 }; 49 50 TestScaler::TestScaler() 51 : source_file_(NULL), 52 width_(352), 53 half_width_(width_ / 2), 54 height_(288), 55 half_height_(height_ / 2), 56 size_y_(width_ * height_), 57 size_uv_(half_width_ * half_height_), 58 frame_length_(CalcBufferSize(kI420, width_, height_)) { 59 } 60 61 void TestScaler::SetUp() { 62 const std::string input_file_name = 63 webrtc::test::ResourcePath("foreman_cif", "yuv"); 64 source_file_ = fopen(input_file_name.c_str(), "rb"); 65 ASSERT_TRUE(source_file_ != NULL) << "Cannot read file: "<< 66 input_file_name << "\n"; 67 test_frame_.CreateEmptyFrame(width_, height_, 68 width_, half_width_, half_width_); 69 } 70 71 void TestScaler::TearDown() { 72 if (source_file_ != NULL) { 73 ASSERT_EQ(0, fclose(source_file_)); 74 } 75 source_file_ = NULL; 76 } 77 78 TEST_F(TestScaler, ScaleWithoutSettingValues) { 79 EXPECT_EQ(-2, test_scaler_.Scale(test_frame_, &test_frame_)); 80 } 81 82 TEST_F(TestScaler, ScaleBadInitialValues) { 83 EXPECT_EQ(-1, test_scaler_.Set(0, 288, 352, 288, kI420, kI420, kScalePoint)); 84 EXPECT_EQ(-1, test_scaler_.Set(704, 0, 352, 288, kI420, kI420, kScaleBox)); 85 EXPECT_EQ(-1, test_scaler_.Set(704, 576, 352, 0, kI420, kI420, 86 kScaleBilinear)); 87 EXPECT_EQ(-1, test_scaler_.Set(704, 576, 0, 288, kI420, kI420, kScalePoint)); 88 } 89 90 TEST_F(TestScaler, ScaleSendingNullSourcePointer) { 91 I420VideoFrame null_src_frame; 92 EXPECT_EQ(-1, test_scaler_.Scale(null_src_frame, &test_frame_)); 93 } 94 95 TEST_F(TestScaler, ScaleSendingBufferTooSmall) { 96 // Sending a buffer which is too small (should reallocate and update size) 97 EXPECT_EQ(0, test_scaler_.Set(width_, height_, 98 half_width_, half_height_, 99 kI420, kI420, 100 kScalePoint)); 101 I420VideoFrame test_frame2; 102 scoped_ptr<uint8_t[]> orig_buffer(new uint8_t[frame_length_]); 103 EXPECT_GT(fread(orig_buffer.get(), 1, frame_length_, source_file_), 0U); 104 test_frame_.CreateFrame(size_y_, orig_buffer.get(), 105 size_uv_, orig_buffer.get() + size_y_, 106 size_uv_, orig_buffer.get() + size_y_ + size_uv_, 107 width_, height_, 108 width_, half_width_, half_width_); 109 EXPECT_EQ(0, test_scaler_.Scale(test_frame_, &test_frame2)); 110 EXPECT_GT(width_ * height_, test_frame2.allocated_size(kYPlane)); 111 EXPECT_GT(size_uv_, test_frame2.allocated_size(kUPlane)); 112 EXPECT_GT(size_uv_, test_frame2.allocated_size(kVPlane)); 113 EXPECT_EQ(half_width_, test_frame2.width()); 114 EXPECT_EQ(half_height_, test_frame2.height()); 115 } 116 117 //TODO (mikhal): Converge the test into one function that accepts the method. 118 TEST_F(TestScaler, DISABLED_ON_ANDROID(PointScaleTest)) { 119 double avg_psnr; 120 FILE* source_file2; 121 ScaleMethod method = kScalePoint; 122 std::string out_name = webrtc::test::OutputPath() + 123 "LibYuvTest_PointScale_176_144.yuv"; 124 ScaleSequence(method, 125 source_file_, out_name, 126 width_, height_, 127 half_width_, half_height_); 128 // Upsample back up and check PSNR. 129 source_file2 = fopen(out_name.c_str(), "rb"); 130 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_" 131 "upfrom_176_144.yuv"; 132 ScaleSequence(method, 133 source_file2, out_name, 134 176, 144, 135 352, 288); 136 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 137 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 138 "original size: %f \n", width_, height_, 176, 144, avg_psnr); 139 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 140 // average PSNR under same conditions. 141 ASSERT_GT(avg_psnr, 27.9); 142 ASSERT_EQ(0, fclose(source_file2)); 143 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_320_240.yuv"; 144 ScaleSequence(method, 145 source_file_, out_name, 146 width_, height_, 147 320, 240); 148 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_704_576.yuv"; 149 ScaleSequence(method, 150 source_file_, out_name, 151 width_, height_, 152 width_ * 2, height_ * 2); 153 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_300_200.yuv"; 154 ScaleSequence(method, 155 source_file_, out_name, 156 width_, height_, 157 300, 200); 158 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_400_300.yuv"; 159 ScaleSequence(method, 160 source_file_, out_name, 161 width_, height_, 162 400, 300); 163 // Down-sample to odd size frame and scale back up. 164 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_282_231.yuv"; 165 ScaleSequence(method, 166 source_file_, out_name, 167 width_, height_, 168 282, 231); 169 source_file2 = fopen(out_name.c_str(), "rb"); 170 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_" 171 "upfrom_282_231.yuv"; 172 ScaleSequence(method, 173 source_file2, out_name, 174 282, 231, 175 352, 288); 176 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 177 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 178 "original size: %f \n", width_, height_, 282, 231, avg_psnr); 179 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 180 // average PSNR under same conditions. 181 ASSERT_GT(avg_psnr, 25.8); 182 ASSERT_EQ(0, fclose(source_file2)); 183 // Up-sample to odd size frame and scale back down. 184 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_699_531.yuv"; 185 ScaleSequence(method, 186 source_file_, out_name, 187 width_, height_, 188 699, 531); 189 source_file2 = fopen(out_name.c_str(), "rb"); 190 out_name = webrtc::test::OutputPath() + "LibYuvTest_PointScale_352_288_" 191 "downfrom_699_531.yuv"; 192 ScaleSequence(method, 193 source_file2, out_name, 194 699, 531, 195 352, 288); 196 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 197 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 198 "original size: %f \n", width_, height_, 699, 531, avg_psnr); 199 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 200 // average PSNR under same conditions. 201 ASSERT_GT(avg_psnr, 37.8); 202 ASSERT_EQ(0, fclose(source_file2)); 203 } 204 205 TEST_F(TestScaler, DISABLED_ON_ANDROID(BiLinearScaleTest)) { 206 double avg_psnr; 207 FILE* source_file2; 208 ScaleMethod method = kScaleBilinear; 209 std::string out_name = webrtc::test::OutputPath() + 210 "LibYuvTest_BilinearScale_176_144.yuv"; 211 ScaleSequence(method, 212 source_file_, out_name, 213 width_, height_, 214 width_ / 2, height_ / 2); 215 // Up-sample back up and check PSNR. 216 source_file2 = fopen(out_name.c_str(), "rb"); 217 out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_" 218 "upfrom_176_144.yuv"; 219 ScaleSequence(method, 220 source_file2, out_name, 221 176, 144, 222 352, 288); 223 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 224 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 225 "original size: %f \n", width_, height_, 176, 144, avg_psnr); 226 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 227 // average PSNR under same conditions. 228 ASSERT_GT(avg_psnr, 27.5); 229 ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 230 ASSERT_EQ(0, fclose(source_file2)); 231 out_name = webrtc::test::OutputPath() + 232 "LibYuvTest_BilinearScale_320_240.yuv"; 233 ScaleSequence(method, 234 source_file_, out_name, 235 width_, height_, 236 320, 240); 237 out_name = webrtc::test::OutputPath() + 238 "LibYuvTest_BilinearScale_704_576.yuv"; 239 ScaleSequence(method, 240 source_file_, out_name, 241 width_, height_, 242 width_ * 2, height_ * 2); 243 out_name = webrtc::test::OutputPath() + 244 "LibYuvTest_BilinearScale_300_200.yuv"; 245 ScaleSequence(method, 246 source_file_, out_name, 247 width_, height_, 248 300, 200); 249 out_name = webrtc::test::OutputPath() + 250 "LibYuvTest_BilinearScale_400_300.yuv"; 251 ScaleSequence(method, 252 source_file_, out_name, 253 width_, height_, 254 400, 300); 255 // Down-sample to odd size frame and scale back up. 256 out_name = webrtc::test::OutputPath() + 257 "LibYuvTest_BilinearScale_282_231.yuv"; 258 ScaleSequence(method, 259 source_file_, out_name, 260 width_, height_, 261 282, 231); 262 source_file2 = fopen(out_name.c_str(), "rb"); 263 out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_" 264 "upfrom_282_231.yuv"; 265 ScaleSequence(method, 266 source_file2, out_name, 267 282, 231, 268 width_, height_); 269 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 270 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 271 "original size: %f \n", width_, height_, 282, 231, avg_psnr); 272 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 273 // average PSNR under same conditions. 274 ASSERT_GT(avg_psnr, 29.7); 275 ASSERT_EQ(0, fclose(source_file2)); 276 // Upsample to odd size frame and scale back down. 277 out_name = webrtc::test::OutputPath() + 278 "LibYuvTest_BilinearScale_699_531.yuv"; 279 ScaleSequence(method, 280 source_file_, out_name, 281 width_, height_, 282 699, 531); 283 source_file2 = fopen(out_name.c_str(), "rb"); 284 out_name = webrtc::test::OutputPath() + "LibYuvTest_BilinearScale_352_288_" 285 "downfrom_699_531.yuv"; 286 ScaleSequence(method, 287 source_file2, out_name, 288 699, 531, 289 width_, height_); 290 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 291 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 292 "original size: %f \n", width_, height_, 699, 531, avg_psnr); 293 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 294 // average PSNR under same conditions. 295 ASSERT_GT(avg_psnr, 31.4); 296 ASSERT_EQ(0, fclose(source_file2)); 297 } 298 299 TEST_F(TestScaler, DISABLED_ON_ANDROID(BoxScaleTest)) { 300 double avg_psnr; 301 FILE* source_file2; 302 ScaleMethod method = kScaleBox; 303 std::string out_name = webrtc::test::OutputPath() + 304 "LibYuvTest_BoxScale_176_144.yuv"; 305 ScaleSequence(method, 306 source_file_, out_name, 307 width_, height_, 308 width_ / 2, height_ / 2); 309 // Up-sample back up and check PSNR. 310 source_file2 = fopen(out_name.c_str(), "rb"); 311 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_" 312 "upfrom_176_144.yuv"; 313 ScaleSequence(method, 314 source_file2, out_name, 315 176, 144, 316 352, 288); 317 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 318 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 319 "original size: %f \n", width_, height_, 176, 144, avg_psnr); 320 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 321 // average PSNR under same conditions. 322 ASSERT_GT(avg_psnr, 27.5); 323 ASSERT_EQ(0, fclose(source_file2)); 324 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_320_240.yuv"; 325 ScaleSequence(method, 326 source_file_, out_name, 327 width_, height_, 328 320, 240); 329 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_704_576.yuv"; 330 ScaleSequence(method, 331 source_file_, out_name, 332 width_, height_, 333 width_ * 2, height_ * 2); 334 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_300_200.yuv"; 335 ScaleSequence(method, 336 source_file_, out_name, 337 width_, height_, 338 300, 200); 339 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_400_300.yuv"; 340 ScaleSequence(method, 341 source_file_, out_name, 342 width_, height_, 343 400, 300); 344 // Down-sample to odd size frame and scale back up. 345 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_282_231.yuv"; 346 ScaleSequence(method, 347 source_file_, out_name, 348 width_, height_, 349 282, 231); 350 source_file2 = fopen(out_name.c_str(), "rb"); 351 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_" 352 "upfrom_282_231.yuv"; 353 ScaleSequence(method, 354 source_file2, out_name, 355 282, 231, 356 width_, height_); 357 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 358 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 359 "original size: %f \n", width_, height_, 282, 231, avg_psnr); 360 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 361 // average PSNR under same conditions. 362 ASSERT_GT(avg_psnr, 29.7); 363 ASSERT_EQ(0, fclose(source_file2)); 364 // Up-sample to odd size frame and scale back down. 365 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_699_531.yuv"; 366 ScaleSequence(method, 367 source_file_, out_name, 368 width_, height_, 369 699, 531); 370 source_file2 = fopen(out_name.c_str(), "rb"); 371 out_name = webrtc::test::OutputPath() + "LibYuvTest_BoxScale_352_288_" 372 "downfrom_699_531.yuv"; 373 ScaleSequence(method, 374 source_file2, out_name, 375 699, 531, 376 width_, height_); 377 avg_psnr = ComputeAvgSequencePSNR(source_file_, out_name, width_, height_); 378 printf("PSNR for scaling from: %d %d, down/up to: %d %d, and back to " 379 "original size: %f \n", width_, height_, 699, 531, avg_psnr); 380 // Average PSNR for lower bound in assert is ~0.1dB lower than the actual 381 // average PSNR under same conditions. 382 ASSERT_GT(avg_psnr, 31.4); 383 ASSERT_EQ(0, fclose(source_file2)); 384 } 385 386 double TestScaler::ComputeAvgSequencePSNR(FILE* input_file, 387 std::string out_name, 388 int width, int height) { 389 FILE* output_file; 390 output_file = fopen(out_name.c_str(), "rb"); 391 assert(output_file != NULL); 392 rewind(input_file); 393 rewind(output_file); 394 395 int required_size = CalcBufferSize(kI420, width, height); 396 uint8_t* input_buffer = new uint8_t[required_size]; 397 uint8_t* output_buffer = new uint8_t[required_size]; 398 399 int frame_count = 0; 400 double avg_psnr = 0; 401 I420VideoFrame in_frame, out_frame; 402 while (feof(input_file) == 0) { 403 if ((size_t)required_size != 404 fread(input_buffer, 1, required_size, input_file)) { 405 break; 406 } 407 if ((size_t)required_size != 408 fread(output_buffer, 1, required_size, output_file)) { 409 break; 410 } 411 frame_count++; 412 ConvertFromI420(in_frame, kI420, 0, input_buffer); 413 ConvertFromI420(out_frame, kI420, 0, output_buffer); 414 double psnr = I420PSNR(&in_frame, &out_frame); 415 avg_psnr += psnr; 416 } 417 avg_psnr = avg_psnr / frame_count; 418 assert(0 == fclose(output_file)); 419 delete [] input_buffer; 420 delete [] output_buffer; 421 return avg_psnr; 422 } 423 424 // TODO (mikhal): Move part to a separate scale test. 425 void TestScaler::ScaleSequence(ScaleMethod method, 426 FILE* source_file, std::string out_name, 427 int src_width, int src_height, 428 int dst_width, int dst_height) { 429 FILE* output_file; 430 EXPECT_EQ(0, test_scaler_.Set(src_width, src_height, 431 dst_width, dst_height, 432 kI420, kI420, method)); 433 434 output_file = fopen(out_name.c_str(), "wb"); 435 ASSERT_TRUE(output_file != NULL); 436 437 rewind(source_file); 438 439 I420VideoFrame input_frame; 440 I420VideoFrame output_frame; 441 int64_t start_clock, total_clock; 442 total_clock = 0; 443 int frame_count = 0; 444 int src_required_size = CalcBufferSize(kI420, src_width, src_height); 445 scoped_ptr<uint8_t[]> frame_buffer(new uint8_t[src_required_size]); 446 int size_y = src_width * src_height; 447 int size_uv = ((src_width + 1) / 2) * ((src_height + 1) / 2); 448 449 // Running through entire sequence. 450 while (feof(source_file) == 0) { 451 if ((size_t)src_required_size != 452 fread(frame_buffer.get(), 1, src_required_size, source_file)) 453 break; 454 455 input_frame.CreateFrame(size_y, frame_buffer.get(), 456 size_uv, frame_buffer.get() + size_y, 457 size_uv, frame_buffer.get() + size_y + size_uv, 458 src_width, src_height, 459 src_width, (src_width + 1) / 2, 460 (src_width + 1) / 2); 461 462 start_clock = TickTime::MillisecondTimestamp(); 463 EXPECT_EQ(0, test_scaler_.Scale(input_frame, &output_frame)); 464 total_clock += TickTime::MillisecondTimestamp() - start_clock; 465 if (PrintI420VideoFrame(output_frame, output_file) < 0) { 466 return; 467 } 468 frame_count++; 469 } 470 471 if (frame_count) { 472 printf("Scaling[%d %d] => [%d %d]: ", 473 src_width, src_height, dst_width, dst_height); 474 printf("Average time per frame[ms]: %.2lf\n", 475 (static_cast<double>(total_clock) / frame_count)); 476 } 477 ASSERT_EQ(0, fclose(output_file)); 478 } 479 480 } // namespace webrtc 481