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