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
69size_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
105int 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
126int 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
156int 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
169int 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
178libyuv::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
193int 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
231int 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
262int 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.
278int 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)
295double 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)
323double 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