OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2004 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 #ifndef WEBRTC_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_ | |
12 #define WEBRTC_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_ | |
13 | |
14 #include <algorithm> | |
15 #include <memory> | |
16 #include <string> | |
17 | |
18 #include "libyuv/convert.h" | |
19 #include "libyuv/convert_from.h" | |
20 #include "libyuv/planar_functions.h" | |
21 #include "libyuv/rotate.h" | |
22 #include "webrtc/base/gunit.h" | |
23 #include "webrtc/base/pathutils.h" | |
24 #include "webrtc/base/stream.h" | |
25 #include "webrtc/base/stringutils.h" | |
26 #include "webrtc/common_video/rotation.h" | |
27 #include "webrtc/media/base/testutils.h" | |
28 #include "webrtc/media/base/videocommon.h" | |
29 #include "webrtc/media/base/videoframe.h" | |
30 #include "webrtc/test/testsupport/fileutils.h" | |
31 | |
32 #if defined(_MSC_VER) | |
33 #define ALIGN16(var) __declspec(align(16)) var | |
34 #else | |
35 #define ALIGN16(var) var __attribute__((aligned(16))) | |
36 #endif | |
37 | |
38 #define kImageFilename "media/faces.1280x720_P420" | |
39 #define kYuvExtension "yuv" | |
40 #define kJpeg420Filename "media/faces_I420" | |
41 #define kJpeg422Filename "media/faces_I422" | |
42 #define kJpeg444Filename "media/faces_I444" | |
43 #define kJpeg411Filename "media/faces_I411" | |
44 #define kJpeg400Filename "media/faces_I400" | |
45 #define kJpegExtension "jpg" | |
46 | |
47 // Generic test class for testing various video frame implementations. | |
48 template <class T> | |
49 class VideoFrameTest : public testing::Test { | |
50 public: | |
51 VideoFrameTest() : repeat_(1) {} | |
52 | |
53 protected: | |
54 static const int kWidth = 1280; | |
55 static const int kHeight = 720; | |
56 static const int kAlignment = 16; | |
57 static const int kMinWidthAll = 1; // Constants for ConstructYUY2AllSizes. | |
58 static const int kMinHeightAll = 1; | |
59 static const int kMaxWidthAll = 17; | |
60 static const int kMaxHeightAll = 23; | |
61 | |
62 // Load a video frame from disk. | |
63 bool LoadFrameNoRepeat(T* frame) { | |
64 int save_repeat = repeat_; // This LoadFrame disables repeat. | |
65 repeat_ = 1; | |
66 bool success = LoadFrame(LoadSample(kImageFilename, kYuvExtension).get(), | |
67 cricket::FOURCC_I420, | |
68 kWidth, kHeight, frame); | |
69 repeat_ = save_repeat; | |
70 return success; | |
71 } | |
72 | |
73 bool LoadFrame(const std::string& filename, | |
74 uint32_t format, | |
75 int32_t width, | |
76 int32_t height, | |
77 T* frame) { | |
78 return LoadFrame(filename, format, width, height, width, abs(height), | |
79 webrtc::kVideoRotation_0, frame); | |
80 } | |
81 bool LoadFrame(const std::string& filename, | |
82 uint32_t format, | |
83 int32_t width, | |
84 int32_t height, | |
85 int dw, | |
86 int dh, | |
87 webrtc::VideoRotation rotation, | |
88 T* frame) { | |
89 std::unique_ptr<rtc::MemoryStream> ms(LoadSample(filename)); | |
90 return LoadFrame(ms.get(), format, width, height, dw, dh, rotation, frame); | |
91 } | |
92 // Load a video frame from a memory stream. | |
93 bool LoadFrame(rtc::MemoryStream* ms, | |
94 uint32_t format, | |
95 int32_t width, | |
96 int32_t height, | |
97 T* frame) { | |
98 return LoadFrame(ms, format, width, height, width, abs(height), | |
99 webrtc::kVideoRotation_0, frame); | |
100 } | |
101 bool LoadFrame(rtc::MemoryStream* ms, | |
102 uint32_t format, | |
103 int32_t width, | |
104 int32_t height, | |
105 int dw, | |
106 int dh, | |
107 webrtc::VideoRotation rotation, | |
108 T* frame) { | |
109 if (!ms) { | |
110 return false; | |
111 } | |
112 size_t data_size; | |
113 bool ret = ms->GetSize(&data_size); | |
114 EXPECT_TRUE(ret); | |
115 if (ret) { | |
116 ret = LoadFrame(reinterpret_cast<uint8_t*>(ms->GetBuffer()), data_size, | |
117 format, width, height, dw, dh, rotation, frame); | |
118 } | |
119 return ret; | |
120 } | |
121 // Load a frame from a raw buffer. | |
122 bool LoadFrame(uint8_t* sample, | |
123 size_t sample_size, | |
124 uint32_t format, | |
125 int32_t width, | |
126 int32_t height, | |
127 T* frame) { | |
128 return LoadFrame(sample, sample_size, format, width, height, width, | |
129 abs(height), webrtc::kVideoRotation_0, frame); | |
130 } | |
131 bool LoadFrame(uint8_t* sample, | |
132 size_t sample_size, | |
133 uint32_t format, | |
134 int32_t width, | |
135 int32_t height, | |
136 int dw, | |
137 int dh, | |
138 webrtc::VideoRotation rotation, | |
139 T* frame) { | |
140 bool ret = false; | |
141 for (int i = 0; i < repeat_; ++i) { | |
142 ret = frame->Init(format, width, height, dw, dh, | |
143 sample, sample_size, 0, rotation); | |
144 } | |
145 return ret; | |
146 } | |
147 | |
148 std::unique_ptr<rtc::MemoryStream> LoadSample(const std::string& filename, | |
149 const std::string& extension) { | |
150 rtc::Pathname path(webrtc::test::ResourcePath(filename, extension)); | |
151 std::unique_ptr<rtc::FileStream> fs( | |
152 rtc::Filesystem::OpenFile(path, "rb")); | |
153 if (!fs.get()) { | |
154 LOG(LS_ERROR) << "Could not open test file path: " << path.pathname() | |
155 << " from current dir " | |
156 << rtc::Filesystem::GetCurrentDirectory().pathname(); | |
157 return NULL; | |
158 } | |
159 | |
160 char buf[4096]; | |
161 std::unique_ptr<rtc::MemoryStream> ms( | |
162 new rtc::MemoryStream()); | |
163 rtc::StreamResult res = Flow(fs.get(), buf, sizeof(buf), ms.get()); | |
164 if (res != rtc::SR_SUCCESS) { | |
165 LOG(LS_ERROR) << "Could not load test file path: " << path.pathname(); | |
166 return NULL; | |
167 } | |
168 | |
169 return ms; | |
170 } | |
171 | |
172 bool DumpSample(const std::string& filename, const void* buffer, int size) { | |
173 rtc::Pathname path(filename); | |
174 std::unique_ptr<rtc::FileStream> fs( | |
175 rtc::Filesystem::OpenFile(path, "wb")); | |
176 if (!fs.get()) { | |
177 return false; | |
178 } | |
179 | |
180 return (fs->Write(buffer, size, NULL, NULL) == rtc::SR_SUCCESS); | |
181 } | |
182 | |
183 // Create a test image in the desired color space. | |
184 // The image is a checkerboard pattern with 63x63 squares, which allows | |
185 // I420 chroma artifacts to easily be seen on the square boundaries. | |
186 // The pattern is { { green, orange }, { blue, purple } } | |
187 // There is also a gradient within each square to ensure that the luma | |
188 // values are handled properly. | |
189 std::unique_ptr<rtc::MemoryStream> CreateYuv422Sample(uint32_t fourcc, | |
190 uint32_t width, | |
191 uint32_t height) { | |
192 int y1_pos, y2_pos, u_pos, v_pos; | |
193 if (!GetYuv422Packing(fourcc, &y1_pos, &y2_pos, &u_pos, &v_pos)) { | |
194 return NULL; | |
195 } | |
196 | |
197 std::unique_ptr<rtc::MemoryStream> ms( | |
198 new rtc::MemoryStream); | |
199 int awidth = (width + 1) & ~1; | |
200 int size = awidth * 2 * height; | |
201 if (!ms->ReserveSize(size)) { | |
202 return NULL; | |
203 } | |
204 for (uint32_t y = 0; y < height; ++y) { | |
205 for (int x = 0; x < awidth; x += 2) { | |
206 uint8_t quad[4]; | |
207 quad[y1_pos] = (x % 63 + y % 63) + 64; | |
208 quad[y2_pos] = ((x + 1) % 63 + y % 63) + 64; | |
209 quad[u_pos] = ((x / 63) & 1) ? 192 : 64; | |
210 quad[v_pos] = ((y / 63) & 1) ? 192 : 64; | |
211 ms->Write(quad, sizeof(quad), NULL, NULL); | |
212 } | |
213 } | |
214 return ms; | |
215 } | |
216 | |
217 // Create a test image for YUV 420 formats with 12 bits per pixel. | |
218 std::unique_ptr<rtc::MemoryStream> CreateYuvSample(uint32_t width, | |
219 uint32_t height, | |
220 uint32_t bpp) { | |
221 std::unique_ptr<rtc::MemoryStream> ms( | |
222 new rtc::MemoryStream); | |
223 if (!ms->ReserveSize(width * height * bpp / 8)) { | |
224 return NULL; | |
225 } | |
226 | |
227 for (uint32_t i = 0; i < width * height * bpp / 8; ++i) { | |
228 uint8_t value = ((i / 63) & 1) ? 192 : 64; | |
229 ms->Write(&value, sizeof(value), NULL, NULL); | |
230 } | |
231 return ms; | |
232 } | |
233 | |
234 std::unique_ptr<rtc::MemoryStream> CreateRgbSample(uint32_t fourcc, | |
235 uint32_t width, | |
236 uint32_t height) { | |
237 int r_pos, g_pos, b_pos, bytes; | |
238 if (!GetRgbPacking(fourcc, &r_pos, &g_pos, &b_pos, &bytes)) { | |
239 return NULL; | |
240 } | |
241 | |
242 std::unique_ptr<rtc::MemoryStream> ms( | |
243 new rtc::MemoryStream); | |
244 if (!ms->ReserveSize(width * height * bytes)) { | |
245 return NULL; | |
246 } | |
247 | |
248 for (uint32_t y = 0; y < height; ++y) { | |
249 for (uint32_t x = 0; x < width; ++x) { | |
250 uint8_t rgb[4] = {255, 255, 255, 255}; | |
251 rgb[r_pos] = ((x / 63) & 1) ? 224 : 32; | |
252 rgb[g_pos] = (x % 63 + y % 63) + 96; | |
253 rgb[b_pos] = ((y / 63) & 1) ? 224 : 32; | |
254 ms->Write(rgb, bytes, NULL, NULL); | |
255 } | |
256 } | |
257 return ms; | |
258 } | |
259 | |
260 // Simple conversion routines to verify the optimized VideoFrame routines. | |
261 // Converts from the specified colorspace to I420. | |
262 std::unique_ptr<T> ConvertYuv422(const rtc::MemoryStream* ms, | |
263 uint32_t fourcc, | |
264 uint32_t width, | |
265 uint32_t height) { | |
266 int y1_pos, y2_pos, u_pos, v_pos; | |
267 if (!GetYuv422Packing(fourcc, &y1_pos, &y2_pos, &u_pos, &v_pos)) { | |
268 return nullptr; | |
269 } | |
270 | |
271 rtc::scoped_refptr<webrtc::I420Buffer> buffer( | |
272 new rtc::RefCountedObject<webrtc::I420Buffer>(width, height)); | |
273 | |
274 buffer->SetToBlack(); | |
275 | |
276 const uint8_t* start = reinterpret_cast<const uint8_t*>(ms->GetBuffer()); | |
277 int awidth = (width + 1) & ~1; | |
278 int stride_y = buffer->StrideY(); | |
279 int stride_u = buffer->StrideU(); | |
280 int stride_v = buffer->StrideV(); | |
281 uint8_t* plane_y = buffer->MutableDataY(); | |
282 uint8_t* plane_u = buffer->MutableDataU(); | |
283 uint8_t* plane_v = buffer->MutableDataV(); | |
284 for (uint32_t y = 0; y < height; ++y) { | |
285 for (uint32_t x = 0; x < width; x += 2) { | |
286 const uint8_t* quad1 = start + (y * awidth + x) * 2; | |
287 plane_y[stride_y * y + x] = quad1[y1_pos]; | |
288 if ((x + 1) < width) { | |
289 plane_y[stride_y * y + x + 1] = quad1[y2_pos]; | |
290 } | |
291 if ((y & 1) == 0) { | |
292 const uint8_t* quad2 = quad1 + awidth * 2; | |
293 if ((y + 1) >= height) { | |
294 quad2 = quad1; | |
295 } | |
296 plane_u[stride_u * (y / 2) + x / 2] = | |
297 (quad1[u_pos] + quad2[u_pos] + 1) / 2; | |
298 plane_v[stride_v * (y / 2) + x / 2] = | |
299 (quad1[v_pos] + quad2[v_pos] + 1) / 2; | |
300 } | |
301 } | |
302 } | |
303 return std::unique_ptr<T>(new T(buffer, 0, webrtc::kVideoRotation_0)); | |
304 } | |
305 | |
306 // Convert RGB to 420. | |
307 // A negative height inverts the image. | |
308 std::unique_ptr<T> ConvertRgb(const rtc::MemoryStream* ms, | |
309 uint32_t fourcc, | |
310 int32_t width, | |
311 int32_t height) { | |
312 int r_pos, g_pos, b_pos, bytes; | |
313 if (!GetRgbPacking(fourcc, &r_pos, &g_pos, &b_pos, &bytes)) { | |
314 return nullptr; | |
315 } | |
316 int pitch = width * bytes; | |
317 const uint8_t* start = reinterpret_cast<const uint8_t*>(ms->GetBuffer()); | |
318 if (height < 0) { | |
319 height = -height; | |
320 start = start + pitch * (height - 1); | |
321 pitch = -pitch; | |
322 } | |
323 rtc::scoped_refptr<webrtc::I420Buffer> buffer( | |
324 new rtc::RefCountedObject<webrtc::I420Buffer>(width, height)); | |
325 | |
326 buffer->SetToBlack(); | |
327 | |
328 int stride_y = buffer->StrideY(); | |
329 int stride_u = buffer->StrideU(); | |
330 int stride_v = buffer->StrideV(); | |
331 uint8_t* plane_y = buffer->MutableDataY(); | |
332 uint8_t* plane_u = buffer->MutableDataU(); | |
333 uint8_t* plane_v = buffer->MutableDataV(); | |
334 for (int32_t y = 0; y < height; y += 2) { | |
335 for (int32_t x = 0; x < width; x += 2) { | |
336 const uint8_t* rgb[4]; | |
337 uint8_t yuv[4][3]; | |
338 rgb[0] = start + y * pitch + x * bytes; | |
339 rgb[1] = rgb[0] + ((x + 1) < width ? bytes : 0); | |
340 rgb[2] = rgb[0] + ((y + 1) < height ? pitch : 0); | |
341 rgb[3] = rgb[2] + ((x + 1) < width ? bytes : 0); | |
342 for (size_t i = 0; i < 4; ++i) { | |
343 ConvertRgbPixel(rgb[i][r_pos], rgb[i][g_pos], rgb[i][b_pos], | |
344 &yuv[i][0], &yuv[i][1], &yuv[i][2]); | |
345 } | |
346 plane_y[stride_y * y + x] = yuv[0][0]; | |
347 if ((x + 1) < width) { | |
348 plane_y[stride_y * y + x + 1] = yuv[1][0]; | |
349 } | |
350 if ((y + 1) < height) { | |
351 plane_y[stride_y * (y + 1) + x] = yuv[2][0]; | |
352 if ((x + 1) < width) { | |
353 plane_y[stride_y * (y + 1) + x + 1] = yuv[3][0]; | |
354 } | |
355 } | |
356 plane_u[stride_u * (y / 2) + x / 2] = | |
357 (yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1] + 2) / 4; | |
358 plane_v[stride_v * (y / 2) + x / 2] = | |
359 (yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2] + 2) / 4; | |
360 } | |
361 } | |
362 return std::unique_ptr<T>(new T(buffer, 0, webrtc::kVideoRotation_0)); | |
363 } | |
364 | |
365 // Simple and slow RGB->YUV conversion. From NTSC standard, c/o Wikipedia. | |
366 void ConvertRgbPixel(uint8_t r, | |
367 uint8_t g, | |
368 uint8_t b, | |
369 uint8_t* y, | |
370 uint8_t* u, | |
371 uint8_t* v) { | |
372 *y = static_cast<int>(.257 * r + .504 * g + .098 * b) + 16; | |
373 *u = static_cast<int>(-.148 * r - .291 * g + .439 * b) + 128; | |
374 *v = static_cast<int>(.439 * r - .368 * g - .071 * b) + 128; | |
375 } | |
376 | |
377 bool GetYuv422Packing(uint32_t fourcc, | |
378 int* y1_pos, | |
379 int* y2_pos, | |
380 int* u_pos, | |
381 int* v_pos) { | |
382 if (fourcc == cricket::FOURCC_YUY2) { | |
383 *y1_pos = 0; *u_pos = 1; *y2_pos = 2; *v_pos = 3; | |
384 } else if (fourcc == cricket::FOURCC_UYVY) { | |
385 *u_pos = 0; *y1_pos = 1; *v_pos = 2; *y2_pos = 3; | |
386 } else { | |
387 return false; | |
388 } | |
389 return true; | |
390 } | |
391 | |
392 bool GetRgbPacking(uint32_t fourcc, | |
393 int* r_pos, | |
394 int* g_pos, | |
395 int* b_pos, | |
396 int* bytes) { | |
397 if (fourcc == cricket::FOURCC_RAW) { | |
398 *r_pos = 0; *g_pos = 1; *b_pos = 2; *bytes = 3; // RGB in memory. | |
399 } else if (fourcc == cricket::FOURCC_24BG) { | |
400 *r_pos = 2; *g_pos = 1; *b_pos = 0; *bytes = 3; // BGR in memory. | |
401 } else if (fourcc == cricket::FOURCC_ABGR) { | |
402 *r_pos = 0; *g_pos = 1; *b_pos = 2; *bytes = 4; // RGBA in memory. | |
403 } else if (fourcc == cricket::FOURCC_BGRA) { | |
404 *r_pos = 1; *g_pos = 2; *b_pos = 3; *bytes = 4; // ARGB in memory. | |
405 } else if (fourcc == cricket::FOURCC_ARGB) { | |
406 *r_pos = 2; *g_pos = 1; *b_pos = 0; *bytes = 4; // BGRA in memory. | |
407 } else { | |
408 return false; | |
409 } | |
410 return true; | |
411 } | |
412 | |
413 // Comparison functions for testing. | |
414 static bool IsNull(const cricket::VideoFrame& frame) { | |
415 return !frame.video_frame_buffer(); | |
416 } | |
417 | |
418 static bool IsSize(const cricket::VideoFrame& frame, | |
419 int width, | |
420 int height) { | |
421 return !IsNull(frame) && frame.video_frame_buffer()->StrideY() >= width && | |
422 frame.video_frame_buffer()->StrideU() >= width / 2 && | |
423 frame.video_frame_buffer()->StrideV() >= width / 2 && | |
424 frame.width() == width && frame.height() == height; | |
425 } | |
426 | |
427 static bool IsPlaneEqual(const std::string& name, | |
428 const uint8_t* plane1, | |
429 uint32_t pitch1, | |
430 const uint8_t* plane2, | |
431 uint32_t pitch2, | |
432 uint32_t width, | |
433 uint32_t height, | |
434 int max_error) { | |
435 const uint8_t* r1 = plane1; | |
436 const uint8_t* r2 = plane2; | |
437 for (uint32_t y = 0; y < height; ++y) { | |
438 for (uint32_t x = 0; x < width; ++x) { | |
439 if (abs(static_cast<int>(r1[x] - r2[x])) > max_error) { | |
440 LOG(LS_INFO) << "IsPlaneEqual(" << name << "): pixel[" | |
441 << x << "," << y << "] differs: " | |
442 << static_cast<int>(r1[x]) << " vs " | |
443 << static_cast<int>(r2[x]); | |
444 return false; | |
445 } | |
446 } | |
447 r1 += pitch1; | |
448 r2 += pitch2; | |
449 } | |
450 return true; | |
451 } | |
452 | |
453 static bool IsEqual(const cricket::VideoFrame& frame, | |
454 int width, | |
455 int height, | |
456 int64_t time_stamp, | |
457 const uint8_t* y, | |
458 uint32_t ypitch, | |
459 const uint8_t* u, | |
460 uint32_t upitch, | |
461 const uint8_t* v, | |
462 uint32_t vpitch, | |
463 int max_error) { | |
464 return IsSize(frame, width, height) && frame.GetTimeStamp() == time_stamp && | |
465 IsPlaneEqual("y", frame.video_frame_buffer()->DataY(), | |
466 frame.video_frame_buffer()->StrideY(), y, ypitch, | |
467 static_cast<uint32_t>(width), | |
468 static_cast<uint32_t>(height), max_error) && | |
469 IsPlaneEqual("u", frame.video_frame_buffer()->DataU(), | |
470 frame.video_frame_buffer()->StrideU(), u, upitch, | |
471 static_cast<uint32_t>((width + 1) / 2), | |
472 static_cast<uint32_t>((height + 1) / 2), max_error) && | |
473 IsPlaneEqual("v", frame.video_frame_buffer()->DataV(), | |
474 frame.video_frame_buffer()->StrideV(), v, vpitch, | |
475 static_cast<uint32_t>((width + 1) / 2), | |
476 static_cast<uint32_t>((height + 1) / 2), max_error); | |
477 } | |
478 | |
479 static bool IsEqual(const cricket::VideoFrame& frame1, | |
480 const cricket::VideoFrame& frame2, | |
481 int max_error) { | |
482 return IsEqual(frame1, | |
483 frame2.width(), frame2.height(), | |
484 frame2.GetTimeStamp(), | |
485 frame2.video_frame_buffer()->DataY(), | |
486 frame2.video_frame_buffer()->StrideY(), | |
487 frame2.video_frame_buffer()->DataU(), | |
488 frame2.video_frame_buffer()->StrideU(), | |
489 frame2.video_frame_buffer()->DataV(), | |
490 frame2.video_frame_buffer()->StrideV(), | |
491 max_error); | |
492 } | |
493 | |
494 static bool IsEqualWithCrop(const cricket::VideoFrame& frame1, | |
495 const cricket::VideoFrame& frame2, | |
496 int hcrop, int vcrop, int max_error) { | |
497 return frame1.width() <= frame2.width() && | |
498 frame1.height() <= frame2.height() && | |
499 IsEqual(frame1, | |
500 frame2.width() - hcrop * 2, | |
501 frame2.height() - vcrop * 2, | |
502 frame2.GetTimeStamp(), | |
503 frame2.video_frame_buffer()->DataY() | |
504 + vcrop * frame2.video_frame_buffer()->StrideY() | |
505 + hcrop, | |
506 frame2.video_frame_buffer()->StrideY(), | |
507 frame2.video_frame_buffer()->DataU() | |
508 + vcrop * frame2.video_frame_buffer()->StrideU() / 2 | |
509 + hcrop / 2, | |
510 frame2.video_frame_buffer()->StrideU(), | |
511 frame2.video_frame_buffer()->DataV() | |
512 + vcrop * frame2.video_frame_buffer()->StrideV() / 2 | |
513 + hcrop / 2, | |
514 frame2.video_frame_buffer()->StrideV(), | |
515 max_error); | |
516 } | |
517 | |
518 static bool IsBlack(const cricket::VideoFrame& frame) { | |
519 return !IsNull(frame) && | |
520 *frame.video_frame_buffer()->DataY() <= 16 && | |
521 *frame.video_frame_buffer()->DataU() == 128 && | |
522 *frame.video_frame_buffer()->DataV() == 128; | |
523 } | |
524 | |
525 //////////////////////// | |
526 // Construction tests // | |
527 //////////////////////// | |
528 | |
529 // Test constructing an image from a I420 buffer. | |
530 void ConstructI420() { | |
531 T frame; | |
532 EXPECT_TRUE(IsNull(frame)); | |
533 std::unique_ptr<rtc::MemoryStream> ms( | |
534 CreateYuvSample(kWidth, kHeight, 12)); | |
535 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, | |
536 kWidth, kHeight, &frame)); | |
537 | |
538 const uint8_t* y = reinterpret_cast<uint8_t*>(ms.get()->GetBuffer()); | |
539 const uint8_t* u = y + kWidth * kHeight; | |
540 const uint8_t* v = u + kWidth * kHeight / 4; | |
541 EXPECT_TRUE(IsEqual(frame, kWidth, kHeight, 0, y, kWidth, u, | |
542 kWidth / 2, v, kWidth / 2, 0)); | |
543 } | |
544 | |
545 // Test constructing an image from a YV12 buffer. | |
546 void ConstructYV12() { | |
547 T frame; | |
548 std::unique_ptr<rtc::MemoryStream> ms( | |
549 CreateYuvSample(kWidth, kHeight, 12)); | |
550 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YV12, | |
551 kWidth, kHeight, &frame)); | |
552 | |
553 const uint8_t* y = reinterpret_cast<uint8_t*>(ms.get()->GetBuffer()); | |
554 const uint8_t* v = y + kWidth * kHeight; | |
555 const uint8_t* u = v + kWidth * kHeight / 4; | |
556 EXPECT_TRUE(IsEqual(frame, kWidth, kHeight, 0, y, kWidth, u, | |
557 kWidth / 2, v, kWidth / 2, 0)); | |
558 } | |
559 | |
560 // Test constructing an image from a I422 buffer. | |
561 void ConstructI422() { | |
562 T frame1, frame2; | |
563 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
564 size_t buf_size = kWidth * kHeight * 2; | |
565 std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment]); | |
566 uint8_t* y = ALIGNP(buf.get(), kAlignment); | |
567 uint8_t* u = y + kWidth * kHeight; | |
568 uint8_t* v = u + (kWidth / 2) * kHeight; | |
569 EXPECT_EQ(0, libyuv::I420ToI422(frame1.video_frame_buffer()->DataY(), | |
570 frame1.video_frame_buffer()->StrideY(), | |
571 frame1.video_frame_buffer()->DataU(), | |
572 frame1.video_frame_buffer()->StrideU(), | |
573 frame1.video_frame_buffer()->DataV(), | |
574 frame1.video_frame_buffer()->StrideV(), | |
575 y, kWidth, | |
576 u, kWidth / 2, | |
577 v, kWidth / 2, | |
578 kWidth, kHeight)); | |
579 EXPECT_TRUE(LoadFrame(y, buf_size, cricket::FOURCC_I422, | |
580 kWidth, kHeight, &frame2)); | |
581 EXPECT_TRUE(IsEqual(frame1, frame2, 1)); | |
582 } | |
583 | |
584 // Test constructing an image from a YUY2 buffer. | |
585 void ConstructYuy2() { | |
586 T frame1, frame2; | |
587 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
588 size_t buf_size = kWidth * kHeight * 2; | |
589 std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment]); | |
590 uint8_t* yuy2 = ALIGNP(buf.get(), kAlignment); | |
591 EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.video_frame_buffer()->DataY(), | |
592 frame1.video_frame_buffer()->StrideY(), | |
593 frame1.video_frame_buffer()->DataU(), | |
594 frame1.video_frame_buffer()->StrideU(), | |
595 frame1.video_frame_buffer()->DataV(), | |
596 frame1.video_frame_buffer()->StrideV(), | |
597 yuy2, kWidth * 2, | |
598 kWidth, kHeight)); | |
599 EXPECT_TRUE(LoadFrame(yuy2, buf_size, cricket::FOURCC_YUY2, | |
600 kWidth, kHeight, &frame2)); | |
601 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
602 } | |
603 | |
604 // Test constructing an image from a YUY2 buffer with buffer unaligned. | |
605 void ConstructYuy2Unaligned() { | |
606 T frame1, frame2; | |
607 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
608 size_t buf_size = kWidth * kHeight * 2; | |
609 std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment + 1]); | |
610 uint8_t* yuy2 = ALIGNP(buf.get(), kAlignment) + 1; | |
611 EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.video_frame_buffer()->DataY(), | |
612 frame1.video_frame_buffer()->StrideY(), | |
613 frame1.video_frame_buffer()->DataU(), | |
614 frame1.video_frame_buffer()->StrideU(), | |
615 frame1.video_frame_buffer()->DataV(), | |
616 frame1.video_frame_buffer()->StrideV(), | |
617 yuy2, kWidth * 2, | |
618 kWidth, kHeight)); | |
619 EXPECT_TRUE(LoadFrame(yuy2, buf_size, cricket::FOURCC_YUY2, | |
620 kWidth, kHeight, &frame2)); | |
621 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
622 } | |
623 | |
624 // Test constructing an image from a wide YUY2 buffer. | |
625 // Normal is 1280x720. Wide is 12800x72 | |
626 void ConstructYuy2Wide() { | |
627 std::unique_ptr<rtc::MemoryStream> ms( | |
628 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth * 10, kHeight / 10)); | |
629 ASSERT_TRUE(ms.get() != NULL); | |
630 std::unique_ptr<T> frame1 = ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, | |
631 kWidth * 10, kHeight / 10); | |
632 ASSERT_TRUE(frame1); | |
633 T frame2; | |
634 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, | |
635 kWidth * 10, kHeight / 10, &frame2)); | |
636 EXPECT_TRUE(IsEqual(*frame1, frame2, 0)); | |
637 } | |
638 | |
639 // Test constructing an image from a UYVY buffer. | |
640 void ConstructUyvy() { | |
641 std::unique_ptr<rtc::MemoryStream> ms( | |
642 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); | |
643 ASSERT_TRUE(ms.get() != NULL); | |
644 std::unique_ptr<T> frame1 = ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, | |
645 kWidth, kHeight); | |
646 T frame2; | |
647 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, | |
648 kWidth, kHeight, &frame2)); | |
649 EXPECT_TRUE(IsEqual(*frame1, frame2, 0)); | |
650 } | |
651 | |
652 // Test constructing an image from a random buffer. | |
653 // We are merely verifying that the code succeeds and is free of crashes. | |
654 void ConstructM420() { | |
655 T frame; | |
656 std::unique_ptr<rtc::MemoryStream> ms( | |
657 CreateYuvSample(kWidth, kHeight, 12)); | |
658 ASSERT_TRUE(ms.get() != NULL); | |
659 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_M420, | |
660 kWidth, kHeight, &frame)); | |
661 } | |
662 | |
663 void ConstructNV21() { | |
664 T frame; | |
665 std::unique_ptr<rtc::MemoryStream> ms( | |
666 CreateYuvSample(kWidth, kHeight, 12)); | |
667 ASSERT_TRUE(ms.get() != NULL); | |
668 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_NV21, | |
669 kWidth, kHeight, &frame)); | |
670 } | |
671 | |
672 void ConstructNV12() { | |
673 T frame; | |
674 std::unique_ptr<rtc::MemoryStream> ms( | |
675 CreateYuvSample(kWidth, kHeight, 12)); | |
676 ASSERT_TRUE(ms.get() != NULL); | |
677 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_NV12, | |
678 kWidth, kHeight, &frame)); | |
679 } | |
680 | |
681 // Test constructing an image from a ABGR buffer | |
682 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. | |
683 void ConstructABGR() { | |
684 std::unique_ptr<rtc::MemoryStream> ms( | |
685 CreateRgbSample(cricket::FOURCC_ABGR, kWidth, kHeight)); | |
686 ASSERT_TRUE(ms.get() != NULL); | |
687 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_ABGR, | |
688 kWidth, kHeight); | |
689 ASSERT_TRUE(frame1); | |
690 T frame2; | |
691 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ABGR, | |
692 kWidth, kHeight, &frame2)); | |
693 EXPECT_TRUE(IsEqual(*frame1, frame2, 2)); | |
694 } | |
695 | |
696 // Test constructing an image from a ARGB buffer | |
697 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. | |
698 void ConstructARGB() { | |
699 std::unique_ptr<rtc::MemoryStream> ms( | |
700 CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight)); | |
701 ASSERT_TRUE(ms.get() != NULL); | |
702 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_ARGB, | |
703 kWidth, kHeight); | |
704 ASSERT_TRUE(frame1); | |
705 T frame2; | |
706 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, | |
707 kWidth, kHeight, &frame2)); | |
708 EXPECT_TRUE(IsEqual(*frame1, frame2, 2)); | |
709 } | |
710 | |
711 // Test constructing an image from a wide ARGB buffer | |
712 // Normal is 1280x720. Wide is 12800x72 | |
713 void ConstructARGBWide() { | |
714 std::unique_ptr<rtc::MemoryStream> ms( | |
715 CreateRgbSample(cricket::FOURCC_ARGB, kWidth * 10, kHeight / 10)); | |
716 ASSERT_TRUE(ms.get() != NULL); | |
717 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_ARGB, | |
718 kWidth * 10, kHeight / 10); | |
719 ASSERT_TRUE(frame1); | |
720 T frame2; | |
721 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, | |
722 kWidth * 10, kHeight / 10, &frame2)); | |
723 EXPECT_TRUE(IsEqual(*frame1, frame2, 2)); | |
724 } | |
725 | |
726 // Test constructing an image from an BGRA buffer. | |
727 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. | |
728 void ConstructBGRA() { | |
729 std::unique_ptr<rtc::MemoryStream> ms( | |
730 CreateRgbSample(cricket::FOURCC_BGRA, kWidth, kHeight)); | |
731 ASSERT_TRUE(ms.get() != NULL); | |
732 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_BGRA, | |
733 kWidth, kHeight); | |
734 ASSERT_TRUE(frame1); | |
735 T frame2; | |
736 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_BGRA, | |
737 kWidth, kHeight, &frame2)); | |
738 EXPECT_TRUE(IsEqual(*frame1, frame2, 2)); | |
739 } | |
740 | |
741 // Test constructing an image from a 24BG buffer. | |
742 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. | |
743 void Construct24BG() { | |
744 std::unique_ptr<rtc::MemoryStream> ms( | |
745 CreateRgbSample(cricket::FOURCC_24BG, kWidth, kHeight)); | |
746 ASSERT_TRUE(ms.get() != NULL); | |
747 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_24BG, | |
748 kWidth, kHeight); | |
749 ASSERT_TRUE(frame1); | |
750 T frame2; | |
751 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_24BG, | |
752 kWidth, kHeight, &frame2)); | |
753 EXPECT_TRUE(IsEqual(*frame1, frame2, 2)); | |
754 } | |
755 | |
756 // Test constructing an image from a raw RGB buffer. | |
757 // Due to rounding, some pixels may differ slightly from the VideoFrame impl. | |
758 void ConstructRaw() { | |
759 std::unique_ptr<rtc::MemoryStream> ms( | |
760 CreateRgbSample(cricket::FOURCC_RAW, kWidth, kHeight)); | |
761 ASSERT_TRUE(ms.get() != NULL); | |
762 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_RAW, | |
763 kWidth, kHeight); | |
764 ASSERT_TRUE(frame1); | |
765 T frame2; | |
766 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_RAW, | |
767 kWidth, kHeight, &frame2)); | |
768 EXPECT_TRUE(IsEqual(*frame1, frame2, 2)); | |
769 } | |
770 | |
771 // Macro to help test different rotations | |
772 #define TEST_MIRROR(FOURCC, BPP) \ | |
773 void Construct##FOURCC##Mirror() { \ | |
774 T frame1, frame2, frame3; \ | |
775 std::unique_ptr<rtc::MemoryStream> ms( \ | |
776 CreateYuvSample(kWidth, kHeight, BPP)); \ | |
777 ASSERT_TRUE(ms.get() != NULL); \ | |
778 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_##FOURCC, kWidth, \ | |
779 -kHeight, kWidth, kHeight, \ | |
780 webrtc::kVideoRotation_180, &frame1)); \ | |
781 size_t data_size; \ | |
782 bool ret = ms->GetSize(&data_size); \ | |
783 EXPECT_TRUE(ret); \ | |
784 EXPECT_TRUE(frame2.Init(cricket::FOURCC_##FOURCC, kWidth, kHeight, kWidth, \ | |
785 kHeight, \ | |
786 reinterpret_cast<uint8_t*>(ms->GetBuffer()), \ | |
787 data_size, 0, webrtc::kVideoRotation_0)); \ | |
788 int width_rotate = frame1.width(); \ | |
789 int height_rotate = frame1.height(); \ | |
790 frame3.InitToEmptyBuffer(width_rotate, height_rotate); \ | |
791 libyuv::I420Mirror(frame2.video_frame_buffer()->DataY(), \ | |
792 frame2.video_frame_buffer()->StrideY(), \ | |
793 frame2.video_frame_buffer()->DataU(), \ | |
794 frame2.video_frame_buffer()->StrideU(), \ | |
795 frame2.video_frame_buffer()->DataV(), \ | |
796 frame2.video_frame_buffer()->StrideV(), \ | |
797 frame3.video_frame_buffer()->MutableDataY(), \ | |
798 frame3.video_frame_buffer()->StrideY(), \ | |
799 frame3.video_frame_buffer()->MutableDataU(), \ | |
800 frame3.video_frame_buffer()->StrideU(), \ | |
801 frame3.video_frame_buffer()->MutableDataV(), \ | |
802 frame3.video_frame_buffer()->StrideV(), kWidth, \ | |
803 kHeight); \ | |
804 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); \ | |
805 } | |
806 | |
807 TEST_MIRROR(I420, 420) | |
808 | |
809 // Macro to help test different rotations | |
810 #define TEST_ROTATE(FOURCC, BPP, ROTATE) \ | |
811 void Construct##FOURCC##Rotate##ROTATE() { \ | |
812 T frame1, frame2, frame3; \ | |
813 std::unique_ptr<rtc::MemoryStream> ms( \ | |
814 CreateYuvSample(kWidth, kHeight, BPP)); \ | |
815 ASSERT_TRUE(ms.get() != NULL); \ | |
816 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_##FOURCC, kWidth, kHeight, \ | |
817 kWidth, kHeight, webrtc::kVideoRotation_##ROTATE, \ | |
818 &frame1)); \ | |
819 size_t data_size; \ | |
820 bool ret = ms->GetSize(&data_size); \ | |
821 EXPECT_TRUE(ret); \ | |
822 EXPECT_TRUE(frame2.Init(cricket::FOURCC_##FOURCC, kWidth, kHeight, kWidth, \ | |
823 kHeight, \ | |
824 reinterpret_cast<uint8_t*>(ms->GetBuffer()), \ | |
825 data_size, 0, webrtc::kVideoRotation_0)); \ | |
826 int width_rotate = frame1.width(); \ | |
827 int height_rotate = frame1.height(); \ | |
828 frame3.InitToEmptyBuffer(width_rotate, height_rotate); \ | |
829 libyuv::I420Rotate(frame2.video_frame_buffer()->DataY(), \ | |
830 frame2.video_frame_buffer()->StrideY(), \ | |
831 frame2.video_frame_buffer()->DataU(), \ | |
832 frame2.video_frame_buffer()->StrideU(), \ | |
833 frame2.video_frame_buffer()->DataV(), \ | |
834 frame2.video_frame_buffer()->StrideV(), \ | |
835 frame3.video_frame_buffer()->MutableDataY(), \ | |
836 frame3.video_frame_buffer()->StrideY(), \ | |
837 frame3.video_frame_buffer()->MutableDataU(), \ | |
838 frame3.video_frame_buffer()->StrideU(), \ | |
839 frame3.video_frame_buffer()->MutableDataV(), \ | |
840 frame3.video_frame_buffer()->StrideV(), kWidth, \ | |
841 kHeight, libyuv::kRotate##ROTATE); \ | |
842 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); \ | |
843 } | |
844 | |
845 // Test constructing an image with rotation. | |
846 TEST_ROTATE(I420, 12, 0) | |
847 TEST_ROTATE(I420, 12, 90) | |
848 TEST_ROTATE(I420, 12, 180) | |
849 TEST_ROTATE(I420, 12, 270) | |
850 TEST_ROTATE(YV12, 12, 0) | |
851 TEST_ROTATE(YV12, 12, 90) | |
852 TEST_ROTATE(YV12, 12, 180) | |
853 TEST_ROTATE(YV12, 12, 270) | |
854 TEST_ROTATE(NV12, 12, 0) | |
855 TEST_ROTATE(NV12, 12, 90) | |
856 TEST_ROTATE(NV12, 12, 180) | |
857 TEST_ROTATE(NV12, 12, 270) | |
858 TEST_ROTATE(NV21, 12, 0) | |
859 TEST_ROTATE(NV21, 12, 90) | |
860 TEST_ROTATE(NV21, 12, 180) | |
861 TEST_ROTATE(NV21, 12, 270) | |
862 TEST_ROTATE(UYVY, 16, 0) | |
863 TEST_ROTATE(UYVY, 16, 90) | |
864 TEST_ROTATE(UYVY, 16, 180) | |
865 TEST_ROTATE(UYVY, 16, 270) | |
866 TEST_ROTATE(YUY2, 16, 0) | |
867 TEST_ROTATE(YUY2, 16, 90) | |
868 TEST_ROTATE(YUY2, 16, 180) | |
869 TEST_ROTATE(YUY2, 16, 270) | |
870 | |
871 // Test constructing an image from a UYVY buffer rotated 90 degrees. | |
872 void ConstructUyvyRotate90() { | |
873 T frame2; | |
874 std::unique_ptr<rtc::MemoryStream> ms( | |
875 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); | |
876 ASSERT_TRUE(ms.get() != NULL); | |
877 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, | |
878 kWidth, kHeight, webrtc::kVideoRotation_90, &frame2)); | |
879 } | |
880 | |
881 // Test constructing an image from a UYVY buffer rotated 180 degrees. | |
882 void ConstructUyvyRotate180() { | |
883 T frame2; | |
884 std::unique_ptr<rtc::MemoryStream> ms( | |
885 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); | |
886 ASSERT_TRUE(ms.get() != NULL); | |
887 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, | |
888 kWidth, kHeight, webrtc::kVideoRotation_180, | |
889 &frame2)); | |
890 } | |
891 | |
892 // Test constructing an image from a UYVY buffer rotated 270 degrees. | |
893 void ConstructUyvyRotate270() { | |
894 T frame2; | |
895 std::unique_ptr<rtc::MemoryStream> ms( | |
896 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); | |
897 ASSERT_TRUE(ms.get() != NULL); | |
898 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, | |
899 kWidth, kHeight, webrtc::kVideoRotation_270, | |
900 &frame2)); | |
901 } | |
902 | |
903 // Test constructing an image from a YUY2 buffer rotated 90 degrees. | |
904 void ConstructYuy2Rotate90() { | |
905 T frame2; | |
906 std::unique_ptr<rtc::MemoryStream> ms( | |
907 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); | |
908 ASSERT_TRUE(ms.get() != NULL); | |
909 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, | |
910 kWidth, kHeight, webrtc::kVideoRotation_90, &frame2)); | |
911 } | |
912 | |
913 // Test constructing an image from a YUY2 buffer rotated 180 degrees. | |
914 void ConstructYuy2Rotate180() { | |
915 T frame2; | |
916 std::unique_ptr<rtc::MemoryStream> ms( | |
917 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); | |
918 ASSERT_TRUE(ms.get() != NULL); | |
919 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, | |
920 kWidth, kHeight, webrtc::kVideoRotation_180, | |
921 &frame2)); | |
922 } | |
923 | |
924 // Test constructing an image from a YUY2 buffer rotated 270 degrees. | |
925 void ConstructYuy2Rotate270() { | |
926 T frame2; | |
927 std::unique_ptr<rtc::MemoryStream> ms( | |
928 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); | |
929 ASSERT_TRUE(ms.get() != NULL); | |
930 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, | |
931 kWidth, kHeight, webrtc::kVideoRotation_270, | |
932 &frame2)); | |
933 } | |
934 | |
935 // Test 1 pixel edge case image I420 buffer. | |
936 void ConstructI4201Pixel() { | |
937 T frame; | |
938 uint8_t pixel[3] = {1, 2, 3}; | |
939 for (int i = 0; i < repeat_; ++i) { | |
940 EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 1, 1, 1, 1, pixel, | |
941 sizeof(pixel), 0, webrtc::kVideoRotation_0)); | |
942 } | |
943 const uint8_t* y = pixel; | |
944 const uint8_t* u = y + 1; | |
945 const uint8_t* v = u + 1; | |
946 EXPECT_TRUE(IsEqual(frame, 1, 1, 0, y, 1, u, 1, v, 1, 0)); | |
947 } | |
948 | |
949 // Test 5 pixel edge case image. | |
950 void ConstructI4205Pixel() { | |
951 T frame; | |
952 uint8_t pixels5x5[5 * 5 + ((5 + 1) / 2 * (5 + 1) / 2) * 2]; | |
953 memset(pixels5x5, 1, 5 * 5 + ((5 + 1) / 2 * (5 + 1) / 2) * 2); | |
954 for (int i = 0; i < repeat_; ++i) { | |
955 EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 5, 5, 5, 5, pixels5x5, | |
956 sizeof(pixels5x5), 0, | |
957 webrtc::kVideoRotation_0)); | |
958 } | |
959 EXPECT_EQ(5, frame.width()); | |
960 EXPECT_EQ(5, frame.height()); | |
961 EXPECT_EQ(5, frame.video_frame_buffer()->StrideY()); | |
962 EXPECT_EQ(3, frame.video_frame_buffer()->StrideU()); | |
963 EXPECT_EQ(3, frame.video_frame_buffer()->StrideV()); | |
964 } | |
965 | |
966 // Test constructing an image from an I420 buffer with horizontal cropping. | |
967 void ConstructI420CropHorizontal() { | |
968 T frame1, frame2; | |
969 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
970 ASSERT_TRUE(LoadFrame(kImageFilename, kYuvExtension, | |
971 cricket::FOURCC_I420, kWidth, kHeight, | |
972 kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0, | |
973 &frame2)); | |
974 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 0)); | |
975 } | |
976 | |
977 // Test constructing an image from a YUY2 buffer with horizontal cropping. | |
978 void ConstructYuy2CropHorizontal() { | |
979 T frame1, frame2; | |
980 std::unique_ptr<rtc::MemoryStream> ms( | |
981 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); | |
982 ASSERT_TRUE(ms.get() != NULL); | |
983 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, | |
984 &frame1)); | |
985 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, | |
986 kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0, | |
987 &frame2)); | |
988 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 0)); | |
989 } | |
990 | |
991 // Test constructing an image from an ARGB buffer with horizontal cropping. | |
992 void ConstructARGBCropHorizontal() { | |
993 std::unique_ptr<rtc::MemoryStream> ms( | |
994 CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight)); | |
995 ASSERT_TRUE(ms.get() != NULL); | |
996 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_ARGB, | |
997 kWidth, kHeight); | |
998 ASSERT_TRUE(frame1); | |
999 T frame2; | |
1000 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight, | |
1001 kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0, | |
1002 &frame2)); | |
1003 EXPECT_TRUE(IsEqualWithCrop(frame2, *frame1, kWidth / 8, 0, 2)); | |
1004 } | |
1005 | |
1006 // Test constructing an image from an I420 buffer, cropping top and bottom. | |
1007 void ConstructI420CropVertical() { | |
1008 T frame1, frame2; | |
1009 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1010 ASSERT_TRUE(LoadFrame(LoadSample(kImageFilename, kYuvExtension).get(), | |
1011 cricket::FOURCC_I420, kWidth, kHeight, | |
1012 kWidth, kHeight * 3 / 4, webrtc::kVideoRotation_0, | |
1013 &frame2)); | |
1014 EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, 0, kHeight / 8, 0)); | |
1015 } | |
1016 | |
1017 // Test constructing an image from I420 synonymous formats. | |
1018 void ConstructI420Aliases() { | |
1019 T frame1, frame2, frame3; | |
1020 ASSERT_TRUE(LoadFrame(LoadSample(kImageFilename, kYuvExtension), | |
1021 cricket::FOURCC_I420, kWidth, kHeight, | |
1022 &frame1)); | |
1023 ASSERT_TRUE(LoadFrame(kImageFilename, kYuvExtension, | |
1024 cricket::FOURCC_IYUV, kWidth, kHeight, | |
1025 &frame2)); | |
1026 ASSERT_TRUE(LoadFrame(kImageFilename, kYuvExtension, | |
1027 cricket::FOURCC_YU12, kWidth, kHeight, | |
1028 &frame3)); | |
1029 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
1030 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); | |
1031 } | |
1032 | |
1033 // Test constructing an image from an I420 MJPG buffer. | |
1034 void ConstructMjpgI420() { | |
1035 T frame1, frame2; | |
1036 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1037 ASSERT_TRUE(LoadFrame(kJpeg420Filename, kJpegExtension, | |
1038 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); | |
1039 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); | |
1040 } | |
1041 | |
1042 // Test constructing an image from an I422 MJPG buffer. | |
1043 void ConstructMjpgI422() { | |
1044 T frame1, frame2; | |
1045 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1046 ASSERT_TRUE(LoadFrame(LoadSample(kJpeg422Filename, kJpegExtension).get(), | |
1047 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); | |
1048 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); | |
1049 } | |
1050 | |
1051 // Test constructing an image from an I444 MJPG buffer. | |
1052 void ConstructMjpgI444() { | |
1053 T frame1, frame2; | |
1054 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1055 ASSERT_TRUE(LoadFrame(LoadSample(kJpeg444Filename, kJpegExtension), | |
1056 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); | |
1057 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); | |
1058 } | |
1059 | |
1060 // Test constructing an image from an I444 MJPG buffer. | |
1061 void ConstructMjpgI411() { | |
1062 T frame1, frame2; | |
1063 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1064 ASSERT_TRUE(LoadFrame(kJpeg411Filename, kJpegExtension, | |
1065 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); | |
1066 EXPECT_TRUE(IsEqual(frame1, frame2, 32)); | |
1067 } | |
1068 | |
1069 // Test constructing an image from an I400 MJPG buffer. | |
1070 // TODO(fbarchard): Stronger compare on chroma. Compare agaisnt a grey image. | |
1071 void ConstructMjpgI400() { | |
1072 T frame1, frame2; | |
1073 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1074 ASSERT_TRUE(LoadFrame(kJpeg400Filename, kJpegExtension, | |
1075 cricket::FOURCC_MJPG, kWidth, kHeight, &frame2)); | |
1076 EXPECT_TRUE(IsPlaneEqual("y", frame1.video_frame_buffer()->DataY(), | |
1077 frame1.video_frame_buffer()->StrideY(), | |
1078 frame2.video_frame_buffer()->DataY(), | |
1079 frame2.video_frame_buffer()->StrideY(), | |
1080 kWidth, kHeight, 32)); | |
1081 EXPECT_TRUE(IsEqual(frame1, frame2, 128)); | |
1082 } | |
1083 | |
1084 // Test constructing an image from an I420 MJPG buffer. | |
1085 void ValidateFrame(const char* name, | |
1086 const char* extension, | |
1087 uint32_t fourcc, | |
1088 int data_adjust, | |
1089 int size_adjust, | |
1090 bool expected_result) { | |
1091 T frame; | |
1092 std::unique_ptr<rtc::MemoryStream> ms(LoadSample(name, extension)); | |
1093 ASSERT_TRUE(ms.get() != NULL); | |
1094 const uint8_t* sample = | |
1095 reinterpret_cast<const uint8_t*>(ms.get()->GetBuffer()); | |
1096 size_t sample_size; | |
1097 ms->GetSize(&sample_size); | |
1098 // Optional adjust size to test invalid size. | |
1099 size_t data_size = sample_size + data_adjust; | |
1100 | |
1101 // Allocate a buffer with end page aligned. | |
1102 const int kPadToHeapSized = 16 * 1024 * 1024; | |
1103 std::unique_ptr<uint8_t[]> page_buffer( | |
1104 new uint8_t[((data_size + kPadToHeapSized + 4095) & ~4095)]); | |
1105 uint8_t* data_ptr = page_buffer.get(); | |
1106 if (!data_ptr) { | |
1107 LOG(LS_WARNING) << "Failed to allocate memory for ValidateFrame test."; | |
1108 EXPECT_FALSE(expected_result); // NULL is okay if failure was expected. | |
1109 return; | |
1110 } | |
1111 data_ptr += kPadToHeapSized + (-(static_cast<int>(data_size)) & 4095); | |
1112 memcpy(data_ptr, sample, std::min(data_size, sample_size)); | |
1113 for (int i = 0; i < repeat_; ++i) { | |
1114 EXPECT_EQ(expected_result, frame.Validate(fourcc, kWidth, kHeight, | |
1115 data_ptr, | |
1116 sample_size + size_adjust)); | |
1117 } | |
1118 } | |
1119 | |
1120 // Test validate for I420 MJPG buffer. | |
1121 void ValidateMjpgI420() { | |
1122 ValidateFrame(kJpeg420Filename, kJpegExtension, | |
1123 cricket::FOURCC_MJPG, 0, 0, true); | |
1124 } | |
1125 | |
1126 // Test validate for I422 MJPG buffer. | |
1127 void ValidateMjpgI422() { | |
1128 ValidateFrame(kJpeg422Filename, kJpegExtension, | |
1129 cricket::FOURCC_MJPG, 0, 0, true); | |
1130 } | |
1131 | |
1132 // Test validate for I444 MJPG buffer. | |
1133 void ValidateMjpgI444() { | |
1134 ValidateFrame(kJpeg444Filename, kJpegExtension, | |
1135 cricket::FOURCC_MJPG, 0, 0, true); | |
1136 } | |
1137 | |
1138 // Test validate for I411 MJPG buffer. | |
1139 void ValidateMjpgI411() { | |
1140 ValidateFrame(kJpeg411Filename, kJpegExtension, | |
1141 cricket::FOURCC_MJPG, 0, 0, true); | |
1142 } | |
1143 | |
1144 // Test validate for I400 MJPG buffer. | |
1145 void ValidateMjpgI400() { | |
1146 ValidateFrame(kJpeg400Filename, kJpegExtension, | |
1147 cricket::FOURCC_MJPG, 0, 0, true); | |
1148 } | |
1149 | |
1150 // Test validate for I420 buffer. | |
1151 void ValidateI420() { | |
1152 ValidateFrame(kImageFilename, kYuvExtension, | |
1153 cricket::FOURCC_I420, 0, 0, true); | |
1154 } | |
1155 | |
1156 // Test validate for I420 buffer where size is too small | |
1157 void ValidateI420SmallSize() { | |
1158 ValidateFrame(kImageFilename, kYuvExtension, | |
1159 cricket::FOURCC_I420, 0, -16384, false); | |
1160 } | |
1161 | |
1162 // Test validate for I420 buffer where size is too large (16 MB) | |
1163 // Will produce warning but pass. | |
1164 void ValidateI420LargeSize() { | |
1165 ValidateFrame(kImageFilename, kYuvExtension, | |
1166 cricket::FOURCC_I420, 16000000, 16000000, | |
1167 true); | |
1168 } | |
1169 | |
1170 // Test validate for I420 buffer where size is 1 GB (not reasonable). | |
1171 void ValidateI420HugeSize() { | |
1172 #ifndef WIN32 // TODO(fbarchard): Reenable when fixing bug 9603762. | |
1173 ValidateFrame(kImageFilename, kYuvExtension, | |
1174 cricket::FOURCC_I420, 1000000000u, | |
1175 1000000000u, false); | |
1176 #endif | |
1177 } | |
1178 | |
1179 // The following test that Validate crashes if the size is greater than the | |
1180 // actual buffer size. | |
1181 // TODO(fbarchard): Consider moving a filter into the capturer/plugin. | |
1182 #if defined(_MSC_VER) && !defined(NDEBUG) | |
1183 int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) { | |
1184 if (code == EXCEPTION_ACCESS_VIOLATION) { | |
1185 LOG(LS_INFO) << "Caught EXCEPTION_ACCESS_VIOLATION as expected."; | |
1186 return EXCEPTION_EXECUTE_HANDLER; | |
1187 } else { | |
1188 LOG(LS_INFO) << "Did not catch EXCEPTION_ACCESS_VIOLATION. Unexpected."; | |
1189 return EXCEPTION_CONTINUE_SEARCH; | |
1190 } | |
1191 } | |
1192 | |
1193 // Test validate fails for truncated MJPG data buffer. If ValidateFrame | |
1194 // crashes the exception handler will return and unittest passes with OK. | |
1195 void ValidateMjpgI420InvalidSize() { | |
1196 __try { | |
1197 ValidateFrame(kJpeg420Filename, kJpegExtension, | |
1198 cricket::FOURCC_MJPG, -16384, 0, false); | |
1199 FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION."; | |
1200 } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { | |
1201 return; // Successfully crashed in ValidateFrame. | |
1202 } | |
1203 } | |
1204 | |
1205 // Test validate fails for truncated I420 buffer. | |
1206 void ValidateI420InvalidSize() { | |
1207 __try { | |
1208 ValidateFrame(kImageFilename, kYuvExtension, | |
1209 cricket::FOURCC_I420, -16384, 0, false); | |
1210 FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION."; | |
1211 } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { | |
1212 return; // Successfully crashed in ValidateFrame. | |
1213 } | |
1214 } | |
1215 #endif | |
1216 | |
1217 // Test constructing an image from a YUY2 buffer (and synonymous formats). | |
1218 void ConstructYuy2Aliases() { | |
1219 T frame1, frame2, frame3, frame4; | |
1220 std::unique_ptr<rtc::MemoryStream> ms( | |
1221 CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight)); | |
1222 ASSERT_TRUE(ms.get() != NULL); | |
1223 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight, | |
1224 &frame1)); | |
1225 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, | |
1226 kWidth, kHeight, &frame2)); | |
1227 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUVS, | |
1228 kWidth, kHeight, &frame3)); | |
1229 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUYV, | |
1230 kWidth, kHeight, &frame4)); | |
1231 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
1232 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); | |
1233 EXPECT_TRUE(IsEqual(frame1, frame4, 0)); | |
1234 } | |
1235 | |
1236 // Test constructing an image from a UYVY buffer (and synonymous formats). | |
1237 void ConstructUyvyAliases() { | |
1238 T frame1, frame2, frame3, frame4; | |
1239 std::unique_ptr<rtc::MemoryStream> ms( | |
1240 CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight)); | |
1241 ASSERT_TRUE(ms.get() != NULL); | |
1242 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight, | |
1243 &frame1)); | |
1244 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, | |
1245 kWidth, kHeight, &frame2)); | |
1246 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_2VUY, | |
1247 kWidth, kHeight, &frame3)); | |
1248 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_HDYC, | |
1249 kWidth, kHeight, &frame4)); | |
1250 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
1251 EXPECT_TRUE(IsEqual(frame1, frame3, 0)); | |
1252 EXPECT_TRUE(IsEqual(frame1, frame4, 0)); | |
1253 } | |
1254 | |
1255 // Test creating a copy. | |
1256 void ConstructCopy() { | |
1257 T frame1, frame2; | |
1258 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1259 for (int i = 0; i < repeat_; ++i) { | |
1260 EXPECT_TRUE(frame2.Init(frame1)); | |
1261 } | |
1262 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
1263 } | |
1264 | |
1265 // Test creating a copy and check that it just increments the refcount. | |
1266 void ConstructCopyIsRef() { | |
1267 T frame1, frame2; | |
1268 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1269 for (int i = 0; i < repeat_; ++i) { | |
1270 EXPECT_TRUE(frame2.Init(frame1)); | |
1271 } | |
1272 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
1273 EXPECT_EQ(frame1.video_frame_buffer(), frame2.video_frame_buffer()); | |
1274 } | |
1275 | |
1276 // Test constructing an image from a YUY2 buffer with a range of sizes. | |
1277 // Only tests that conversion does not crash or corrupt heap. | |
1278 void ConstructYuy2AllSizes() { | |
1279 T frame1, frame2; | |
1280 for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) { | |
1281 for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) { | |
1282 std::unique_ptr<rtc::MemoryStream> ms( | |
1283 CreateYuv422Sample(cricket::FOURCC_YUY2, width, height)); | |
1284 ASSERT_TRUE(ms.get() != NULL); | |
1285 EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, width, height, | |
1286 &frame1)); | |
1287 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, | |
1288 width, height, &frame2)); | |
1289 EXPECT_TRUE(IsEqual(frame1, frame2, 0)); | |
1290 } | |
1291 } | |
1292 } | |
1293 | |
1294 // Test constructing an image from a ARGB buffer with a range of sizes. | |
1295 // Only tests that conversion does not crash or corrupt heap. | |
1296 void ConstructARGBAllSizes() { | |
1297 for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) { | |
1298 for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) { | |
1299 std::unique_ptr<rtc::MemoryStream> ms( | |
1300 CreateRgbSample(cricket::FOURCC_ARGB, width, height)); | |
1301 ASSERT_TRUE(ms.get() != NULL); | |
1302 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_ARGB, | |
1303 width, height); | |
1304 ASSERT_TRUE(frame1); | |
1305 T frame2; | |
1306 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, | |
1307 width, height, &frame2)); | |
1308 EXPECT_TRUE(IsEqual(*frame1, frame2, 64)); | |
1309 } | |
1310 } | |
1311 // Test a practical window size for screencasting usecase. | |
1312 const int kOddWidth = 1228; | |
1313 const int kOddHeight = 260; | |
1314 for (int j = 0; j < 2; ++j) { | |
1315 for (int i = 0; i < 2; ++i) { | |
1316 std::unique_ptr<rtc::MemoryStream> ms( | |
1317 CreateRgbSample(cricket::FOURCC_ARGB, kOddWidth + i, kOddHeight + j)); | |
1318 ASSERT_TRUE(ms.get() != NULL); | |
1319 std::unique_ptr<T> frame1 = ConvertRgb(ms.get(), cricket::FOURCC_ARGB, | |
1320 kOddWidth + i, kOddHeight + j); | |
1321 ASSERT_TRUE(frame1); | |
1322 T frame2; | |
1323 EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, | |
1324 kOddWidth + i, kOddHeight + j, &frame2)); | |
1325 EXPECT_TRUE(IsEqual(*frame1, frame2, 64)); | |
1326 } | |
1327 } | |
1328 } | |
1329 | |
1330 ////////////////////// | |
1331 // Conversion tests // | |
1332 ////////////////////// | |
1333 | |
1334 // Test converting from I420 to I422. | |
1335 void ConvertToI422Buffer() { | |
1336 T frame1, frame2; | |
1337 size_t out_size = kWidth * kHeight * 2; | |
1338 std::unique_ptr<uint8_t[]> buf(new uint8_t[out_size + kAlignment]); | |
1339 uint8_t* y = ALIGNP(buf.get(), kAlignment); | |
1340 uint8_t* u = y + kWidth * kHeight; | |
1341 uint8_t* v = u + (kWidth / 2) * kHeight; | |
1342 ASSERT_TRUE(LoadFrameNoRepeat(&frame1)); | |
1343 for (int i = 0; i < repeat_; ++i) { | |
1344 EXPECT_EQ(0, libyuv::I420ToI422(frame1.video_frame_buffer()->DataY(), | |
1345 frame1.video_frame_buffer()->StrideY(), | |
1346 frame1.video_frame_buffer()->DataU(), | |
1347 frame1.video_frame_buffer()->StrideU(), | |
1348 frame1.video_frame_buffer()->DataV(), | |
1349 frame1.video_frame_buffer()->StrideV(), | |
1350 y, kWidth, | |
1351 u, kWidth / 2, | |
1352 v, kWidth / 2, | |
1353 kWidth, kHeight)); | |
1354 } | |
1355 EXPECT_TRUE(frame2.Init(cricket::FOURCC_I422, kWidth, kHeight, kWidth, | |
1356 kHeight, y, out_size, 1, 1, 0, | |
1357 webrtc::kVideoRotation_0)); | |
1358 EXPECT_TRUE(IsEqual(frame1, frame2, 1)); | |
1359 } | |
1360 | |
1361 int repeat_; | |
1362 }; | |
1363 | |
1364 #endif // WEBRTC_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_ | |
OLD | NEW |