xref: /aosp_15_r20/external/webrtc/common_video/libyuv/webrtc_libyuv.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 "common_video/libyuv/include/webrtc_libyuv.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
14*d9f75844SAndroid Build Coastguard Worker 
15*d9f75844SAndroid Build Coastguard Worker #include "api/video/i420_buffer.h"
16*d9f75844SAndroid Build Coastguard Worker #include "common_video/include/video_frame_buffer.h"
17*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
18*d9f75844SAndroid Build Coastguard Worker #include "third_party/libyuv/include/libyuv.h"
19*d9f75844SAndroid Build Coastguard Worker 
20*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
21*d9f75844SAndroid Build Coastguard Worker 
CalcBufferSize(VideoType type,int width,int height)22*d9f75844SAndroid Build Coastguard Worker size_t CalcBufferSize(VideoType type, int width, int height) {
23*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(width, 0);
24*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(height, 0);
25*d9f75844SAndroid Build Coastguard Worker   size_t buffer_size = 0;
26*d9f75844SAndroid Build Coastguard Worker   switch (type) {
27*d9f75844SAndroid Build Coastguard Worker     case VideoType::kI420:
28*d9f75844SAndroid Build Coastguard Worker     case VideoType::kIYUV:
29*d9f75844SAndroid Build Coastguard Worker     case VideoType::kYV12:
30*d9f75844SAndroid Build Coastguard Worker     case VideoType::kNV12: {
31*d9f75844SAndroid Build Coastguard Worker       int half_width = (width + 1) >> 1;
32*d9f75844SAndroid Build Coastguard Worker       int half_height = (height + 1) >> 1;
33*d9f75844SAndroid Build Coastguard Worker       buffer_size = width * height + half_width * half_height * 2;
34*d9f75844SAndroid Build Coastguard Worker       break;
35*d9f75844SAndroid Build Coastguard Worker     }
36*d9f75844SAndroid Build Coastguard Worker     case VideoType::kRGB565:
37*d9f75844SAndroid Build Coastguard Worker     case VideoType::kYUY2:
38*d9f75844SAndroid Build Coastguard Worker     case VideoType::kUYVY:
39*d9f75844SAndroid Build Coastguard Worker       buffer_size = width * height * 2;
40*d9f75844SAndroid Build Coastguard Worker       break;
41*d9f75844SAndroid Build Coastguard Worker     case VideoType::kRGB24:
42*d9f75844SAndroid Build Coastguard Worker       buffer_size = width * height * 3;
43*d9f75844SAndroid Build Coastguard Worker       break;
44*d9f75844SAndroid Build Coastguard Worker     case VideoType::kBGRA:
45*d9f75844SAndroid Build Coastguard Worker     case VideoType::kARGB:
46*d9f75844SAndroid Build Coastguard Worker       buffer_size = width * height * 4;
47*d9f75844SAndroid Build Coastguard Worker       break;
48*d9f75844SAndroid Build Coastguard Worker     default:
49*d9f75844SAndroid Build Coastguard Worker       RTC_DCHECK_NOTREACHED();
50*d9f75844SAndroid Build Coastguard Worker       break;
51*d9f75844SAndroid Build Coastguard Worker   }
52*d9f75844SAndroid Build Coastguard Worker   return buffer_size;
53*d9f75844SAndroid Build Coastguard Worker }
54*d9f75844SAndroid Build Coastguard Worker 
ExtractBuffer(const rtc::scoped_refptr<I420BufferInterface> & input_frame,size_t size,uint8_t * buffer)55*d9f75844SAndroid Build Coastguard Worker int ExtractBuffer(const rtc::scoped_refptr<I420BufferInterface>& input_frame,
56*d9f75844SAndroid Build Coastguard Worker                   size_t size,
57*d9f75844SAndroid Build Coastguard Worker                   uint8_t* buffer) {
58*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(buffer);
59*d9f75844SAndroid Build Coastguard Worker   if (!input_frame)
60*d9f75844SAndroid Build Coastguard Worker     return -1;
61*d9f75844SAndroid Build Coastguard Worker   int width = input_frame->width();
62*d9f75844SAndroid Build Coastguard Worker   int height = input_frame->height();
63*d9f75844SAndroid Build Coastguard Worker   size_t length = CalcBufferSize(VideoType::kI420, width, height);
64*d9f75844SAndroid Build Coastguard Worker   if (size < length) {
65*d9f75844SAndroid Build Coastguard Worker     return -1;
66*d9f75844SAndroid Build Coastguard Worker   }
67*d9f75844SAndroid Build Coastguard Worker 
68*d9f75844SAndroid Build Coastguard Worker   int chroma_width = input_frame->ChromaWidth();
69*d9f75844SAndroid Build Coastguard Worker   int chroma_height = input_frame->ChromaHeight();
70*d9f75844SAndroid Build Coastguard Worker 
71*d9f75844SAndroid Build Coastguard Worker   libyuv::I420Copy(input_frame->DataY(), input_frame->StrideY(),
72*d9f75844SAndroid Build Coastguard Worker                    input_frame->DataU(), input_frame->StrideU(),
73*d9f75844SAndroid Build Coastguard Worker                    input_frame->DataV(), input_frame->StrideV(), buffer, width,
74*d9f75844SAndroid Build Coastguard Worker                    buffer + width * height, chroma_width,
75*d9f75844SAndroid Build Coastguard Worker                    buffer + width * height + chroma_width * chroma_height,
76*d9f75844SAndroid Build Coastguard Worker                    chroma_width, width, height);
77*d9f75844SAndroid Build Coastguard Worker 
78*d9f75844SAndroid Build Coastguard Worker   return static_cast<int>(length);
79*d9f75844SAndroid Build Coastguard Worker }
80*d9f75844SAndroid Build Coastguard Worker 
ExtractBuffer(const VideoFrame & input_frame,size_t size,uint8_t * buffer)81*d9f75844SAndroid Build Coastguard Worker int ExtractBuffer(const VideoFrame& input_frame, size_t size, uint8_t* buffer) {
82*d9f75844SAndroid Build Coastguard Worker   return ExtractBuffer(input_frame.video_frame_buffer()->ToI420(), size,
83*d9f75844SAndroid Build Coastguard Worker                        buffer);
84*d9f75844SAndroid Build Coastguard Worker }
85*d9f75844SAndroid Build Coastguard Worker 
ConvertVideoType(VideoType video_type)86*d9f75844SAndroid Build Coastguard Worker int ConvertVideoType(VideoType video_type) {
87*d9f75844SAndroid Build Coastguard Worker   switch (video_type) {
88*d9f75844SAndroid Build Coastguard Worker     case VideoType::kUnknown:
89*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_ANY;
90*d9f75844SAndroid Build Coastguard Worker     case VideoType::kI420:
91*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_I420;
92*d9f75844SAndroid Build Coastguard Worker     case VideoType::kIYUV:  // same as VideoType::kYV12
93*d9f75844SAndroid Build Coastguard Worker     case VideoType::kYV12:
94*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_YV12;
95*d9f75844SAndroid Build Coastguard Worker     case VideoType::kRGB24:
96*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_24BG;
97*d9f75844SAndroid Build Coastguard Worker     case VideoType::kRGB565:
98*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_RGBP;
99*d9f75844SAndroid Build Coastguard Worker     case VideoType::kYUY2:
100*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_YUY2;
101*d9f75844SAndroid Build Coastguard Worker     case VideoType::kUYVY:
102*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_UYVY;
103*d9f75844SAndroid Build Coastguard Worker     case VideoType::kMJPEG:
104*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_MJPG;
105*d9f75844SAndroid Build Coastguard Worker     case VideoType::kARGB:
106*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_ARGB;
107*d9f75844SAndroid Build Coastguard Worker     case VideoType::kBGRA:
108*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_BGRA;
109*d9f75844SAndroid Build Coastguard Worker     case VideoType::kNV12:
110*d9f75844SAndroid Build Coastguard Worker       return libyuv::FOURCC_NV12;
111*d9f75844SAndroid Build Coastguard Worker   }
112*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_NOTREACHED();
113*d9f75844SAndroid Build Coastguard Worker   return libyuv::FOURCC_ANY;
114*d9f75844SAndroid Build Coastguard Worker }
115*d9f75844SAndroid Build Coastguard Worker 
ConvertFromI420(const VideoFrame & src_frame,VideoType dst_video_type,int dst_sample_size,uint8_t * dst_frame)116*d9f75844SAndroid Build Coastguard Worker int ConvertFromI420(const VideoFrame& src_frame,
117*d9f75844SAndroid Build Coastguard Worker                     VideoType dst_video_type,
118*d9f75844SAndroid Build Coastguard Worker                     int dst_sample_size,
119*d9f75844SAndroid Build Coastguard Worker                     uint8_t* dst_frame) {
120*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420BufferInterface> i420_buffer =
121*d9f75844SAndroid Build Coastguard Worker       src_frame.video_frame_buffer()->ToI420();
122*d9f75844SAndroid Build Coastguard Worker   return libyuv::ConvertFromI420(
123*d9f75844SAndroid Build Coastguard Worker       i420_buffer->DataY(), i420_buffer->StrideY(), i420_buffer->DataU(),
124*d9f75844SAndroid Build Coastguard Worker       i420_buffer->StrideU(), i420_buffer->DataV(), i420_buffer->StrideV(),
125*d9f75844SAndroid Build Coastguard Worker       dst_frame, dst_sample_size, src_frame.width(), src_frame.height(),
126*d9f75844SAndroid Build Coastguard Worker       ConvertVideoType(dst_video_type));
127*d9f75844SAndroid Build Coastguard Worker }
128*d9f75844SAndroid Build Coastguard Worker 
ScaleI420ABuffer(const I420ABufferInterface & buffer,int target_width,int target_height)129*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420ABufferInterface> ScaleI420ABuffer(
130*d9f75844SAndroid Build Coastguard Worker     const I420ABufferInterface& buffer,
131*d9f75844SAndroid Build Coastguard Worker     int target_width,
132*d9f75844SAndroid Build Coastguard Worker     int target_height) {
133*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> yuv_buffer =
134*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Create(target_width, target_height);
135*d9f75844SAndroid Build Coastguard Worker   yuv_buffer->ScaleFrom(buffer);
136*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> axx_buffer =
137*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Create(target_width, target_height);
138*d9f75844SAndroid Build Coastguard Worker   libyuv::ScalePlane(buffer.DataA(), buffer.StrideA(), buffer.width(),
139*d9f75844SAndroid Build Coastguard Worker                      buffer.height(), axx_buffer->MutableDataY(),
140*d9f75844SAndroid Build Coastguard Worker                      axx_buffer->StrideY(), target_width, target_height,
141*d9f75844SAndroid Build Coastguard Worker                      libyuv::kFilterBox);
142*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420ABufferInterface> merged_buffer = WrapI420ABuffer(
143*d9f75844SAndroid Build Coastguard Worker       yuv_buffer->width(), yuv_buffer->height(), yuv_buffer->DataY(),
144*d9f75844SAndroid Build Coastguard Worker       yuv_buffer->StrideY(), yuv_buffer->DataU(), yuv_buffer->StrideU(),
145*d9f75844SAndroid Build Coastguard Worker       yuv_buffer->DataV(), yuv_buffer->StrideV(), axx_buffer->DataY(),
146*d9f75844SAndroid Build Coastguard Worker       axx_buffer->StrideY(),
147*d9f75844SAndroid Build Coastguard Worker       // To keep references alive.
148*d9f75844SAndroid Build Coastguard Worker       [yuv_buffer, axx_buffer] {});
149*d9f75844SAndroid Build Coastguard Worker   return merged_buffer;
150*d9f75844SAndroid Build Coastguard Worker }
151*d9f75844SAndroid Build Coastguard Worker 
ScaleVideoFrameBuffer(const I420BufferInterface & source,int dst_width,int dst_height)152*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420BufferInterface> ScaleVideoFrameBuffer(
153*d9f75844SAndroid Build Coastguard Worker     const I420BufferInterface& source,
154*d9f75844SAndroid Build Coastguard Worker     int dst_width,
155*d9f75844SAndroid Build Coastguard Worker     int dst_height) {
156*d9f75844SAndroid Build Coastguard Worker   rtc::scoped_refptr<I420Buffer> scaled_buffer =
157*d9f75844SAndroid Build Coastguard Worker       I420Buffer::Create(dst_width, dst_height);
158*d9f75844SAndroid Build Coastguard Worker   scaled_buffer->ScaleFrom(source);
159*d9f75844SAndroid Build Coastguard Worker   return scaled_buffer;
160*d9f75844SAndroid Build Coastguard Worker }
161*d9f75844SAndroid Build Coastguard Worker 
I420SSE(const I420BufferInterface & ref_buffer,const I420BufferInterface & test_buffer)162*d9f75844SAndroid Build Coastguard Worker double I420SSE(const I420BufferInterface& ref_buffer,
163*d9f75844SAndroid Build Coastguard Worker                const I420BufferInterface& test_buffer) {
164*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_EQ(ref_buffer.width(), test_buffer.width());
165*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_EQ(ref_buffer.height(), test_buffer.height());
166*d9f75844SAndroid Build Coastguard Worker   const uint64_t width = test_buffer.width();
167*d9f75844SAndroid Build Coastguard Worker   const uint64_t height = test_buffer.height();
168*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
169*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(),
170*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideY(), width, height);
171*d9f75844SAndroid Build Coastguard Worker   const int width_uv = (width + 1) >> 1;
172*d9f75844SAndroid Build Coastguard Worker   const int height_uv = (height + 1) >> 1;
173*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
174*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(),
175*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideU(), width_uv, height_uv);
176*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
177*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(),
178*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideV(), width_uv, height_uv);
179*d9f75844SAndroid Build Coastguard Worker   const double samples = width * height + 2 * (width_uv * height_uv);
180*d9f75844SAndroid Build Coastguard Worker   const double sse = sse_y + sse_u + sse_v;
181*d9f75844SAndroid Build Coastguard Worker   return sse / (samples * 255.0 * 255.0);
182*d9f75844SAndroid Build Coastguard Worker }
183*d9f75844SAndroid Build Coastguard Worker 
184*d9f75844SAndroid Build Coastguard Worker // Compute PSNR for an I420A frame (all planes). Can upscale test frame.
I420APSNR(const I420ABufferInterface & ref_buffer,const I420ABufferInterface & test_buffer)185*d9f75844SAndroid Build Coastguard Worker double I420APSNR(const I420ABufferInterface& ref_buffer,
186*d9f75844SAndroid Build Coastguard Worker                  const I420ABufferInterface& test_buffer) {
187*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
188*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
189*d9f75844SAndroid Build Coastguard Worker   if ((ref_buffer.width() != test_buffer.width()) ||
190*d9f75844SAndroid Build Coastguard Worker       (ref_buffer.height() != test_buffer.height())) {
191*d9f75844SAndroid Build Coastguard Worker     rtc::scoped_refptr<I420ABufferInterface> scaled_buffer =
192*d9f75844SAndroid Build Coastguard Worker         ScaleI420ABuffer(test_buffer, ref_buffer.width(), ref_buffer.height());
193*d9f75844SAndroid Build Coastguard Worker     return I420APSNR(ref_buffer, *scaled_buffer);
194*d9f75844SAndroid Build Coastguard Worker   }
195*d9f75844SAndroid Build Coastguard Worker   const int width = test_buffer.width();
196*d9f75844SAndroid Build Coastguard Worker   const int height = test_buffer.height();
197*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
198*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(),
199*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideY(), width, height);
200*d9f75844SAndroid Build Coastguard Worker   const int width_uv = (width + 1) >> 1;
201*d9f75844SAndroid Build Coastguard Worker   const int height_uv = (height + 1) >> 1;
202*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
203*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(),
204*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideU(), width_uv, height_uv);
205*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
206*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(),
207*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideV(), width_uv, height_uv);
208*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse_a = libyuv::ComputeSumSquareErrorPlane(
209*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataA(), ref_buffer.StrideA(), test_buffer.DataA(),
210*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideA(), width, height);
211*d9f75844SAndroid Build Coastguard Worker   const uint64_t samples = 2 * (uint64_t)width * (uint64_t)height +
212*d9f75844SAndroid Build Coastguard Worker                            2 * ((uint64_t)width_uv * (uint64_t)height_uv);
213*d9f75844SAndroid Build Coastguard Worker   const uint64_t sse = sse_y + sse_u + sse_v + sse_a;
214*d9f75844SAndroid Build Coastguard Worker   const double psnr = libyuv::SumSquareErrorToPsnr(sse, samples);
215*d9f75844SAndroid Build Coastguard Worker   return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr;
216*d9f75844SAndroid Build Coastguard Worker }
217*d9f75844SAndroid Build Coastguard Worker 
218*d9f75844SAndroid Build Coastguard Worker // Compute PSNR for an I420A frame (all planes)
I420APSNR(const VideoFrame * ref_frame,const VideoFrame * test_frame)219*d9f75844SAndroid Build Coastguard Worker double I420APSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
220*d9f75844SAndroid Build Coastguard Worker   if (!ref_frame || !test_frame)
221*d9f75844SAndroid Build Coastguard Worker     return -1;
222*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(ref_frame->video_frame_buffer()->type() ==
223*d9f75844SAndroid Build Coastguard Worker              VideoFrameBuffer::Type::kI420A);
224*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(test_frame->video_frame_buffer()->type() ==
225*d9f75844SAndroid Build Coastguard Worker              VideoFrameBuffer::Type::kI420A);
226*d9f75844SAndroid Build Coastguard Worker   return I420APSNR(*ref_frame->video_frame_buffer()->GetI420A(),
227*d9f75844SAndroid Build Coastguard Worker                    *test_frame->video_frame_buffer()->GetI420A());
228*d9f75844SAndroid Build Coastguard Worker }
229*d9f75844SAndroid Build Coastguard Worker 
230*d9f75844SAndroid Build Coastguard Worker // Compute PSNR for an I420 frame (all planes). Can upscale test frame.
I420PSNR(const I420BufferInterface & ref_buffer,const I420BufferInterface & test_buffer)231*d9f75844SAndroid Build Coastguard Worker double I420PSNR(const I420BufferInterface& ref_buffer,
232*d9f75844SAndroid Build Coastguard Worker                 const I420BufferInterface& test_buffer) {
233*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
234*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
235*d9f75844SAndroid Build Coastguard Worker   if ((ref_buffer.width() != test_buffer.width()) ||
236*d9f75844SAndroid Build Coastguard Worker       (ref_buffer.height() != test_buffer.height())) {
237*d9f75844SAndroid Build Coastguard Worker     rtc::scoped_refptr<I420Buffer> scaled_buffer =
238*d9f75844SAndroid Build Coastguard Worker         I420Buffer::Create(ref_buffer.width(), ref_buffer.height());
239*d9f75844SAndroid Build Coastguard Worker     scaled_buffer->ScaleFrom(test_buffer);
240*d9f75844SAndroid Build Coastguard Worker     return I420PSNR(ref_buffer, *scaled_buffer);
241*d9f75844SAndroid Build Coastguard Worker   }
242*d9f75844SAndroid Build Coastguard Worker   double psnr = libyuv::I420Psnr(
243*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(),
244*d9f75844SAndroid Build Coastguard Worker       ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(),
245*d9f75844SAndroid Build Coastguard Worker       test_buffer.DataY(), test_buffer.StrideY(), test_buffer.DataU(),
246*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideU(), test_buffer.DataV(), test_buffer.StrideV(),
247*d9f75844SAndroid Build Coastguard Worker       test_buffer.width(), test_buffer.height());
248*d9f75844SAndroid Build Coastguard Worker   // LibYuv sets the max psnr value to 128, we restrict it here.
249*d9f75844SAndroid Build Coastguard Worker   // In case of 0 mse in one frame, 128 can skew the results significantly.
250*d9f75844SAndroid Build Coastguard Worker   return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr;
251*d9f75844SAndroid Build Coastguard Worker }
252*d9f75844SAndroid Build Coastguard Worker 
253*d9f75844SAndroid Build Coastguard Worker // Compute PSNR for an I420 frame (all planes)
I420PSNR(const VideoFrame * ref_frame,const VideoFrame * test_frame)254*d9f75844SAndroid Build Coastguard Worker double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
255*d9f75844SAndroid Build Coastguard Worker   if (!ref_frame || !test_frame)
256*d9f75844SAndroid Build Coastguard Worker     return -1;
257*d9f75844SAndroid Build Coastguard Worker   return I420PSNR(*ref_frame->video_frame_buffer()->ToI420(),
258*d9f75844SAndroid Build Coastguard Worker                   *test_frame->video_frame_buffer()->ToI420());
259*d9f75844SAndroid Build Coastguard Worker }
260*d9f75844SAndroid Build Coastguard Worker 
I420WeightedPSNR(const I420BufferInterface & ref_buffer,const I420BufferInterface & test_buffer)261*d9f75844SAndroid Build Coastguard Worker double I420WeightedPSNR(const I420BufferInterface& ref_buffer,
262*d9f75844SAndroid Build Coastguard Worker                         const I420BufferInterface& test_buffer) {
263*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
264*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
265*d9f75844SAndroid Build Coastguard Worker   if ((ref_buffer.width() != test_buffer.width()) ||
266*d9f75844SAndroid Build Coastguard Worker       (ref_buffer.height() != test_buffer.height())) {
267*d9f75844SAndroid Build Coastguard Worker     rtc::scoped_refptr<I420Buffer> scaled_ref_buffer =
268*d9f75844SAndroid Build Coastguard Worker         I420Buffer::Create(test_buffer.width(), test_buffer.height());
269*d9f75844SAndroid Build Coastguard Worker     scaled_ref_buffer->ScaleFrom(ref_buffer);
270*d9f75844SAndroid Build Coastguard Worker     return I420WeightedPSNR(*scaled_ref_buffer, test_buffer);
271*d9f75844SAndroid Build Coastguard Worker   }
272*d9f75844SAndroid Build Coastguard Worker 
273*d9f75844SAndroid Build Coastguard Worker   // Luma.
274*d9f75844SAndroid Build Coastguard Worker   int width_y = test_buffer.width();
275*d9f75844SAndroid Build Coastguard Worker   int height_y = test_buffer.height();
276*d9f75844SAndroid Build Coastguard Worker   uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane(
277*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(),
278*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideY(), width_y, height_y);
279*d9f75844SAndroid Build Coastguard Worker   uint64_t num_samples_y = (uint64_t)width_y * (uint64_t)height_y;
280*d9f75844SAndroid Build Coastguard Worker   double psnr_y = libyuv::SumSquareErrorToPsnr(sse_y, num_samples_y);
281*d9f75844SAndroid Build Coastguard Worker 
282*d9f75844SAndroid Build Coastguard Worker   // Chroma.
283*d9f75844SAndroid Build Coastguard Worker   int width_uv = (width_y + 1) >> 1;
284*d9f75844SAndroid Build Coastguard Worker   int height_uv = (height_y + 1) >> 1;
285*d9f75844SAndroid Build Coastguard Worker   uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane(
286*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(),
287*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideU(), width_uv, height_uv);
288*d9f75844SAndroid Build Coastguard Worker   uint64_t num_samples_uv = (uint64_t)width_uv * (uint64_t)height_uv;
289*d9f75844SAndroid Build Coastguard Worker   double psnr_u = libyuv::SumSquareErrorToPsnr(sse_u, num_samples_uv);
290*d9f75844SAndroid Build Coastguard Worker   uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane(
291*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(),
292*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideV(), width_uv, height_uv);
293*d9f75844SAndroid Build Coastguard Worker   double psnr_v = libyuv::SumSquareErrorToPsnr(sse_v, num_samples_uv);
294*d9f75844SAndroid Build Coastguard Worker 
295*d9f75844SAndroid Build Coastguard Worker   // Weights from Ohm et. al 2012.
296*d9f75844SAndroid Build Coastguard Worker   double psnr_yuv = (6.0 * psnr_y + psnr_u + psnr_v) / 8.0;
297*d9f75844SAndroid Build Coastguard Worker   return (psnr_yuv > kPerfectPSNR) ? kPerfectPSNR : psnr_yuv;
298*d9f75844SAndroid Build Coastguard Worker }
299*d9f75844SAndroid Build Coastguard Worker 
300*d9f75844SAndroid Build Coastguard Worker // Compute SSIM for an I420A frame (all planes). Can upscale test frame.
I420ASSIM(const I420ABufferInterface & ref_buffer,const I420ABufferInterface & test_buffer)301*d9f75844SAndroid Build Coastguard Worker double I420ASSIM(const I420ABufferInterface& ref_buffer,
302*d9f75844SAndroid Build Coastguard Worker                  const I420ABufferInterface& test_buffer) {
303*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
304*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
305*d9f75844SAndroid Build Coastguard Worker   if ((ref_buffer.width() != test_buffer.width()) ||
306*d9f75844SAndroid Build Coastguard Worker       (ref_buffer.height() != test_buffer.height())) {
307*d9f75844SAndroid Build Coastguard Worker     rtc::scoped_refptr<I420ABufferInterface> scaled_buffer =
308*d9f75844SAndroid Build Coastguard Worker         ScaleI420ABuffer(test_buffer, ref_buffer.width(), ref_buffer.height());
309*d9f75844SAndroid Build Coastguard Worker     return I420ASSIM(ref_buffer, *scaled_buffer);
310*d9f75844SAndroid Build Coastguard Worker   }
311*d9f75844SAndroid Build Coastguard Worker   const double yuv_ssim = libyuv::I420Ssim(
312*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(),
313*d9f75844SAndroid Build Coastguard Worker       ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(),
314*d9f75844SAndroid Build Coastguard Worker       test_buffer.DataY(), test_buffer.StrideY(), test_buffer.DataU(),
315*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideU(), test_buffer.DataV(), test_buffer.StrideV(),
316*d9f75844SAndroid Build Coastguard Worker       test_buffer.width(), test_buffer.height());
317*d9f75844SAndroid Build Coastguard Worker   const double a_ssim = libyuv::CalcFrameSsim(
318*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataA(), ref_buffer.StrideA(), test_buffer.DataA(),
319*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideA(), test_buffer.width(), test_buffer.height());
320*d9f75844SAndroid Build Coastguard Worker   return (yuv_ssim + (a_ssim * 0.8)) / 1.8;
321*d9f75844SAndroid Build Coastguard Worker }
322*d9f75844SAndroid Build Coastguard Worker 
323*d9f75844SAndroid Build Coastguard Worker // Compute SSIM for an I420A frame (all planes)
I420ASSIM(const VideoFrame * ref_frame,const VideoFrame * test_frame)324*d9f75844SAndroid Build Coastguard Worker double I420ASSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
325*d9f75844SAndroid Build Coastguard Worker   if (!ref_frame || !test_frame)
326*d9f75844SAndroid Build Coastguard Worker     return -1;
327*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(ref_frame->video_frame_buffer()->type() ==
328*d9f75844SAndroid Build Coastguard Worker              VideoFrameBuffer::Type::kI420A);
329*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(test_frame->video_frame_buffer()->type() ==
330*d9f75844SAndroid Build Coastguard Worker              VideoFrameBuffer::Type::kI420A);
331*d9f75844SAndroid Build Coastguard Worker   return I420ASSIM(*ref_frame->video_frame_buffer()->GetI420A(),
332*d9f75844SAndroid Build Coastguard Worker                    *test_frame->video_frame_buffer()->GetI420A());
333*d9f75844SAndroid Build Coastguard Worker }
334*d9f75844SAndroid Build Coastguard Worker 
335*d9f75844SAndroid Build Coastguard Worker // Compute SSIM for an I420 frame (all planes). Can upscale test_buffer.
I420SSIM(const I420BufferInterface & ref_buffer,const I420BufferInterface & test_buffer)336*d9f75844SAndroid Build Coastguard Worker double I420SSIM(const I420BufferInterface& ref_buffer,
337*d9f75844SAndroid Build Coastguard Worker                 const I420BufferInterface& test_buffer) {
338*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width());
339*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height());
340*d9f75844SAndroid Build Coastguard Worker   if ((ref_buffer.width() != test_buffer.width()) ||
341*d9f75844SAndroid Build Coastguard Worker       (ref_buffer.height() != test_buffer.height())) {
342*d9f75844SAndroid Build Coastguard Worker     rtc::scoped_refptr<I420Buffer> scaled_buffer =
343*d9f75844SAndroid Build Coastguard Worker         I420Buffer::Create(ref_buffer.width(), ref_buffer.height());
344*d9f75844SAndroid Build Coastguard Worker     scaled_buffer->ScaleFrom(test_buffer);
345*d9f75844SAndroid Build Coastguard Worker     return I420SSIM(ref_buffer, *scaled_buffer);
346*d9f75844SAndroid Build Coastguard Worker   }
347*d9f75844SAndroid Build Coastguard Worker   return libyuv::I420Ssim(
348*d9f75844SAndroid Build Coastguard Worker       ref_buffer.DataY(), ref_buffer.StrideY(), ref_buffer.DataU(),
349*d9f75844SAndroid Build Coastguard Worker       ref_buffer.StrideU(), ref_buffer.DataV(), ref_buffer.StrideV(),
350*d9f75844SAndroid Build Coastguard Worker       test_buffer.DataY(), test_buffer.StrideY(), test_buffer.DataU(),
351*d9f75844SAndroid Build Coastguard Worker       test_buffer.StrideU(), test_buffer.DataV(), test_buffer.StrideV(),
352*d9f75844SAndroid Build Coastguard Worker       test_buffer.width(), test_buffer.height());
353*d9f75844SAndroid Build Coastguard Worker }
354*d9f75844SAndroid Build Coastguard Worker 
I420SSIM(const VideoFrame * ref_frame,const VideoFrame * test_frame)355*d9f75844SAndroid Build Coastguard Worker double I420SSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
356*d9f75844SAndroid Build Coastguard Worker   if (!ref_frame || !test_frame)
357*d9f75844SAndroid Build Coastguard Worker     return -1;
358*d9f75844SAndroid Build Coastguard Worker   return I420SSIM(*ref_frame->video_frame_buffer()->ToI420(),
359*d9f75844SAndroid Build Coastguard Worker                   *test_frame->video_frame_buffer()->ToI420());
360*d9f75844SAndroid Build Coastguard Worker }
361*d9f75844SAndroid Build Coastguard Worker 
NV12Scale(uint8_t * tmp_buffer,const uint8_t * src_y,int src_stride_y,const uint8_t * src_uv,int src_stride_uv,int src_width,int src_height,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_uv,int dst_stride_uv,int dst_width,int dst_height)362*d9f75844SAndroid Build Coastguard Worker void NV12Scale(uint8_t* tmp_buffer,
363*d9f75844SAndroid Build Coastguard Worker                const uint8_t* src_y,
364*d9f75844SAndroid Build Coastguard Worker                int src_stride_y,
365*d9f75844SAndroid Build Coastguard Worker                const uint8_t* src_uv,
366*d9f75844SAndroid Build Coastguard Worker                int src_stride_uv,
367*d9f75844SAndroid Build Coastguard Worker                int src_width,
368*d9f75844SAndroid Build Coastguard Worker                int src_height,
369*d9f75844SAndroid Build Coastguard Worker                uint8_t* dst_y,
370*d9f75844SAndroid Build Coastguard Worker                int dst_stride_y,
371*d9f75844SAndroid Build Coastguard Worker                uint8_t* dst_uv,
372*d9f75844SAndroid Build Coastguard Worker                int dst_stride_uv,
373*d9f75844SAndroid Build Coastguard Worker                int dst_width,
374*d9f75844SAndroid Build Coastguard Worker                int dst_height) {
375*d9f75844SAndroid Build Coastguard Worker   const int src_chroma_width = (src_width + 1) / 2;
376*d9f75844SAndroid Build Coastguard Worker   const int src_chroma_height = (src_height + 1) / 2;
377*d9f75844SAndroid Build Coastguard Worker 
378*d9f75844SAndroid Build Coastguard Worker   if (src_width == dst_width && src_height == dst_height) {
379*d9f75844SAndroid Build Coastguard Worker     // No scaling.
380*d9f75844SAndroid Build Coastguard Worker     libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, src_width,
381*d9f75844SAndroid Build Coastguard Worker                       src_height);
382*d9f75844SAndroid Build Coastguard Worker     libyuv::CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv,
383*d9f75844SAndroid Build Coastguard Worker                       src_chroma_width * 2, src_chroma_height);
384*d9f75844SAndroid Build Coastguard Worker     return;
385*d9f75844SAndroid Build Coastguard Worker   }
386*d9f75844SAndroid Build Coastguard Worker 
387*d9f75844SAndroid Build Coastguard Worker   // Scaling.
388*d9f75844SAndroid Build Coastguard Worker   // Allocate temporary memory for spitting UV planes and scaling them.
389*d9f75844SAndroid Build Coastguard Worker   const int dst_chroma_width = (dst_width + 1) / 2;
390*d9f75844SAndroid Build Coastguard Worker   const int dst_chroma_height = (dst_height + 1) / 2;
391*d9f75844SAndroid Build Coastguard Worker 
392*d9f75844SAndroid Build Coastguard Worker   uint8_t* const src_u = tmp_buffer;
393*d9f75844SAndroid Build Coastguard Worker   uint8_t* const src_v = src_u + src_chroma_width * src_chroma_height;
394*d9f75844SAndroid Build Coastguard Worker   uint8_t* const dst_u = src_v + src_chroma_width * src_chroma_height;
395*d9f75844SAndroid Build Coastguard Worker   uint8_t* const dst_v = dst_u + dst_chroma_width * dst_chroma_height;
396*d9f75844SAndroid Build Coastguard Worker 
397*d9f75844SAndroid Build Coastguard Worker   // Split source UV plane into separate U and V plane using the temporary data.
398*d9f75844SAndroid Build Coastguard Worker   libyuv::SplitUVPlane(src_uv, src_stride_uv, src_u, src_chroma_width, src_v,
399*d9f75844SAndroid Build Coastguard Worker                        src_chroma_width, src_chroma_width, src_chroma_height);
400*d9f75844SAndroid Build Coastguard Worker 
401*d9f75844SAndroid Build Coastguard Worker   // Scale the planes.
402*d9f75844SAndroid Build Coastguard Worker   libyuv::I420Scale(
403*d9f75844SAndroid Build Coastguard Worker       src_y, src_stride_y, src_u, src_chroma_width, src_v, src_chroma_width,
404*d9f75844SAndroid Build Coastguard Worker       src_width, src_height, dst_y, dst_stride_y, dst_u, dst_chroma_width,
405*d9f75844SAndroid Build Coastguard Worker       dst_v, dst_chroma_width, dst_width, dst_height, libyuv::kFilterBox);
406*d9f75844SAndroid Build Coastguard Worker 
407*d9f75844SAndroid Build Coastguard Worker   // Merge the UV planes into the destination.
408*d9f75844SAndroid Build Coastguard Worker   libyuv::MergeUVPlane(dst_u, dst_chroma_width, dst_v, dst_chroma_width, dst_uv,
409*d9f75844SAndroid Build Coastguard Worker                        dst_stride_uv, dst_chroma_width, dst_chroma_height);
410*d9f75844SAndroid Build Coastguard Worker }
411*d9f75844SAndroid Build Coastguard Worker 
412*d9f75844SAndroid Build Coastguard Worker NV12ToI420Scaler::NV12ToI420Scaler() = default;
413*d9f75844SAndroid Build Coastguard Worker NV12ToI420Scaler::~NV12ToI420Scaler() = default;
414*d9f75844SAndroid Build Coastguard Worker 
NV12ToI420Scale(const uint8_t * src_y,int src_stride_y,const uint8_t * src_uv,int src_stride_uv,int src_width,int src_height,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int dst_width,int dst_height)415*d9f75844SAndroid Build Coastguard Worker void NV12ToI420Scaler::NV12ToI420Scale(const uint8_t* src_y,
416*d9f75844SAndroid Build Coastguard Worker                                        int src_stride_y,
417*d9f75844SAndroid Build Coastguard Worker                                        const uint8_t* src_uv,
418*d9f75844SAndroid Build Coastguard Worker                                        int src_stride_uv,
419*d9f75844SAndroid Build Coastguard Worker                                        int src_width,
420*d9f75844SAndroid Build Coastguard Worker                                        int src_height,
421*d9f75844SAndroid Build Coastguard Worker                                        uint8_t* dst_y,
422*d9f75844SAndroid Build Coastguard Worker                                        int dst_stride_y,
423*d9f75844SAndroid Build Coastguard Worker                                        uint8_t* dst_u,
424*d9f75844SAndroid Build Coastguard Worker                                        int dst_stride_u,
425*d9f75844SAndroid Build Coastguard Worker                                        uint8_t* dst_v,
426*d9f75844SAndroid Build Coastguard Worker                                        int dst_stride_v,
427*d9f75844SAndroid Build Coastguard Worker                                        int dst_width,
428*d9f75844SAndroid Build Coastguard Worker                                        int dst_height) {
429*d9f75844SAndroid Build Coastguard Worker   if (src_width == dst_width && src_height == dst_height) {
430*d9f75844SAndroid Build Coastguard Worker     // No scaling.
431*d9f75844SAndroid Build Coastguard Worker     tmp_uv_planes_.clear();
432*d9f75844SAndroid Build Coastguard Worker     tmp_uv_planes_.shrink_to_fit();
433*d9f75844SAndroid Build Coastguard Worker     libyuv::NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y,
434*d9f75844SAndroid Build Coastguard Worker                        dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
435*d9f75844SAndroid Build Coastguard Worker                        src_width, src_height);
436*d9f75844SAndroid Build Coastguard Worker     return;
437*d9f75844SAndroid Build Coastguard Worker   }
438*d9f75844SAndroid Build Coastguard Worker 
439*d9f75844SAndroid Build Coastguard Worker   // Scaling.
440*d9f75844SAndroid Build Coastguard Worker   // Allocate temporary memory for spitting UV planes.
441*d9f75844SAndroid Build Coastguard Worker   const int src_uv_width = (src_width + 1) / 2;
442*d9f75844SAndroid Build Coastguard Worker   const int src_uv_height = (src_height + 1) / 2;
443*d9f75844SAndroid Build Coastguard Worker   tmp_uv_planes_.resize(src_uv_width * src_uv_height * 2);
444*d9f75844SAndroid Build Coastguard Worker   tmp_uv_planes_.shrink_to_fit();
445*d9f75844SAndroid Build Coastguard Worker 
446*d9f75844SAndroid Build Coastguard Worker   // Split source UV plane into separate U and V plane using the temporary data.
447*d9f75844SAndroid Build Coastguard Worker   uint8_t* const src_u = tmp_uv_planes_.data();
448*d9f75844SAndroid Build Coastguard Worker   uint8_t* const src_v = tmp_uv_planes_.data() + src_uv_width * src_uv_height;
449*d9f75844SAndroid Build Coastguard Worker   libyuv::SplitUVPlane(src_uv, src_stride_uv, src_u, src_uv_width, src_v,
450*d9f75844SAndroid Build Coastguard Worker                        src_uv_width, src_uv_width, src_uv_height);
451*d9f75844SAndroid Build Coastguard Worker 
452*d9f75844SAndroid Build Coastguard Worker   // Scale the planes into the destination.
453*d9f75844SAndroid Build Coastguard Worker   libyuv::I420Scale(src_y, src_stride_y, src_u, src_uv_width, src_v,
454*d9f75844SAndroid Build Coastguard Worker                     src_uv_width, src_width, src_height, dst_y, dst_stride_y,
455*d9f75844SAndroid Build Coastguard Worker                     dst_u, dst_stride_u, dst_v, dst_stride_v, dst_width,
456*d9f75844SAndroid Build Coastguard Worker                     dst_height, libyuv::kFilterBox);
457*d9f75844SAndroid Build Coastguard Worker }
458*d9f75844SAndroid Build Coastguard Worker 
459*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
460