1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
12 
13 #include <assert.h>
14 #include <string.h>
15 
16 // NOTE(ajm): Path provided by gyp.
17 #include "libyuv.h"  // NOLINT
18 
19 namespace webrtc {
20 
21 const int k16ByteAlignment = 16;
22 
RawVideoTypeToCommonVideoVideoType(RawVideoType type)23 VideoType RawVideoTypeToCommonVideoVideoType(RawVideoType type) {
24   switch (type) {
25     case kVideoI420:
26       return kI420;
27     case kVideoIYUV:
28       return kIYUV;
29     case kVideoRGB24:
30       return kRGB24;
31     case kVideoARGB:
32       return kARGB;
33     case kVideoARGB4444:
34       return kARGB4444;
35     case kVideoRGB565:
36       return kRGB565;
37     case kVideoARGB1555:
38       return kARGB1555;
39     case kVideoYUY2:
40       return kYUY2;
41     case kVideoYV12:
42       return kYV12;
43     case kVideoUYVY:
44       return kUYVY;
45     case kVideoNV21:
46       return kNV21;
47     case kVideoNV12:
48       return kNV12;
49     case kVideoBGRA:
50       return kBGRA;
51     case kVideoMJPEG:
52       return kMJPG;
53     default:
54       assert(false);
55   }
56   return kUnknown;
57 }
58 
AlignInt(int value,int alignment)59 int AlignInt(int value, int alignment) {
60   assert(!((alignment - 1) & alignment));
61   return ((value + alignment - 1) & ~(alignment - 1));
62 }
63 
Calc16ByteAlignedStride(int width,int * stride_y,int * stride_uv)64 void Calc16ByteAlignedStride(int width, int* stride_y, int* stride_uv) {
65   *stride_y = AlignInt(width, k16ByteAlignment);
66   *stride_uv = AlignInt((width + 1) / 2, k16ByteAlignment);
67 }
68 
CalcBufferSize(VideoType type,int width,int height)69 size_t CalcBufferSize(VideoType type, int width, int height) {
70   assert(width >= 0);
71   assert(height >= 0);
72   size_t buffer_size = 0;
73   switch (type) {
74     case kI420:
75     case kNV12:
76     case kNV21:
77     case kIYUV:
78     case kYV12: {
79       int half_width = (width + 1) >> 1;
80       int half_height = (height + 1) >> 1;
81       buffer_size = width * height + half_width * half_height * 2;
82       break;
83     }
84     case kARGB4444:
85     case kRGB565:
86     case kARGB1555:
87     case kYUY2:
88     case kUYVY:
89       buffer_size = width * height * 2;
90       break;
91     case kRGB24:
92       buffer_size = width * height * 3;
93       break;
94     case kBGRA:
95     case kARGB:
96       buffer_size = width * height * 4;
97       break;
98     default:
99       assert(false);
100       break;
101   }
102   return buffer_size;
103 }
104 
PrintVideoFrame(const VideoFrame & frame,FILE * file)105 int PrintVideoFrame(const VideoFrame& frame, FILE* file) {
106   if (file == NULL)
107     return -1;
108   if (frame.IsZeroSize())
109     return -1;
110   for (int planeNum = 0; planeNum < kNumOfPlanes; ++planeNum) {
111     int width = (planeNum ? (frame.width() + 1) / 2 : frame.width());
112     int height = (planeNum ? (frame.height() + 1) / 2 : frame.height());
113     PlaneType plane_type = static_cast<PlaneType>(planeNum);
114     const uint8_t* plane_buffer = frame.buffer(plane_type);
115     for (int y = 0; y < height; y++) {
116      if (fwrite(plane_buffer, 1, width, file) !=
117          static_cast<unsigned int>(width)) {
118        return -1;
119        }
120        plane_buffer += frame.stride(plane_type);
121     }
122   }
123   return 0;
124 }
125 
ExtractBuffer(const VideoFrame & input_frame,size_t size,uint8_t * buffer)126 int ExtractBuffer(const VideoFrame& input_frame, size_t size, uint8_t* buffer) {
127   assert(buffer);
128   if (input_frame.IsZeroSize())
129     return -1;
130   size_t length =
131       CalcBufferSize(kI420, input_frame.width(), input_frame.height());
132   if (size < length) {
133      return -1;
134   }
135 
136   int pos = 0;
137   uint8_t* buffer_ptr = buffer;
138 
139   for (int plane = 0; plane < kNumOfPlanes; ++plane) {
140     int width = (plane ? (input_frame.width() + 1) / 2 :
141       input_frame.width());
142     int height = (plane ? (input_frame.height() + 1) / 2 :
143       input_frame.height());
144     const uint8_t* plane_ptr = input_frame.buffer(
145         static_cast<PlaneType>(plane));
146     for (int y = 0; y < height; y++) {
147       memcpy(&buffer_ptr[pos], plane_ptr, width);
148       pos += width;
149       plane_ptr += input_frame.stride(static_cast<PlaneType>(plane));
150     }
151   }
152   return static_cast<int>(length);
153 }
154 
155 
ConvertNV12ToRGB565(const uint8_t * src_frame,uint8_t * dst_frame,int width,int height)156 int ConvertNV12ToRGB565(const uint8_t* src_frame,
157                         uint8_t* dst_frame,
158                         int width, int height) {
159   int abs_height = (height < 0) ? -height : height;
160   const uint8_t* yplane = src_frame;
161   const uint8_t* uvInterlaced = src_frame + (width * abs_height);
162 
163   return libyuv::NV12ToRGB565(yplane, width,
164                               uvInterlaced, (width + 1) >> 1,
165                               dst_frame, width,
166                               width, height);
167 }
168 
ConvertRGB24ToARGB(const uint8_t * src_frame,uint8_t * dst_frame,int width,int height,int dst_stride)169 int ConvertRGB24ToARGB(const uint8_t* src_frame, uint8_t* dst_frame,
170                        int width, int height, int dst_stride) {
171   if (dst_stride == 0)
172     dst_stride = width;
173   return libyuv::RGB24ToARGB(src_frame, width,
174                              dst_frame, dst_stride,
175                              width, height);
176 }
177 
ConvertRotationMode(VideoRotation rotation)178 libyuv::RotationMode ConvertRotationMode(VideoRotation rotation) {
179   switch (rotation) {
180     case kVideoRotation_0:
181       return libyuv::kRotate0;
182     case kVideoRotation_90:
183       return libyuv::kRotate90;
184     case kVideoRotation_180:
185       return libyuv::kRotate180;
186     case kVideoRotation_270:
187       return libyuv::kRotate270;
188   }
189   assert(false);
190   return libyuv::kRotate0;
191 }
192 
ConvertVideoType(VideoType video_type)193 int ConvertVideoType(VideoType video_type) {
194   switch (video_type) {
195     case kUnknown:
196       return libyuv::FOURCC_ANY;
197     case  kI420:
198       return libyuv::FOURCC_I420;
199     case kIYUV:  // same as KYV12
200     case kYV12:
201       return libyuv::FOURCC_YV12;
202     case kRGB24:
203       return libyuv::FOURCC_24BG;
204     case kABGR:
205       return libyuv::FOURCC_ABGR;
206     case kRGB565:
207       return libyuv::FOURCC_RGBP;
208     case kYUY2:
209       return libyuv::FOURCC_YUY2;
210     case kUYVY:
211       return libyuv::FOURCC_UYVY;
212     case kMJPG:
213       return libyuv::FOURCC_MJPG;
214     case kNV21:
215       return libyuv::FOURCC_NV21;
216     case kNV12:
217       return libyuv::FOURCC_NV12;
218     case kARGB:
219       return libyuv::FOURCC_ARGB;
220     case kBGRA:
221       return libyuv::FOURCC_BGRA;
222     case kARGB4444:
223       return libyuv::FOURCC_R444;
224     case kARGB1555:
225       return libyuv::FOURCC_RGBO;
226   }
227   assert(false);
228   return libyuv::FOURCC_ANY;
229 }
230 
ConvertToI420(VideoType src_video_type,const uint8_t * src_frame,int crop_x,int crop_y,int src_width,int src_height,size_t sample_size,VideoRotation rotation,VideoFrame * dst_frame)231 int ConvertToI420(VideoType src_video_type,
232                   const uint8_t* src_frame,
233                   int crop_x,
234                   int crop_y,
235                   int src_width,
236                   int src_height,
237                   size_t sample_size,
238                   VideoRotation rotation,
239                   VideoFrame* dst_frame) {
240   int dst_width = dst_frame->width();
241   int dst_height = dst_frame->height();
242   // LibYuv expects pre-rotation values for dst.
243   // Stride values should correspond to the destination values.
244   if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) {
245     dst_width = dst_frame->height();
246     dst_height = dst_frame->width();
247   }
248   return libyuv::ConvertToI420(src_frame, sample_size,
249                                dst_frame->buffer(kYPlane),
250                                dst_frame->stride(kYPlane),
251                                dst_frame->buffer(kUPlane),
252                                dst_frame->stride(kUPlane),
253                                dst_frame->buffer(kVPlane),
254                                dst_frame->stride(kVPlane),
255                                crop_x, crop_y,
256                                src_width, src_height,
257                                dst_width, dst_height,
258                                ConvertRotationMode(rotation),
259                                ConvertVideoType(src_video_type));
260 }
261 
ConvertFromI420(const VideoFrame & src_frame,VideoType dst_video_type,int dst_sample_size,uint8_t * dst_frame)262 int ConvertFromI420(const VideoFrame& src_frame,
263                     VideoType dst_video_type,
264                     int dst_sample_size,
265                     uint8_t* dst_frame) {
266   return libyuv::ConvertFromI420(src_frame.buffer(kYPlane),
267                                  src_frame.stride(kYPlane),
268                                  src_frame.buffer(kUPlane),
269                                  src_frame.stride(kUPlane),
270                                  src_frame.buffer(kVPlane),
271                                  src_frame.stride(kVPlane),
272                                  dst_frame, dst_sample_size,
273                                  src_frame.width(), src_frame.height(),
274                                  ConvertVideoType(dst_video_type));
275 }
276 
277 // TODO(mikhal): Create a designated VideoFrame for non I420.
ConvertFromYV12(const VideoFrame & src_frame,VideoType dst_video_type,int dst_sample_size,uint8_t * dst_frame)278 int ConvertFromYV12(const VideoFrame& src_frame,
279                     VideoType dst_video_type,
280                     int dst_sample_size,
281                     uint8_t* dst_frame) {
282   // YV12 = Y, V, U
283   return libyuv::ConvertFromI420(src_frame.buffer(kYPlane),
284                                  src_frame.stride(kYPlane),
285                                  src_frame.buffer(kVPlane),
286                                  src_frame.stride(kVPlane),
287                                  src_frame.buffer(kUPlane),
288                                  src_frame.stride(kUPlane),
289                                  dst_frame, dst_sample_size,
290                                  src_frame.width(), src_frame.height(),
291                                  ConvertVideoType(dst_video_type));
292 }
293 
294 // Compute PSNR for an I420 frame (all planes)
I420PSNR(const VideoFrame * ref_frame,const VideoFrame * test_frame)295 double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
296   if (!ref_frame || !test_frame)
297     return -1;
298   else if ((ref_frame->width() !=  test_frame->width()) ||
299           (ref_frame->height() !=  test_frame->height()))
300     return -1;
301   else if (ref_frame->width() < 0 || ref_frame->height() < 0)
302     return -1;
303 
304   double psnr = libyuv::I420Psnr(ref_frame->buffer(kYPlane),
305                                  ref_frame->stride(kYPlane),
306                                  ref_frame->buffer(kUPlane),
307                                  ref_frame->stride(kUPlane),
308                                  ref_frame->buffer(kVPlane),
309                                  ref_frame->stride(kVPlane),
310                                  test_frame->buffer(kYPlane),
311                                  test_frame->stride(kYPlane),
312                                  test_frame->buffer(kUPlane),
313                                  test_frame->stride(kUPlane),
314                                  test_frame->buffer(kVPlane),
315                                  test_frame->stride(kVPlane),
316                                  test_frame->width(), test_frame->height());
317   // LibYuv sets the max psnr value to 128, we restrict it here.
318   // In case of 0 mse in one frame, 128 can skew the results significantly.
319   return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr;
320 }
321 
322 // Compute SSIM for an I420 frame (all planes)
I420SSIM(const VideoFrame * ref_frame,const VideoFrame * test_frame)323 double I420SSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame) {
324   if (!ref_frame || !test_frame)
325     return -1;
326   else if ((ref_frame->width() !=  test_frame->width()) ||
327           (ref_frame->height() !=  test_frame->height()))
328     return -1;
329   else if (ref_frame->width() < 0 || ref_frame->height()  < 0)
330     return -1;
331 
332   return libyuv::I420Ssim(ref_frame->buffer(kYPlane),
333                           ref_frame->stride(kYPlane),
334                           ref_frame->buffer(kUPlane),
335                           ref_frame->stride(kUPlane),
336                           ref_frame->buffer(kVPlane),
337                           ref_frame->stride(kVPlane),
338                           test_frame->buffer(kYPlane),
339                           test_frame->stride(kYPlane),
340                           test_frame->buffer(kUPlane),
341                           test_frame->stride(kUPlane),
342                           test_frame->buffer(kVPlane),
343                           test_frame->stride(kVPlane),
344                           test_frame->width(), test_frame->height());
345 }
346 }  // namespace webrtc
347