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