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