• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TestScaler()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 
SetUp()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 
TearDown()70 void TestScaler::TearDown() {
71   if (source_file_ != NULL) {
72     ASSERT_EQ(0, fclose(source_file_));
73   }
74   source_file_ = NULL;
75 }
76 
TEST_F(TestScaler,ScaleWithoutSettingValues)77 TEST_F(TestScaler, ScaleWithoutSettingValues) {
78   EXPECT_EQ(-2, test_scaler_.Scale(test_frame_, &test_frame_));
79 }
80 
TEST_F(TestScaler,ScaleBadInitialValues)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 
TEST_F(TestScaler,ScaleSendingNullSourcePointer)89 TEST_F(TestScaler, ScaleSendingNullSourcePointer) {
90   VideoFrame null_src_frame;
91   EXPECT_EQ(-1, test_scaler_.Scale(null_src_frame, &test_frame_));
92 }
93 
TEST_F(TestScaler,ScaleSendingBufferTooSmall)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
TEST_F(TestScaler,MAYBE_PointScaleTest)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
TEST_F(TestScaler,MAYBE_BiLinearScaleTest)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
TEST_F(TestScaler,MAYBE_BoxScaleTest)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 
ComputeAvgSequencePSNR(FILE * input_file,std::string out_name,int width,int height)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.
ScaleSequence(ScaleMethod method,FILE * source_file,std::string out_name,int src_width,int src_height,int dst_width,int dst_height)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