xref: /aosp_15_r20/external/webrtc/common_video/libyuv/libyuv_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "third_party/libyuv/include/libyuv.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <math.h>
14*d9f75844SAndroid Build Coastguard Worker #include <string.h>
15*d9f75844SAndroid Build Coastguard Worker 
16*d9f75844SAndroid Build Coastguard Worker #include <memory>
17*d9f75844SAndroid Build Coastguard Worker 
18*d9f75844SAndroid Build Coastguard Worker #include "api/video/i420_buffer.h"
19*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_frame.h"
20*d9f75844SAndroid Build Coastguard Worker #include "common_video/libyuv/include/webrtc_libyuv.h"
21*d9f75844SAndroid Build Coastguard Worker #include "test/frame_utils.h"
22*d9f75844SAndroid Build Coastguard Worker #include "test/gmock.h"
23*d9f75844SAndroid Build Coastguard Worker #include "test/gtest.h"
24*d9f75844SAndroid Build Coastguard Worker #include "test/testsupport/file_utils.h"
25*d9f75844SAndroid Build Coastguard Worker 
26*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
27*d9f75844SAndroid Build Coastguard Worker 
28*d9f75844SAndroid Build Coastguard Worker namespace {
Calc16ByteAlignedStride(int width,int * stride_y,int * stride_uv)29*d9f75844SAndroid Build Coastguard Worker void Calc16ByteAlignedStride(int width, int* stride_y, int* stride_uv) {
30*d9f75844SAndroid Build Coastguard Worker   *stride_y = 16 * ((width + 15) / 16);
31*d9f75844SAndroid Build Coastguard Worker   *stride_uv = 16 * ((width + 31) / 32);
32*d9f75844SAndroid Build Coastguard Worker }
33*d9f75844SAndroid Build Coastguard Worker 
PrintPlane(const uint8_t * buf,int width,int height,int stride,FILE * file)34*d9f75844SAndroid Build Coastguard Worker int PrintPlane(const uint8_t* buf,
35*d9f75844SAndroid Build Coastguard Worker                int width,
36*d9f75844SAndroid Build Coastguard Worker                int height,
37*d9f75844SAndroid Build Coastguard Worker                int stride,
38*d9f75844SAndroid Build Coastguard Worker                FILE* file) {
39*d9f75844SAndroid Build Coastguard Worker   for (int i = 0; i < height; i++, buf += stride) {
40*d9f75844SAndroid Build Coastguard Worker     if (fwrite(buf, 1, width, file) != static_cast<unsigned int>(width))
41*d9f75844SAndroid Build Coastguard Worker       return -1;
42*d9f75844SAndroid Build Coastguard Worker   }
43*d9f75844SAndroid Build Coastguard Worker   return 0;
44*d9f75844SAndroid Build Coastguard Worker }
45*d9f75844SAndroid Build Coastguard Worker 
PrintVideoFrame(const I420BufferInterface & frame,FILE * file)46*d9f75844SAndroid Build Coastguard Worker int PrintVideoFrame(const I420BufferInterface& frame, FILE* file) {
47*d9f75844SAndroid Build Coastguard Worker   int width = frame.width();
48*d9f75844SAndroid Build Coastguard Worker   int height = frame.height();
49*d9f75844SAndroid Build Coastguard Worker   int chroma_width = frame.ChromaWidth();
50*d9f75844SAndroid Build Coastguard Worker   int chroma_height = frame.ChromaHeight();
51*d9f75844SAndroid Build Coastguard Worker 
52*d9f75844SAndroid Build Coastguard Worker   if (PrintPlane(frame.DataY(), width, height, frame.StrideY(), file) < 0) {
53*d9f75844SAndroid Build Coastguard Worker     return -1;
54*d9f75844SAndroid Build Coastguard Worker   }
55*d9f75844SAndroid Build Coastguard Worker   if (PrintPlane(frame.DataU(), chroma_width, chroma_height, frame.StrideU(),
56*d9f75844SAndroid Build Coastguard Worker                  file) < 0) {
57*d9f75844SAndroid Build Coastguard Worker     return -1;
58*d9f75844SAndroid Build Coastguard Worker   }
59*d9f75844SAndroid Build Coastguard Worker   if (PrintPlane(frame.DataV(), chroma_width, chroma_height, frame.StrideV(),
60*d9f75844SAndroid Build Coastguard Worker                  file) < 0) {
61*d9f75844SAndroid Build Coastguard Worker     return -1;
62*d9f75844SAndroid Build Coastguard Worker   }
63*d9f75844SAndroid Build Coastguard Worker   return 0;
64*d9f75844SAndroid Build Coastguard Worker }
65*d9f75844SAndroid Build Coastguard Worker 
66*d9f75844SAndroid Build Coastguard Worker }  // Anonymous namespace
67*d9f75844SAndroid Build Coastguard Worker 
68*d9f75844SAndroid Build Coastguard Worker class TestLibYuv : public ::testing::Test {
69*d9f75844SAndroid Build Coastguard Worker  protected:
70*d9f75844SAndroid Build Coastguard Worker   TestLibYuv();
71*d9f75844SAndroid Build Coastguard Worker   void SetUp() override;
72*d9f75844SAndroid Build Coastguard Worker   void TearDown() override;
73*d9f75844SAndroid Build Coastguard Worker 
74*d9f75844SAndroid Build Coastguard Worker   FILE* source_file_;
75*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<VideoFrame> orig_frame_;
76*d9f75844SAndroid Build Coastguard Worker   const int width_;
77*d9f75844SAndroid Build Coastguard Worker   const int height_;
78*d9f75844SAndroid Build Coastguard Worker   const int size_y_;
79*d9f75844SAndroid Build Coastguard Worker   const int size_uv_;
80*d9f75844SAndroid Build Coastguard Worker   const size_t frame_length_;
81*d9f75844SAndroid Build Coastguard Worker };
82*d9f75844SAndroid Build Coastguard Worker 
TestLibYuv()83*d9f75844SAndroid Build Coastguard Worker TestLibYuv::TestLibYuv()
84*d9f75844SAndroid Build Coastguard Worker     : source_file_(NULL),
85*d9f75844SAndroid Build Coastguard Worker       orig_frame_(),
86*d9f75844SAndroid Build Coastguard Worker       width_(352),
87*d9f75844SAndroid Build Coastguard Worker       height_(288),
88*d9f75844SAndroid Build Coastguard Worker       size_y_(width_ * height_),
89*d9f75844SAndroid Build Coastguard Worker       size_uv_(((width_ + 1) / 2) * ((height_ + 1) / 2)),
90*d9f75844SAndroid Build Coastguard Worker       frame_length_(CalcBufferSize(VideoType::kI420, 352, 288)) {}
91*d9f75844SAndroid Build Coastguard Worker 
SetUp()92*d9f75844SAndroid Build Coastguard Worker void TestLibYuv::SetUp() {
93*d9f75844SAndroid Build Coastguard Worker   const std::string input_file_name =
94*d9f75844SAndroid Build Coastguard Worker       webrtc::test::ResourcePath("foreman_cif", "yuv");
95*d9f75844SAndroid Build Coastguard Worker   source_file_ = fopen(input_file_name.c_str(), "rb");
96*d9f75844SAndroid Build Coastguard Worker   ASSERT_TRUE(source_file_ != NULL)
97*d9f75844SAndroid Build Coastguard Worker       << "Cannot read file: " << input_file_name << "\n";
98*d9f75844SAndroid Build Coastguard Worker 
99*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420BufferInterface> buffer(
100*d9f75844SAndroid Build Coastguard Worker       test::ReadI420Buffer(width_, height_, source_file_));
101*d9f75844SAndroid Build Coastguard Worker 
102*d9f75844SAndroid Build Coastguard Worker   orig_frame_ =
103*d9f75844SAndroid Build Coastguard Worker       std::make_unique<VideoFrame>(VideoFrame::Builder()
104*d9f75844SAndroid Build Coastguard Worker                                        .set_video_frame_buffer(buffer)
105*d9f75844SAndroid Build Coastguard Worker                                        .set_rotation(webrtc::kVideoRotation_0)
106*d9f75844SAndroid Build Coastguard Worker                                        .set_timestamp_us(0)
107*d9f75844SAndroid Build Coastguard Worker                                        .build());
108*d9f75844SAndroid Build Coastguard Worker }
109*d9f75844SAndroid Build Coastguard Worker 
TearDown()110*d9f75844SAndroid Build Coastguard Worker void TestLibYuv::TearDown() {
111*d9f75844SAndroid Build Coastguard Worker   if (source_file_ != NULL) {
112*d9f75844SAndroid Build Coastguard Worker     ASSERT_EQ(0, fclose(source_file_));
113*d9f75844SAndroid Build Coastguard Worker   }
114*d9f75844SAndroid Build Coastguard Worker   source_file_ = NULL;
115*d9f75844SAndroid Build Coastguard Worker }
116*d9f75844SAndroid Build Coastguard Worker 
TEST_F(TestLibYuv,ConvertTest)117*d9f75844SAndroid Build Coastguard Worker TEST_F(TestLibYuv, ConvertTest) {
118*d9f75844SAndroid Build Coastguard Worker   // Reading YUV frame - testing on the first frame of the foreman sequence
119*d9f75844SAndroid Build Coastguard Worker   int j = 0;
120*d9f75844SAndroid Build Coastguard Worker   std::string output_file_name =
121*d9f75844SAndroid Build Coastguard Worker       webrtc::test::OutputPath() + "LibYuvTest_conversion.yuv";
122*d9f75844SAndroid Build Coastguard Worker   FILE* output_file = fopen(output_file_name.c_str(), "wb");
123*d9f75844SAndroid Build Coastguard Worker   ASSERT_TRUE(output_file != NULL);
124*d9f75844SAndroid Build Coastguard Worker 
125*d9f75844SAndroid Build Coastguard Worker   double psnr = 0.0;
126*d9f75844SAndroid Build Coastguard Worker 
127*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> res_i420_buffer =
128*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Create(width_, height_);
129*d9f75844SAndroid Build Coastguard Worker 
130*d9f75844SAndroid Build Coastguard Worker   printf("\nConvert #%d I420 <-> I420 \n", j);
131*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]);
132*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kI420, 0,
133*d9f75844SAndroid Build Coastguard Worker                                out_i420_buffer.get()));
134*d9f75844SAndroid Build Coastguard Worker   int y_size = width_ * height_;
135*d9f75844SAndroid Build Coastguard Worker   int u_size = res_i420_buffer->ChromaWidth() * res_i420_buffer->ChromaHeight();
136*d9f75844SAndroid Build Coastguard Worker   int ret = libyuv::I420Copy(
137*d9f75844SAndroid Build Coastguard Worker       out_i420_buffer.get(), width_, out_i420_buffer.get() + y_size,
138*d9f75844SAndroid Build Coastguard Worker       width_ >> 1, out_i420_buffer.get() + y_size + u_size, width_ >> 1,
139*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->MutableDataY(), res_i420_buffer.get()->StrideY(),
140*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->MutableDataU(), res_i420_buffer.get()->StrideU(),
141*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->MutableDataV(), res_i420_buffer.get()->StrideV(),
142*d9f75844SAndroid Build Coastguard Worker       width_, height_);
143*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
144*d9f75844SAndroid Build Coastguard Worker 
145*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
146*d9f75844SAndroid Build Coastguard Worker     return;
147*d9f75844SAndroid Build Coastguard Worker   }
148*d9f75844SAndroid Build Coastguard Worker   psnr =
149*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
150*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(48.0, psnr);
151*d9f75844SAndroid Build Coastguard Worker   j++;
152*d9f75844SAndroid Build Coastguard Worker 
153*d9f75844SAndroid Build Coastguard Worker   printf("\nConvert #%d I420 <-> RGB24\n", j);
154*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> res_rgb_buffer2(new uint8_t[width_ * height_ * 3]);
155*d9f75844SAndroid Build Coastguard Worker   // Align the stride values for the output frame.
156*d9f75844SAndroid Build Coastguard Worker   int stride_y = 0;
157*d9f75844SAndroid Build Coastguard Worker   int stride_uv = 0;
158*d9f75844SAndroid Build Coastguard Worker   Calc16ByteAlignedStride(width_, &stride_y, &stride_uv);
159*d9f75844SAndroid Build Coastguard Worker   res_i420_buffer =
160*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Create(width_, height_, stride_y, stride_uv, stride_uv);
161*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kRGB24, 0,
162*d9f75844SAndroid Build Coastguard Worker                                res_rgb_buffer2.get()));
163*d9f75844SAndroid Build Coastguard Worker 
164*d9f75844SAndroid Build Coastguard Worker   ret = libyuv::ConvertToI420(
165*d9f75844SAndroid Build Coastguard Worker       res_rgb_buffer2.get(), 0, res_i420_buffer.get()->MutableDataY(),
166*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(),
167*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(),
168*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideV(), 0, 0, width_, height_,
169*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
170*d9f75844SAndroid Build Coastguard Worker       ConvertVideoType(VideoType::kRGB24));
171*d9f75844SAndroid Build Coastguard Worker 
172*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
173*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
174*d9f75844SAndroid Build Coastguard Worker     return;
175*d9f75844SAndroid Build Coastguard Worker   }
176*d9f75844SAndroid Build Coastguard Worker   psnr =
177*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
178*d9f75844SAndroid Build Coastguard Worker 
179*d9f75844SAndroid Build Coastguard Worker   // Optimization Speed- quality trade-off => 45 dB only (platform dependant).
180*d9f75844SAndroid Build Coastguard Worker   EXPECT_GT(ceil(psnr), 44);
181*d9f75844SAndroid Build Coastguard Worker   j++;
182*d9f75844SAndroid Build Coastguard Worker 
183*d9f75844SAndroid Build Coastguard Worker   printf("\nConvert #%d I420 <-> UYVY\n", j);
184*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> out_uyvy_buffer(new uint8_t[width_ * height_ * 2]);
185*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kUYVY, 0,
186*d9f75844SAndroid Build Coastguard Worker                                out_uyvy_buffer.get()));
187*d9f75844SAndroid Build Coastguard Worker 
188*d9f75844SAndroid Build Coastguard Worker   ret = libyuv::ConvertToI420(
189*d9f75844SAndroid Build Coastguard Worker       out_uyvy_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(),
190*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(),
191*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(),
192*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideV(), 0, 0, width_, height_,
193*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
194*d9f75844SAndroid Build Coastguard Worker       ConvertVideoType(VideoType::kUYVY));
195*d9f75844SAndroid Build Coastguard Worker 
196*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
197*d9f75844SAndroid Build Coastguard Worker   psnr =
198*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
199*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(48.0, psnr);
200*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
201*d9f75844SAndroid Build Coastguard Worker     return;
202*d9f75844SAndroid Build Coastguard Worker   }
203*d9f75844SAndroid Build Coastguard Worker   j++;
204*d9f75844SAndroid Build Coastguard Worker 
205*d9f75844SAndroid Build Coastguard Worker   printf("\nConvert #%d I420 <-> YUY2\n", j);
206*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> out_yuy2_buffer(new uint8_t[width_ * height_ * 2]);
207*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kYUY2, 0,
208*d9f75844SAndroid Build Coastguard Worker                                out_yuy2_buffer.get()));
209*d9f75844SAndroid Build Coastguard Worker 
210*d9f75844SAndroid Build Coastguard Worker   ret = libyuv::ConvertToI420(
211*d9f75844SAndroid Build Coastguard Worker       out_yuy2_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(),
212*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(),
213*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(),
214*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideV(), 0, 0, width_, height_,
215*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
216*d9f75844SAndroid Build Coastguard Worker       ConvertVideoType(VideoType::kYUY2));
217*d9f75844SAndroid Build Coastguard Worker 
218*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
219*d9f75844SAndroid Build Coastguard Worker 
220*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
221*d9f75844SAndroid Build Coastguard Worker     return;
222*d9f75844SAndroid Build Coastguard Worker   }
223*d9f75844SAndroid Build Coastguard Worker 
224*d9f75844SAndroid Build Coastguard Worker   psnr =
225*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
226*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(48.0, psnr);
227*d9f75844SAndroid Build Coastguard Worker 
228*d9f75844SAndroid Build Coastguard Worker   printf("\nConvert #%d I420 <-> RGB565\n", j);
229*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> out_rgb565_buffer(
230*d9f75844SAndroid Build Coastguard Worker       new uint8_t[width_ * height_ * 2]);
231*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kRGB565, 0,
232*d9f75844SAndroid Build Coastguard Worker                                out_rgb565_buffer.get()));
233*d9f75844SAndroid Build Coastguard Worker 
234*d9f75844SAndroid Build Coastguard Worker   ret = libyuv::ConvertToI420(
235*d9f75844SAndroid Build Coastguard Worker       out_rgb565_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(),
236*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(),
237*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(),
238*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideV(), 0, 0, width_, height_,
239*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
240*d9f75844SAndroid Build Coastguard Worker       ConvertVideoType(VideoType::kRGB565));
241*d9f75844SAndroid Build Coastguard Worker 
242*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
243*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
244*d9f75844SAndroid Build Coastguard Worker     return;
245*d9f75844SAndroid Build Coastguard Worker   }
246*d9f75844SAndroid Build Coastguard Worker   j++;
247*d9f75844SAndroid Build Coastguard Worker 
248*d9f75844SAndroid Build Coastguard Worker   psnr =
249*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
250*d9f75844SAndroid Build Coastguard Worker   // TODO(leozwang) Investigate the right psnr should be set for I420ToRGB565,
251*d9f75844SAndroid Build Coastguard Worker   // Another example is I420ToRGB24, the psnr is 44
252*d9f75844SAndroid Build Coastguard Worker   // TODO(mikhal): Add psnr for RGB565, 1555, 4444, convert to ARGB.
253*d9f75844SAndroid Build Coastguard Worker   EXPECT_GT(ceil(psnr), 40);
254*d9f75844SAndroid Build Coastguard Worker 
255*d9f75844SAndroid Build Coastguard Worker   printf("\nConvert #%d I420 <-> ARGB8888\n", j);
256*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> out_argb8888_buffer(
257*d9f75844SAndroid Build Coastguard Worker       new uint8_t[width_ * height_ * 4]);
258*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kARGB, 0,
259*d9f75844SAndroid Build Coastguard Worker                                out_argb8888_buffer.get()));
260*d9f75844SAndroid Build Coastguard Worker 
261*d9f75844SAndroid Build Coastguard Worker   ret = libyuv::ConvertToI420(
262*d9f75844SAndroid Build Coastguard Worker       out_argb8888_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(),
263*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(),
264*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(),
265*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->StrideV(), 0, 0, width_, height_,
266*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
267*d9f75844SAndroid Build Coastguard Worker       ConvertVideoType(VideoType::kARGB));
268*d9f75844SAndroid Build Coastguard Worker 
269*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
270*d9f75844SAndroid Build Coastguard Worker 
271*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
272*d9f75844SAndroid Build Coastguard Worker     return;
273*d9f75844SAndroid Build Coastguard Worker   }
274*d9f75844SAndroid Build Coastguard Worker 
275*d9f75844SAndroid Build Coastguard Worker   psnr =
276*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
277*d9f75844SAndroid Build Coastguard Worker   // TODO(leozwang) Investigate the right psnr should be set for
278*d9f75844SAndroid Build Coastguard Worker   // I420ToARGB8888,
279*d9f75844SAndroid Build Coastguard Worker   EXPECT_GT(ceil(psnr), 42);
280*d9f75844SAndroid Build Coastguard Worker 
281*d9f75844SAndroid Build Coastguard Worker   ASSERT_EQ(0, fclose(output_file));
282*d9f75844SAndroid Build Coastguard Worker }
283*d9f75844SAndroid Build Coastguard Worker 
TEST_F(TestLibYuv,ConvertAlignedFrame)284*d9f75844SAndroid Build Coastguard Worker TEST_F(TestLibYuv, ConvertAlignedFrame) {
285*d9f75844SAndroid Build Coastguard Worker   // Reading YUV frame - testing on the first frame of the foreman sequence
286*d9f75844SAndroid Build Coastguard Worker   std::string output_file_name =
287*d9f75844SAndroid Build Coastguard Worker       webrtc::test::OutputPath() + "LibYuvTest_conversion.yuv";
288*d9f75844SAndroid Build Coastguard Worker   FILE* output_file = fopen(output_file_name.c_str(), "wb");
289*d9f75844SAndroid Build Coastguard Worker   ASSERT_TRUE(output_file != NULL);
290*d9f75844SAndroid Build Coastguard Worker 
291*d9f75844SAndroid Build Coastguard Worker   double psnr = 0.0;
292*d9f75844SAndroid Build Coastguard Worker 
293*d9f75844SAndroid Build Coastguard Worker   int stride_y = 0;
294*d9f75844SAndroid Build Coastguard Worker   int stride_uv = 0;
295*d9f75844SAndroid Build Coastguard Worker   Calc16ByteAlignedStride(width_, &stride_y, &stride_uv);
296*d9f75844SAndroid Build Coastguard Worker 
297*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> res_i420_buffer =
298*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Create(width_, height_, stride_y, stride_uv, stride_uv);
299*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<uint8_t[]> out_i420_buffer(new uint8_t[frame_length_]);
300*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kI420, 0,
301*d9f75844SAndroid Build Coastguard Worker                                out_i420_buffer.get()));
302*d9f75844SAndroid Build Coastguard Worker   int y_size = width_ * height_;
303*d9f75844SAndroid Build Coastguard Worker   int u_size = res_i420_buffer->ChromaWidth() * res_i420_buffer->ChromaHeight();
304*d9f75844SAndroid Build Coastguard Worker   int ret = libyuv::I420Copy(
305*d9f75844SAndroid Build Coastguard Worker       out_i420_buffer.get(), width_, out_i420_buffer.get() + y_size,
306*d9f75844SAndroid Build Coastguard Worker       width_ >> 1, out_i420_buffer.get() + y_size + u_size, width_ >> 1,
307*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->MutableDataY(), res_i420_buffer.get()->StrideY(),
308*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->MutableDataU(), res_i420_buffer.get()->StrideU(),
309*d9f75844SAndroid Build Coastguard Worker       res_i420_buffer.get()->MutableDataV(), res_i420_buffer.get()->StrideV(),
310*d9f75844SAndroid Build Coastguard Worker       width_, height_);
311*d9f75844SAndroid Build Coastguard Worker 
312*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(0, ret);
313*d9f75844SAndroid Build Coastguard Worker 
314*d9f75844SAndroid Build Coastguard Worker   if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) {
315*d9f75844SAndroid Build Coastguard Worker     return;
316*d9f75844SAndroid Build Coastguard Worker   }
317*d9f75844SAndroid Build Coastguard Worker   psnr =
318*d9f75844SAndroid Build Coastguard Worker       I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer);
319*d9f75844SAndroid Build Coastguard Worker   EXPECT_EQ(48.0, psnr);
320*d9f75844SAndroid Build Coastguard Worker }
321*d9f75844SAndroid Build Coastguard Worker 
Average(int a,int b,int c,int d)322*d9f75844SAndroid Build Coastguard Worker static uint8_t Average(int a, int b, int c, int d) {
323*d9f75844SAndroid Build Coastguard Worker   return (a + b + c + d + 2) / 4;
324*d9f75844SAndroid Build Coastguard Worker }
325*d9f75844SAndroid Build Coastguard Worker 
TEST_F(TestLibYuv,NV12Scale2x2to2x2)326*d9f75844SAndroid Build Coastguard Worker TEST_F(TestLibYuv, NV12Scale2x2to2x2) {
327*d9f75844SAndroid Build Coastguard Worker   const std::vector<uint8_t> src_y = {0, 1, 2, 3};
328*d9f75844SAndroid Build Coastguard Worker   const std::vector<uint8_t> src_uv = {0, 1};
329*d9f75844SAndroid Build Coastguard Worker   std::vector<uint8_t> dst_y(4);
330*d9f75844SAndroid Build Coastguard Worker   std::vector<uint8_t> dst_uv(2);
331*d9f75844SAndroid Build Coastguard Worker 
332*d9f75844SAndroid Build Coastguard Worker   uint8_t* tmp_buffer = nullptr;
333*d9f75844SAndroid Build Coastguard Worker 
334*d9f75844SAndroid Build Coastguard Worker   NV12Scale(tmp_buffer, src_y.data(), 2, src_uv.data(), 2, 2, 2, dst_y.data(),
335*d9f75844SAndroid Build Coastguard Worker             2, dst_uv.data(), 2, 2, 2);
336*d9f75844SAndroid Build Coastguard Worker 
337*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(dst_y, ::testing::ContainerEq(src_y));
338*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(dst_uv, ::testing::ContainerEq(src_uv));
339*d9f75844SAndroid Build Coastguard Worker }
340*d9f75844SAndroid Build Coastguard Worker 
TEST_F(TestLibYuv,NV12Scale4x4to2x2)341*d9f75844SAndroid Build Coastguard Worker TEST_F(TestLibYuv, NV12Scale4x4to2x2) {
342*d9f75844SAndroid Build Coastguard Worker   const uint8_t src_y[] = {0, 1, 2,  3,  4,  5,  6,  7,
343*d9f75844SAndroid Build Coastguard Worker                            8, 9, 10, 11, 12, 13, 14, 15};
344*d9f75844SAndroid Build Coastguard Worker   const uint8_t src_uv[] = {0, 1, 2, 3, 4, 5, 6, 7};
345*d9f75844SAndroid Build Coastguard Worker   std::vector<uint8_t> dst_y(4);
346*d9f75844SAndroid Build Coastguard Worker   std::vector<uint8_t> dst_uv(2);
347*d9f75844SAndroid Build Coastguard Worker 
348*d9f75844SAndroid Build Coastguard Worker   std::vector<uint8_t> tmp_buffer;
349*d9f75844SAndroid Build Coastguard Worker   const int src_chroma_width = (4 + 1) / 2;
350*d9f75844SAndroid Build Coastguard Worker   const int src_chroma_height = (4 + 1) / 2;
351*d9f75844SAndroid Build Coastguard Worker   const int dst_chroma_width = (2 + 1) / 2;
352*d9f75844SAndroid Build Coastguard Worker   const int dst_chroma_height = (2 + 1) / 2;
353*d9f75844SAndroid Build Coastguard Worker   tmp_buffer.resize(src_chroma_width * src_chroma_height * 2 +
354*d9f75844SAndroid Build Coastguard Worker                     dst_chroma_width * dst_chroma_height * 2);
355*d9f75844SAndroid Build Coastguard Worker   tmp_buffer.shrink_to_fit();
356*d9f75844SAndroid Build Coastguard Worker 
357*d9f75844SAndroid Build Coastguard Worker   NV12Scale(tmp_buffer.data(), src_y, 4, src_uv, 4, 4, 4, dst_y.data(), 2,
358*d9f75844SAndroid Build Coastguard Worker             dst_uv.data(), 2, 2, 2);
359*d9f75844SAndroid Build Coastguard Worker 
360*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(dst_y, ::testing::ElementsAre(
361*d9f75844SAndroid Build Coastguard Worker                          Average(0, 1, 4, 5), Average(2, 3, 6, 7),
362*d9f75844SAndroid Build Coastguard Worker                          Average(8, 9, 12, 13), Average(10, 11, 14, 15)));
363*d9f75844SAndroid Build Coastguard Worker   EXPECT_THAT(dst_uv,
364*d9f75844SAndroid Build Coastguard Worker               ::testing::ElementsAre(Average(0, 2, 4, 6), Average(1, 3, 5, 7)));
365*d9f75844SAndroid Build Coastguard Worker }
366*d9f75844SAndroid Build Coastguard Worker 
TEST(I420WeightedPSNRTest,SmokeTest)367*d9f75844SAndroid Build Coastguard Worker TEST(I420WeightedPSNRTest, SmokeTest) {
368*d9f75844SAndroid Build Coastguard Worker   uint8_t ref_y[] = {0, 0, 0, 0};
369*d9f75844SAndroid Build Coastguard Worker   uint8_t ref_uv[] = {0};
370*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> ref_buffer =
371*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Copy(/*width=*/2, /*height=*/2, ref_y, /*stride_y=*/2, ref_uv,
372*d9f75844SAndroid Build Coastguard Worker                        /*stride_u=*/1, ref_uv, /*stride_v=*/1);
373*d9f75844SAndroid Build Coastguard Worker 
374*d9f75844SAndroid Build Coastguard Worker   uint8_t test_y[] = {1, 1, 1, 1};
375*d9f75844SAndroid Build Coastguard Worker   uint8_t test_uv[] = {2};
376*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> test_buffer = I420Buffer::Copy(
377*d9f75844SAndroid Build Coastguard Worker       /*width=*/2, /*height=*/2, test_y, /*stride_y=*/2, test_uv,
378*d9f75844SAndroid Build Coastguard Worker       /*stride_u=*/1, test_uv, /*stride_v=*/1);
379*d9f75844SAndroid Build Coastguard Worker 
380*d9f75844SAndroid Build Coastguard Worker   auto psnr = [](double mse) { return 10.0 * log10(255.0 * 255.0 / mse); };
381*d9f75844SAndroid Build Coastguard Worker   EXPECT_NEAR(I420WeightedPSNR(*ref_buffer, *test_buffer),
382*d9f75844SAndroid Build Coastguard Worker               (6.0 * psnr(1.0) + psnr(4.0) + psnr(4.0)) / 8.0,
383*d9f75844SAndroid Build Coastguard Worker               /*abs_error=*/0.001);
384*d9f75844SAndroid Build Coastguard Worker }
385*d9f75844SAndroid Build Coastguard Worker 
386*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
387