• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 int 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 
PrintI420VideoFrame(const I420VideoFrame & frame,FILE * file)103 int 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 
ExtractBuffer(const I420VideoFrame & input_frame,int size,uint8_t * buffer)124 int 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 
ConvertNV12ToRGB565(const uint8_t * src_frame,uint8_t * dst_frame,int width,int height)154 int 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 
ConvertRGB24ToARGB(const uint8_t * src_frame,uint8_t * dst_frame,int width,int height,int dst_stride)167 int 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 
ConvertRotationMode(VideoRotationMode rotation)176 libyuv::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 
ConvertVideoType(VideoType video_type)191 int 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 
ConvertToI420(VideoType src_video_type,const uint8_t * src_frame,int crop_x,int crop_y,int src_width,int src_height,int sample_size,VideoRotationMode rotation,I420VideoFrame * dst_frame)229 int 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 
ConvertFromI420(const I420VideoFrame & src_frame,VideoType dst_video_type,int dst_sample_size,uint8_t * dst_frame)258 int 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.
ConvertFromYV12(const I420VideoFrame & src_frame,VideoType dst_video_type,int dst_sample_size,uint8_t * dst_frame)273 int 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 
MirrorI420LeftRight(const I420VideoFrame * src_frame,I420VideoFrame * dst_frame)288 int 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 
MirrorI420UpDown(const I420VideoFrame * src_frame,I420VideoFrame * dst_frame)309 int 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)
I420PSNR(const I420VideoFrame * ref_frame,const I420VideoFrame * test_frame)333 double 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)
I420SSIM(const I420VideoFrame * ref_frame,const I420VideoFrame * test_frame)362 double 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