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
19namespace webrtc {
20
21const int k16ByteAlignment = 16;
22
23VideoType 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
59int AlignInt(int value, int alignment) {
60  assert(!((alignment - 1) & alignment));
61  return ((value + alignment - 1) & ~ (alignment - 1));
62}
63
64void 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
69int CalcBufferSize(VideoType type, int width, int height) {
70  int buffer_size = 0;
71  switch (type) {
72    case kI420:
73    case kNV12:
74    case kNV21:
75    case kIYUV:
76    case kYV12: {
77      int half_width = (width + 1) >> 1;
78      int half_height = (height + 1) >> 1;
79      buffer_size = width * height + half_width * half_height * 2;
80      break;
81    }
82    case kARGB4444:
83    case kRGB565:
84    case kARGB1555:
85    case kYUY2:
86    case kUYVY:
87      buffer_size = width * height * 2;
88      break;
89    case kRGB24:
90      buffer_size = width * height * 3;
91      break;
92    case kBGRA:
93    case kARGB:
94      buffer_size = width * height * 4;
95      break;
96    default:
97      assert(false);
98      return -1;
99  }
100  return buffer_size;
101}
102
103int PrintI420VideoFrame(const I420VideoFrame& frame, FILE* file) {
104  if (file == NULL)
105    return -1;
106  if (frame.IsZeroSize())
107    return -1;
108  for (int planeNum = 0; planeNum < kNumOfPlanes; ++planeNum) {
109    int width = (planeNum ? (frame.width() + 1) / 2 : frame.width());
110    int height = (planeNum ? (frame.height() + 1) / 2 : frame.height());
111    PlaneType plane_type = static_cast<PlaneType>(planeNum);
112    const uint8_t* plane_buffer = frame.buffer(plane_type);
113    for (int y = 0; y < height; y++) {
114     if (fwrite(plane_buffer, 1, width, file) !=
115         static_cast<unsigned int>(width)) {
116       return -1;
117       }
118       plane_buffer += frame.stride(plane_type);
119    }
120 }
121 return 0;
122}
123
124int ExtractBuffer(const I420VideoFrame& input_frame,
125                  int size, uint8_t* buffer) {
126  assert(buffer);
127  if (input_frame.IsZeroSize())
128    return -1;
129  int length = CalcBufferSize(kI420, input_frame.width(), input_frame.height());
130  if (size < length) {
131     return -1;
132  }
133
134  int pos = 0;
135  uint8_t* buffer_ptr = buffer;
136
137  for (int plane = 0; plane < kNumOfPlanes; ++plane) {
138    int width = (plane ? (input_frame.width() + 1) / 2 :
139      input_frame.width());
140    int height = (plane ? (input_frame.height() + 1) / 2 :
141      input_frame.height());
142    const uint8_t* plane_ptr = input_frame.buffer(
143        static_cast<PlaneType>(plane));
144    for (int y = 0; y < height; y++) {
145      memcpy(&buffer_ptr[pos], plane_ptr, width);
146      pos += width;
147      plane_ptr += input_frame.stride(static_cast<PlaneType>(plane));
148    }
149  }
150  return length;
151}
152
153
154int ConvertNV12ToRGB565(const uint8_t* src_frame,
155                        uint8_t* dst_frame,
156                        int width, int height) {
157  int abs_height = (height < 0) ? -height : height;
158  const uint8_t* yplane = src_frame;
159  const uint8_t* uvInterlaced = src_frame + (width * abs_height);
160
161  return libyuv::NV12ToRGB565(yplane, width,
162                              uvInterlaced, (width + 1) >> 1,
163                              dst_frame, width,
164                              width, height);
165}
166
167int ConvertRGB24ToARGB(const uint8_t* src_frame, uint8_t* dst_frame,
168                       int width, int height, int dst_stride) {
169  if (dst_stride == 0)
170    dst_stride = width;
171  return libyuv::RGB24ToARGB(src_frame, width,
172                             dst_frame, dst_stride,
173                             width, height);
174}
175
176libyuv::RotationMode ConvertRotationMode(VideoRotationMode rotation) {
177  switch(rotation) {
178    case kRotateNone:
179      return libyuv::kRotate0;
180    case kRotate90:
181      return libyuv::kRotate90;
182    case kRotate180:
183      return libyuv::kRotate180;
184    case kRotate270:
185      return libyuv::kRotate270;
186  }
187  assert(false);
188  return libyuv::kRotate0;
189}
190
191int ConvertVideoType(VideoType video_type) {
192  switch(video_type) {
193    case kUnknown:
194      return libyuv::FOURCC_ANY;
195    case  kI420:
196      return libyuv::FOURCC_I420;
197    case kIYUV:  // same as KYV12
198    case kYV12:
199      return libyuv::FOURCC_YV12;
200    case kRGB24:
201      return libyuv::FOURCC_24BG;
202    case kABGR:
203      return libyuv::FOURCC_ABGR;
204    case kRGB565:
205      return libyuv::FOURCC_RGBP;
206    case kYUY2:
207      return libyuv::FOURCC_YUY2;
208    case kUYVY:
209      return libyuv::FOURCC_UYVY;
210    case kMJPG:
211      return libyuv::FOURCC_MJPG;
212    case kNV21:
213      return libyuv::FOURCC_NV21;
214    case kNV12:
215      return libyuv::FOURCC_NV12;
216    case kARGB:
217      return libyuv::FOURCC_ARGB;
218    case kBGRA:
219      return libyuv::FOURCC_BGRA;
220    case kARGB4444:
221      return libyuv::FOURCC_R444;
222    case kARGB1555:
223      return libyuv::FOURCC_RGBO;
224  }
225  assert(false);
226  return libyuv::FOURCC_ANY;
227}
228
229int ConvertToI420(VideoType src_video_type,
230                  const uint8_t* src_frame,
231                  int crop_x, int crop_y,
232                  int src_width, int src_height,
233                  int sample_size,
234                  VideoRotationMode rotation,
235                  I420VideoFrame* dst_frame) {
236  int dst_width = dst_frame->width();
237  int dst_height = dst_frame->height();
238  // LibYuv expects pre-rotation values for dst.
239  // Stride values should correspond to the destination values.
240  if (rotation == kRotate90 || rotation == kRotate270) {
241    dst_width = dst_frame->height();
242    dst_height =dst_frame->width();
243  }
244  return libyuv::ConvertToI420(src_frame, sample_size,
245                               dst_frame->buffer(kYPlane),
246                               dst_frame->stride(kYPlane),
247                               dst_frame->buffer(kUPlane),
248                               dst_frame->stride(kUPlane),
249                               dst_frame->buffer(kVPlane),
250                               dst_frame->stride(kVPlane),
251                               crop_x, crop_y,
252                               src_width, src_height,
253                               dst_width, dst_height,
254                               ConvertRotationMode(rotation),
255                               ConvertVideoType(src_video_type));
256}
257
258int ConvertFromI420(const I420VideoFrame& src_frame,
259                    VideoType dst_video_type, int dst_sample_size,
260                    uint8_t* dst_frame) {
261  return libyuv::ConvertFromI420(src_frame.buffer(kYPlane),
262                                 src_frame.stride(kYPlane),
263                                 src_frame.buffer(kUPlane),
264                                 src_frame.stride(kUPlane),
265                                 src_frame.buffer(kVPlane),
266                                 src_frame.stride(kVPlane),
267                                 dst_frame, dst_sample_size,
268                                 src_frame.width(), src_frame.height(),
269                                 ConvertVideoType(dst_video_type));
270}
271
272// TODO(mikhal): Create a designated VideoFrame for non I420.
273int ConvertFromYV12(const I420VideoFrame& src_frame,
274                    VideoType dst_video_type, int dst_sample_size,
275                    uint8_t* dst_frame) {
276  // YV12 = Y, V, U
277  return libyuv::ConvertFromI420(src_frame.buffer(kYPlane),
278                                 src_frame.stride(kYPlane),
279                                 src_frame.buffer(kVPlane),
280                                 src_frame.stride(kVPlane),
281                                 src_frame.buffer(kUPlane),
282                                 src_frame.stride(kUPlane),
283                                 dst_frame, dst_sample_size,
284                                 src_frame.width(), src_frame.height(),
285                                 ConvertVideoType(dst_video_type));
286}
287
288int MirrorI420LeftRight(const I420VideoFrame* src_frame,
289                        I420VideoFrame* dst_frame) {
290  // Source and destination frames should have equal resolution.
291  if (src_frame->width() != dst_frame->width() ||
292      src_frame->height() != dst_frame->height())
293    return -1;
294  return libyuv::I420Mirror(src_frame->buffer(kYPlane),
295                            src_frame->stride(kYPlane),
296                            src_frame->buffer(kUPlane),
297                            src_frame->stride(kUPlane),
298                            src_frame->buffer(kVPlane),
299                            src_frame->stride(kVPlane),
300                            dst_frame->buffer(kYPlane),
301                            dst_frame->stride(kYPlane),
302                            dst_frame->buffer(kUPlane),
303                            dst_frame->stride(kUPlane),
304                            dst_frame->buffer(kVPlane),
305                            dst_frame->stride(kVPlane),
306                            src_frame->width(), src_frame->height());
307}
308
309int MirrorI420UpDown(const I420VideoFrame* src_frame,
310                     I420VideoFrame* dst_frame) {
311  // Source and destination frames should have equal resolution
312  if (src_frame->width() != dst_frame->width() ||
313      src_frame->height() != dst_frame->height())
314    return -1;
315
316  // Inserting negative height flips the frame.
317  return libyuv::I420Copy(src_frame->buffer(kYPlane),
318                          src_frame->stride(kYPlane),
319                          src_frame->buffer(kUPlane),
320                          src_frame->stride(kUPlane),
321                          src_frame->buffer(kVPlane),
322                          src_frame->stride(kVPlane),
323                          dst_frame->buffer(kYPlane),
324                          dst_frame->stride(kYPlane),
325                          dst_frame->buffer(kUPlane),
326                          dst_frame->stride(kUPlane),
327                          dst_frame->buffer(kVPlane),
328                          dst_frame->stride(kVPlane),
329                          src_frame->width(), -(src_frame->height()));
330}
331
332// Compute PSNR for an I420 frame (all planes)
333double I420PSNR(const I420VideoFrame* ref_frame,
334                const I420VideoFrame* test_frame) {
335  if (!ref_frame || !test_frame)
336    return -1;
337  else if ((ref_frame->width() !=  test_frame->width()) ||
338          (ref_frame->height() !=  test_frame->height()))
339    return -1;
340  else if (ref_frame->width() < 0 || ref_frame->height() < 0)
341    return -1;
342
343  double psnr = libyuv::I420Psnr(ref_frame->buffer(kYPlane),
344                                 ref_frame->stride(kYPlane),
345                                 ref_frame->buffer(kUPlane),
346                                 ref_frame->stride(kUPlane),
347                                 ref_frame->buffer(kVPlane),
348                                 ref_frame->stride(kVPlane),
349                                 test_frame->buffer(kYPlane),
350                                 test_frame->stride(kYPlane),
351                                 test_frame->buffer(kUPlane),
352                                 test_frame->stride(kUPlane),
353                                 test_frame->buffer(kVPlane),
354                                 test_frame->stride(kVPlane),
355                                 test_frame->width(), test_frame->height());
356  // LibYuv sets the max psnr value to 128, we restrict it here.
357  // In case of 0 mse in one frame, 128 can skew the results significantly.
358  return (psnr > kPerfectPSNR) ? kPerfectPSNR : psnr;
359}
360
361// Compute SSIM for an I420 frame (all planes)
362double I420SSIM(const I420VideoFrame* ref_frame,
363                const I420VideoFrame* test_frame) {
364  if (!ref_frame || !test_frame)
365    return -1;
366  else if ((ref_frame->width() !=  test_frame->width()) ||
367          (ref_frame->height() !=  test_frame->height()))
368    return -1;
369  else if (ref_frame->width() < 0 || ref_frame->height()  < 0)
370    return -1;
371
372  return libyuv::I420Ssim(ref_frame->buffer(kYPlane),
373                          ref_frame->stride(kYPlane),
374                          ref_frame->buffer(kUPlane),
375                          ref_frame->stride(kUPlane),
376                          ref_frame->buffer(kVPlane),
377                          ref_frame->stride(kVPlane),
378                          test_frame->buffer(kYPlane),
379                          test_frame->stride(kYPlane),
380                          test_frame->buffer(kUPlane),
381                          test_frame->stride(kUPlane),
382                          test_frame->buffer(kVPlane),
383                          test_frame->stride(kVPlane),
384                          test_frame->width(), test_frame->height());
385}
386}  // namespace webrtc
387