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
20namespace webrtc {
21
22class 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
50TestScaler::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
61void 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
71void TestScaler::TearDown() {
72  if (source_file_ != NULL) {
73    ASSERT_EQ(0, fclose(source_file_));
74  }
75  source_file_ = NULL;
76}
77
78TEST_F(TestScaler, ScaleWithoutSettingValues) {
79  EXPECT_EQ(-2, test_scaler_.Scale(test_frame_, &test_frame_));
80}
81
82TEST_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
90TEST_F(TestScaler, ScaleSendingNullSourcePointer) {
91  I420VideoFrame null_src_frame;
92  EXPECT_EQ(-1, test_scaler_.Scale(null_src_frame, &test_frame_));
93}
94
95TEST_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.
118TEST_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
205TEST_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
299TEST_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
386double 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.
425void 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