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