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