• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2004 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifndef TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_
29 #define TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_
30 
31 #include <algorithm>
32 #include <string>
33 
34 #include "libyuv/convert.h"
35 #include "libyuv/convert_from.h"
36 #include "libyuv/planar_functions.h"
37 #include "libyuv/rotate.h"
38 #include "talk/media/base/testutils.h"
39 #include "talk/media/base/videocommon.h"
40 #include "talk/media/base/videoframe.h"
41 #include "webrtc/base/gunit.h"
42 #include "webrtc/base/pathutils.h"
43 #include "webrtc/base/stream.h"
44 #include "webrtc/base/stringutils.h"
45 #include "webrtc/common_video/rotation.h"
46 
47 #if defined(_MSC_VER)
48 #define ALIGN16(var) __declspec(align(16)) var
49 #else
50 #define ALIGN16(var) var __attribute__((aligned(16)))
51 #endif
52 
53 #define kImageFilename "faces.1280x720_P420.yuv"
54 #define kJpeg420Filename "faces_I420.jpg"
55 #define kJpeg422Filename "faces_I422.jpg"
56 #define kJpeg444Filename "faces_I444.jpg"
57 #define kJpeg411Filename "faces_I411.jpg"
58 #define kJpeg400Filename "faces_I400.jpg"
59 
60 // Generic test class for testing various video frame implementations.
61 template <class T>
62 class VideoFrameTest : public testing::Test {
63  public:
VideoFrameTest()64   VideoFrameTest() : repeat_(1) {}
65 
66  protected:
67   static const int kWidth = 1280;
68   static const int kHeight = 720;
69   static const int kAlignment = 16;
70   static const int kMinWidthAll = 1;  // Constants for ConstructYUY2AllSizes.
71   static const int kMinHeightAll = 1;
72   static const int kMaxWidthAll = 17;
73   static const int kMaxHeightAll = 23;
74 
75   // Load a video frame from disk.
LoadFrameNoRepeat(T * frame)76   bool LoadFrameNoRepeat(T* frame) {
77     int save_repeat = repeat_;  // This LoadFrame disables repeat.
78     repeat_ = 1;
79     bool success = LoadFrame(kImageFilename, cricket::FOURCC_I420,
80                             kWidth, kHeight, frame);
81     repeat_ = save_repeat;
82     return success;
83   }
84 
LoadFrame(const std::string & filename,uint32_t format,int32_t width,int32_t height,T * frame)85   bool LoadFrame(const std::string& filename,
86                  uint32_t format,
87                  int32_t width,
88                  int32_t height,
89                  T* frame) {
90     return LoadFrame(filename, format, width, height, width, abs(height),
91                      webrtc::kVideoRotation_0, frame);
92   }
LoadFrame(const std::string & filename,uint32_t format,int32_t width,int32_t height,int dw,int dh,webrtc::VideoRotation rotation,T * frame)93   bool LoadFrame(const std::string& filename,
94                  uint32_t format,
95                  int32_t width,
96                  int32_t height,
97                  int dw,
98                  int dh,
99                  webrtc::VideoRotation rotation,
100                  T* frame) {
101     rtc::scoped_ptr<rtc::MemoryStream> ms(LoadSample(filename));
102     return LoadFrame(ms.get(), format, width, height, dw, dh, rotation, frame);
103   }
104   // Load a video frame from a memory stream.
LoadFrame(rtc::MemoryStream * ms,uint32_t format,int32_t width,int32_t height,T * frame)105   bool LoadFrame(rtc::MemoryStream* ms,
106                  uint32_t format,
107                  int32_t width,
108                  int32_t height,
109                  T* frame) {
110     return LoadFrame(ms, format, width, height, width, abs(height),
111                      webrtc::kVideoRotation_0, frame);
112   }
LoadFrame(rtc::MemoryStream * ms,uint32_t format,int32_t width,int32_t height,int dw,int dh,webrtc::VideoRotation rotation,T * frame)113   bool LoadFrame(rtc::MemoryStream* ms,
114                  uint32_t format,
115                  int32_t width,
116                  int32_t height,
117                  int dw,
118                  int dh,
119                  webrtc::VideoRotation rotation,
120                  T* frame) {
121     if (!ms) {
122       return false;
123     }
124     size_t data_size;
125     bool ret = ms->GetSize(&data_size);
126     EXPECT_TRUE(ret);
127     if (ret) {
128       ret = LoadFrame(reinterpret_cast<uint8_t*>(ms->GetBuffer()), data_size,
129                       format, width, height, dw, dh, rotation, frame);
130     }
131     return ret;
132   }
133   // Load a frame from a raw buffer.
LoadFrame(uint8_t * sample,size_t sample_size,uint32_t format,int32_t width,int32_t height,T * frame)134   bool LoadFrame(uint8_t* sample,
135                  size_t sample_size,
136                  uint32_t format,
137                  int32_t width,
138                  int32_t height,
139                  T* frame) {
140     return LoadFrame(sample, sample_size, format, width, height, width,
141                      abs(height), webrtc::kVideoRotation_0, frame);
142   }
LoadFrame(uint8_t * sample,size_t sample_size,uint32_t format,int32_t width,int32_t height,int dw,int dh,webrtc::VideoRotation rotation,T * frame)143   bool LoadFrame(uint8_t* sample,
144                  size_t sample_size,
145                  uint32_t format,
146                  int32_t width,
147                  int32_t height,
148                  int dw,
149                  int dh,
150                  webrtc::VideoRotation rotation,
151                  T* frame) {
152     bool ret = false;
153     for (int i = 0; i < repeat_; ++i) {
154       ret = frame->Init(format, width, height, dw, dh,
155                         sample, sample_size, 1, 1, 0, rotation);
156     }
157     return ret;
158   }
159 
LoadSample(const std::string & filename)160   rtc::MemoryStream* LoadSample(const std::string& filename) {
161     rtc::Pathname path(cricket::GetTestFilePath(filename));
162     rtc::scoped_ptr<rtc::FileStream> fs(
163         rtc::Filesystem::OpenFile(path, "rb"));
164     if (!fs.get()) {
165       LOG(LS_ERROR) << "Could not open test file path: " << path.pathname()
166                     << " from current dir "
167                     << rtc::Filesystem::GetCurrentDirectory().pathname();
168       return NULL;
169     }
170 
171     char buf[4096];
172     rtc::scoped_ptr<rtc::MemoryStream> ms(
173         new rtc::MemoryStream());
174     rtc::StreamResult res = Flow(fs.get(), buf, sizeof(buf), ms.get());
175     if (res != rtc::SR_SUCCESS) {
176       LOG(LS_ERROR) << "Could not load test file path: " << path.pathname();
177       return NULL;
178     }
179 
180     return ms.release();
181   }
182 
183   // Write an I420 frame out to disk.
DumpFrame(const std::string & prefix,const cricket::VideoFrame & frame)184   bool DumpFrame(const std::string& prefix,
185                  const cricket::VideoFrame& frame) {
186     char filename[256];
187     rtc::sprintfn(filename, sizeof(filename), "%s.%dx%d_P420.yuv",
188                         prefix.c_str(), frame.GetWidth(), frame.GetHeight());
189     size_t out_size = cricket::VideoFrame::SizeOf(frame.GetWidth(),
190                                                   frame.GetHeight());
191     rtc::scoped_ptr<uint8_t[]> out(new uint8_t[out_size]);
192     frame.CopyToBuffer(out.get(), out_size);
193     return DumpSample(filename, out.get(), out_size);
194   }
195 
DumpSample(const std::string & filename,const void * buffer,int size)196   bool DumpSample(const std::string& filename, const void* buffer, int size) {
197     rtc::Pathname path(filename);
198     rtc::scoped_ptr<rtc::FileStream> fs(
199         rtc::Filesystem::OpenFile(path, "wb"));
200     if (!fs.get()) {
201       return false;
202     }
203 
204     return (fs->Write(buffer, size, NULL, NULL) == rtc::SR_SUCCESS);
205   }
206 
207   // Create a test image in the desired color space.
208   // The image is a checkerboard pattern with 63x63 squares, which allows
209   // I420 chroma artifacts to easily be seen on the square boundaries.
210   // The pattern is { { green, orange }, { blue, purple } }
211   // There is also a gradient within each square to ensure that the luma
212   // values are handled properly.
CreateYuv422Sample(uint32_t fourcc,uint32_t width,uint32_t height)213   rtc::MemoryStream* CreateYuv422Sample(uint32_t fourcc,
214                                         uint32_t width,
215                                         uint32_t height) {
216     int y1_pos, y2_pos, u_pos, v_pos;
217     if (!GetYuv422Packing(fourcc, &y1_pos, &y2_pos, &u_pos, &v_pos)) {
218       return NULL;
219     }
220 
221     rtc::scoped_ptr<rtc::MemoryStream> ms(
222         new rtc::MemoryStream);
223     int awidth = (width + 1) & ~1;
224     int size = awidth * 2 * height;
225     if (!ms->ReserveSize(size)) {
226       return NULL;
227     }
228     for (uint32_t y = 0; y < height; ++y) {
229       for (int x = 0; x < awidth; x += 2) {
230         uint8_t quad[4];
231         quad[y1_pos] = (x % 63 + y % 63) + 64;
232         quad[y2_pos] = ((x + 1) % 63 + y % 63) + 64;
233         quad[u_pos] = ((x / 63) & 1) ? 192 : 64;
234         quad[v_pos] = ((y / 63) & 1) ? 192 : 64;
235         ms->Write(quad, sizeof(quad), NULL, NULL);
236       }
237     }
238     return ms.release();
239   }
240 
241   // Create a test image for YUV 420 formats with 12 bits per pixel.
CreateYuvSample(uint32_t width,uint32_t height,uint32_t bpp)242   rtc::MemoryStream* CreateYuvSample(uint32_t width,
243                                      uint32_t height,
244                                      uint32_t bpp) {
245     rtc::scoped_ptr<rtc::MemoryStream> ms(
246         new rtc::MemoryStream);
247     if (!ms->ReserveSize(width * height * bpp / 8)) {
248       return NULL;
249     }
250 
251     for (uint32_t i = 0; i < width * height * bpp / 8; ++i) {
252       char value = ((i / 63) & 1) ? 192 : 64;
253       ms->Write(&value, sizeof(value), NULL, NULL);
254     }
255     return ms.release();
256   }
257 
CreateRgbSample(uint32_t fourcc,uint32_t width,uint32_t height)258   rtc::MemoryStream* CreateRgbSample(uint32_t fourcc,
259                                      uint32_t width,
260                                      uint32_t height) {
261     int r_pos, g_pos, b_pos, bytes;
262     if (!GetRgbPacking(fourcc, &r_pos, &g_pos, &b_pos, &bytes)) {
263       return NULL;
264     }
265 
266     rtc::scoped_ptr<rtc::MemoryStream> ms(
267         new rtc::MemoryStream);
268     if (!ms->ReserveSize(width * height * bytes)) {
269       return NULL;
270     }
271 
272     for (uint32_t y = 0; y < height; ++y) {
273       for (uint32_t x = 0; x < width; ++x) {
274         uint8_t rgb[4] = {255, 255, 255, 255};
275         rgb[r_pos] = ((x / 63) & 1) ? 224 : 32;
276         rgb[g_pos] = (x % 63 + y % 63) + 96;
277         rgb[b_pos] = ((y / 63) & 1) ? 224 : 32;
278         ms->Write(rgb, bytes, NULL, NULL);
279       }
280     }
281     return ms.release();
282   }
283 
284   // Simple conversion routines to verify the optimized VideoFrame routines.
285   // Converts from the specified colorspace to I420.
ConvertYuv422(const rtc::MemoryStream * ms,uint32_t fourcc,uint32_t width,uint32_t height,T * frame)286   bool ConvertYuv422(const rtc::MemoryStream* ms,
287                      uint32_t fourcc,
288                      uint32_t width,
289                      uint32_t height,
290                      T* frame) {
291     int y1_pos, y2_pos, u_pos, v_pos;
292     if (!GetYuv422Packing(fourcc, &y1_pos, &y2_pos, &u_pos, &v_pos)) {
293       return false;
294     }
295 
296     const uint8_t* start = reinterpret_cast<const uint8_t*>(ms->GetBuffer());
297     int awidth = (width + 1) & ~1;
298     frame->InitToBlack(width, height, 1, 1, 0);
299     int stride_y = frame->GetYPitch();
300     int stride_u = frame->GetUPitch();
301     int stride_v = frame->GetVPitch();
302     for (uint32_t y = 0; y < height; ++y) {
303       for (uint32_t x = 0; x < width; x += 2) {
304         const uint8_t* quad1 = start + (y * awidth + x) * 2;
305         frame->GetYPlane()[stride_y * y + x] = quad1[y1_pos];
306         if ((x + 1) < width) {
307           frame->GetYPlane()[stride_y * y + x + 1] = quad1[y2_pos];
308         }
309         if ((y & 1) == 0) {
310           const uint8_t* quad2 = quad1 + awidth * 2;
311           if ((y + 1) >= height) {
312             quad2 = quad1;
313           }
314           frame->GetUPlane()[stride_u * (y / 2) + x / 2] =
315               (quad1[u_pos] + quad2[u_pos] + 1) / 2;
316           frame->GetVPlane()[stride_v * (y / 2) + x / 2] =
317               (quad1[v_pos] + quad2[v_pos] + 1) / 2;
318         }
319       }
320     }
321     return true;
322   }
323 
324   // Convert RGB to 420.
325   // A negative height inverts the image.
ConvertRgb(const rtc::MemoryStream * ms,uint32_t fourcc,int32_t width,int32_t height,T * frame)326   bool ConvertRgb(const rtc::MemoryStream* ms,
327                   uint32_t fourcc,
328                   int32_t width,
329                   int32_t height,
330                   T* frame) {
331     int r_pos, g_pos, b_pos, bytes;
332     if (!GetRgbPacking(fourcc, &r_pos, &g_pos, &b_pos, &bytes)) {
333       return false;
334     }
335     int pitch = width * bytes;
336     const uint8_t* start = reinterpret_cast<const uint8_t*>(ms->GetBuffer());
337     if (height < 0) {
338       height = -height;
339       start = start + pitch * (height - 1);
340       pitch = -pitch;
341     }
342     frame->InitToBlack(width, height, 1, 1, 0);
343     int stride_y = frame->GetYPitch();
344     int stride_u = frame->GetUPitch();
345     int stride_v = frame->GetVPitch();
346     for (int32_t y = 0; y < height; y += 2) {
347       for (int32_t x = 0; x < width; x += 2) {
348         const uint8_t* rgb[4];
349         uint8_t yuv[4][3];
350         rgb[0] = start + y * pitch + x * bytes;
351         rgb[1] = rgb[0] + ((x + 1) < width ? bytes : 0);
352         rgb[2] = rgb[0] + ((y + 1) < height ? pitch : 0);
353         rgb[3] = rgb[2] + ((x + 1) < width ? bytes : 0);
354         for (size_t i = 0; i < 4; ++i) {
355           ConvertRgbPixel(rgb[i][r_pos], rgb[i][g_pos], rgb[i][b_pos],
356                           &yuv[i][0], &yuv[i][1], &yuv[i][2]);
357         }
358         frame->GetYPlane()[stride_y * y + x] = yuv[0][0];
359         if ((x + 1) < width) {
360           frame->GetYPlane()[stride_y * y + x + 1] = yuv[1][0];
361         }
362         if ((y + 1) < height) {
363           frame->GetYPlane()[stride_y * (y + 1) + x] = yuv[2][0];
364           if ((x + 1) < width) {
365             frame->GetYPlane()[stride_y * (y + 1) + x + 1] = yuv[3][0];
366           }
367         }
368         frame->GetUPlane()[stride_u * (y / 2) + x / 2] =
369             (yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1] + 2) / 4;
370         frame->GetVPlane()[stride_v * (y / 2) + x / 2] =
371             (yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2] + 2) / 4;
372       }
373     }
374     return true;
375   }
376 
377   // Simple and slow RGB->YUV conversion. From NTSC standard, c/o Wikipedia.
ConvertRgbPixel(uint8_t r,uint8_t g,uint8_t b,uint8_t * y,uint8_t * u,uint8_t * v)378   void ConvertRgbPixel(uint8_t r,
379                        uint8_t g,
380                        uint8_t b,
381                        uint8_t* y,
382                        uint8_t* u,
383                        uint8_t* v) {
384     *y = static_cast<int>(.257 * r + .504 * g + .098 * b) + 16;
385     *u = static_cast<int>(-.148 * r - .291 * g + .439 * b) + 128;
386     *v = static_cast<int>(.439 * r - .368 * g - .071 * b) + 128;
387   }
388 
GetYuv422Packing(uint32_t fourcc,int * y1_pos,int * y2_pos,int * u_pos,int * v_pos)389   bool GetYuv422Packing(uint32_t fourcc,
390                         int* y1_pos,
391                         int* y2_pos,
392                         int* u_pos,
393                         int* v_pos) {
394     if (fourcc == cricket::FOURCC_YUY2) {
395       *y1_pos = 0; *u_pos = 1; *y2_pos = 2; *v_pos = 3;
396     } else if (fourcc == cricket::FOURCC_UYVY) {
397       *u_pos = 0; *y1_pos = 1; *v_pos = 2; *y2_pos = 3;
398     } else {
399       return false;
400     }
401     return true;
402   }
403 
GetRgbPacking(uint32_t fourcc,int * r_pos,int * g_pos,int * b_pos,int * bytes)404   bool GetRgbPacking(uint32_t fourcc,
405                      int* r_pos,
406                      int* g_pos,
407                      int* b_pos,
408                      int* bytes) {
409     if (fourcc == cricket::FOURCC_RAW) {
410       *r_pos = 0; *g_pos = 1; *b_pos = 2; *bytes = 3;  // RGB in memory.
411     } else if (fourcc == cricket::FOURCC_24BG) {
412       *r_pos = 2; *g_pos = 1; *b_pos = 0; *bytes = 3;  // BGR in memory.
413     } else if (fourcc == cricket::FOURCC_ABGR) {
414       *r_pos = 0; *g_pos = 1; *b_pos = 2; *bytes = 4;  // RGBA in memory.
415     } else if (fourcc == cricket::FOURCC_BGRA) {
416       *r_pos = 1; *g_pos = 2; *b_pos = 3; *bytes = 4;  // ARGB in memory.
417     } else if (fourcc == cricket::FOURCC_ARGB) {
418       *r_pos = 2; *g_pos = 1; *b_pos = 0; *bytes = 4;  // BGRA in memory.
419     } else {
420       return false;
421     }
422     return true;
423   }
424 
425   // Comparison functions for testing.
IsNull(const cricket::VideoFrame & frame)426   static bool IsNull(const cricket::VideoFrame& frame) {
427     return !frame.GetYPlane();
428   }
429 
IsSize(const cricket::VideoFrame & frame,uint32_t width,uint32_t height)430   static bool IsSize(const cricket::VideoFrame& frame,
431                      uint32_t width,
432                      uint32_t height) {
433     return !IsNull(frame) && frame.GetYPitch() >= static_cast<int32_t>(width) &&
434            frame.GetUPitch() >= static_cast<int32_t>(width) / 2 &&
435            frame.GetVPitch() >= static_cast<int32_t>(width) / 2 &&
436            frame.GetWidth() == width && frame.GetHeight() == height;
437   }
438 
IsPlaneEqual(const std::string & name,const uint8_t * plane1,uint32_t pitch1,const uint8_t * plane2,uint32_t pitch2,uint32_t width,uint32_t height,int max_error)439   static bool IsPlaneEqual(const std::string& name,
440                            const uint8_t* plane1,
441                            uint32_t pitch1,
442                            const uint8_t* plane2,
443                            uint32_t pitch2,
444                            uint32_t width,
445                            uint32_t height,
446                            int max_error) {
447     const uint8_t* r1 = plane1;
448     const uint8_t* r2 = plane2;
449     for (uint32_t y = 0; y < height; ++y) {
450       for (uint32_t x = 0; x < width; ++x) {
451         if (abs(static_cast<int>(r1[x] - r2[x])) > max_error) {
452           LOG(LS_INFO) << "IsPlaneEqual(" << name << "): pixel["
453                        << x << "," << y << "] differs: "
454                        << static_cast<int>(r1[x]) << " vs "
455                        << static_cast<int>(r2[x]);
456           return false;
457         }
458       }
459       r1 += pitch1;
460       r2 += pitch2;
461     }
462     return true;
463   }
464 
IsEqual(const cricket::VideoFrame & frame,size_t width,size_t height,size_t pixel_width,size_t pixel_height,int64_t time_stamp,const uint8_t * y,uint32_t ypitch,const uint8_t * u,uint32_t upitch,const uint8_t * v,uint32_t vpitch,int max_error)465   static bool IsEqual(const cricket::VideoFrame& frame,
466                       size_t width,
467                       size_t height,
468                       size_t pixel_width,
469                       size_t pixel_height,
470                       int64_t time_stamp,
471                       const uint8_t* y,
472                       uint32_t ypitch,
473                       const uint8_t* u,
474                       uint32_t upitch,
475                       const uint8_t* v,
476                       uint32_t vpitch,
477                       int max_error) {
478     return IsSize(frame, static_cast<uint32_t>(width),
479                   static_cast<uint32_t>(height)) &&
480            frame.GetPixelWidth() == pixel_width &&
481            frame.GetPixelHeight() == pixel_height &&
482            frame.GetTimeStamp() == time_stamp &&
483            IsPlaneEqual("y", frame.GetYPlane(), frame.GetYPitch(), y, ypitch,
484                         static_cast<uint32_t>(width),
485                         static_cast<uint32_t>(height), max_error) &&
486            IsPlaneEqual("u", frame.GetUPlane(), frame.GetUPitch(), u, upitch,
487                         static_cast<uint32_t>((width + 1) / 2),
488                         static_cast<uint32_t>((height + 1) / 2), max_error) &&
489            IsPlaneEqual("v", frame.GetVPlane(), frame.GetVPitch(), v, vpitch,
490                         static_cast<uint32_t>((width + 1) / 2),
491                         static_cast<uint32_t>((height + 1) / 2), max_error);
492   }
493 
IsEqual(const cricket::VideoFrame & frame1,const cricket::VideoFrame & frame2,int max_error)494   static bool IsEqual(const cricket::VideoFrame& frame1,
495                       const cricket::VideoFrame& frame2,
496                       int max_error) {
497     return IsEqual(frame1,
498                    frame2.GetWidth(), frame2.GetHeight(),
499                    frame2.GetPixelWidth(), frame2.GetPixelHeight(),
500                    frame2.GetTimeStamp(),
501                    frame2.GetYPlane(), frame2.GetYPitch(),
502                    frame2.GetUPlane(), frame2.GetUPitch(),
503                    frame2.GetVPlane(), frame2.GetVPitch(),
504                    max_error);
505   }
506 
IsEqualWithCrop(const cricket::VideoFrame & frame1,const cricket::VideoFrame & frame2,int hcrop,int vcrop,int max_error)507   static bool IsEqualWithCrop(const cricket::VideoFrame& frame1,
508                               const cricket::VideoFrame& frame2,
509                               int hcrop, int vcrop, int max_error) {
510     return frame1.GetWidth() <= frame2.GetWidth() &&
511            frame1.GetHeight() <= frame2.GetHeight() &&
512            IsEqual(frame1,
513                    frame2.GetWidth() - hcrop * 2,
514                    frame2.GetHeight() - vcrop * 2,
515                    frame2.GetPixelWidth(), frame2.GetPixelHeight(),
516                    frame2.GetTimeStamp(),
517                    frame2.GetYPlane() + vcrop * frame2.GetYPitch()
518                        + hcrop,
519                    frame2.GetYPitch(),
520                    frame2.GetUPlane() + vcrop * frame2.GetUPitch() / 2
521                        + hcrop / 2,
522                    frame2.GetUPitch(),
523                    frame2.GetVPlane() + vcrop * frame2.GetVPitch() / 2
524                        + hcrop / 2,
525                    frame2.GetVPitch(),
526                    max_error);
527   }
528 
IsBlack(const cricket::VideoFrame & frame)529   static bool IsBlack(const cricket::VideoFrame& frame) {
530     return !IsNull(frame) &&
531         *frame.GetYPlane() == 16 &&
532         *frame.GetUPlane() == 128 &&
533         *frame.GetVPlane() == 128;
534   }
535 
536   ////////////////////////
537   // Construction tests //
538   ////////////////////////
539 
540   // Test constructing an image from a I420 buffer.
ConstructI420()541   void ConstructI420() {
542     T frame;
543     EXPECT_TRUE(IsNull(frame));
544     rtc::scoped_ptr<rtc::MemoryStream> ms(
545         CreateYuvSample(kWidth, kHeight, 12));
546     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420,
547                           kWidth, kHeight, &frame));
548 
549     const uint8_t* y = reinterpret_cast<uint8_t*>(ms.get()->GetBuffer());
550     const uint8_t* u = y + kWidth * kHeight;
551     const uint8_t* v = u + kWidth * kHeight / 4;
552     EXPECT_TRUE(IsEqual(frame, kWidth, kHeight, 1, 1, 0, y, kWidth, u,
553                         kWidth / 2, v, kWidth / 2, 0));
554   }
555 
556   // Test constructing an image from a YV12 buffer.
ConstructYV12()557   void ConstructYV12() {
558     T frame;
559     rtc::scoped_ptr<rtc::MemoryStream> ms(
560         CreateYuvSample(kWidth, kHeight, 12));
561     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YV12,
562                           kWidth, kHeight, &frame));
563 
564     const uint8_t* y = reinterpret_cast<uint8_t*>(ms.get()->GetBuffer());
565     const uint8_t* v = y + kWidth * kHeight;
566     const uint8_t* u = v + kWidth * kHeight / 4;
567     EXPECT_TRUE(IsEqual(frame, kWidth, kHeight, 1, 1, 0, y, kWidth, u,
568                         kWidth / 2, v, kWidth / 2, 0));
569   }
570 
571   // Test constructing an image from a I422 buffer.
ConstructI422()572   void ConstructI422() {
573     T frame1, frame2;
574     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
575     size_t buf_size = kWidth * kHeight * 2;
576     rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment]);
577     uint8_t* y = ALIGNP(buf.get(), kAlignment);
578     uint8_t* u = y + kWidth * kHeight;
579     uint8_t* v = u + (kWidth / 2) * kHeight;
580     EXPECT_EQ(0, libyuv::I420ToI422(frame1.GetYPlane(), frame1.GetYPitch(),
581                                     frame1.GetUPlane(), frame1.GetUPitch(),
582                                     frame1.GetVPlane(), frame1.GetVPitch(),
583                                     y, kWidth,
584                                     u, kWidth / 2,
585                                     v, kWidth / 2,
586                                     kWidth, kHeight));
587     EXPECT_TRUE(LoadFrame(y, buf_size, cricket::FOURCC_I422,
588                           kWidth, kHeight, &frame2));
589     EXPECT_TRUE(IsEqual(frame1, frame2, 1));
590   }
591 
592   // Test constructing an image from a YUY2 buffer.
ConstructYuy2()593   void ConstructYuy2() {
594     T frame1, frame2;
595     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
596     size_t buf_size = kWidth * kHeight * 2;
597     rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment]);
598     uint8_t* yuy2 = ALIGNP(buf.get(), kAlignment);
599     EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(),
600                                     frame1.GetUPlane(), frame1.GetUPitch(),
601                                     frame1.GetVPlane(), frame1.GetVPitch(),
602                                     yuy2, kWidth * 2,
603                                     kWidth, kHeight));
604     EXPECT_TRUE(LoadFrame(yuy2, buf_size, cricket::FOURCC_YUY2,
605                           kWidth, kHeight, &frame2));
606     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
607   }
608 
609   // Test constructing an image from a YUY2 buffer with buffer unaligned.
ConstructYuy2Unaligned()610   void ConstructYuy2Unaligned() {
611     T frame1, frame2;
612     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
613     size_t buf_size = kWidth * kHeight * 2;
614     rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[buf_size + kAlignment + 1]);
615     uint8_t* yuy2 = ALIGNP(buf.get(), kAlignment) + 1;
616     EXPECT_EQ(0, libyuv::I420ToYUY2(frame1.GetYPlane(), frame1.GetYPitch(),
617                                     frame1.GetUPlane(), frame1.GetUPitch(),
618                                     frame1.GetVPlane(), frame1.GetVPitch(),
619                                     yuy2, kWidth * 2,
620                                     kWidth, kHeight));
621     EXPECT_TRUE(LoadFrame(yuy2, buf_size, cricket::FOURCC_YUY2,
622                           kWidth, kHeight, &frame2));
623     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
624   }
625 
626   // Test constructing an image from a wide YUY2 buffer.
627   // Normal is 1280x720.  Wide is 12800x72
ConstructYuy2Wide()628   void ConstructYuy2Wide() {
629     T frame1, frame2;
630     rtc::scoped_ptr<rtc::MemoryStream> ms(
631         CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth * 10, kHeight / 10));
632     ASSERT_TRUE(ms.get() != NULL);
633     EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2,
634                               kWidth * 10, kHeight / 10,
635                               &frame1));
636     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2,
637                           kWidth * 10, kHeight / 10, &frame2));
638     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
639   }
640 
641   // Test constructing an image from a UYVY buffer.
ConstructUyvy()642   void ConstructUyvy() {
643     T frame1, frame2;
644     rtc::scoped_ptr<rtc::MemoryStream> ms(
645         CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight));
646     ASSERT_TRUE(ms.get() != NULL);
647     EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight,
648                               &frame1));
649     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY,
650                           kWidth, kHeight, &frame2));
651     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
652   }
653 
654   // Test constructing an image from a random buffer.
655   // We are merely verifying that the code succeeds and is free of crashes.
ConstructM420()656   void ConstructM420() {
657     T frame;
658     rtc::scoped_ptr<rtc::MemoryStream> ms(
659         CreateYuvSample(kWidth, kHeight, 12));
660     ASSERT_TRUE(ms.get() != NULL);
661     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_M420,
662                           kWidth, kHeight, &frame));
663   }
664 
ConstructNV21()665   void ConstructNV21() {
666     T frame;
667     rtc::scoped_ptr<rtc::MemoryStream> ms(
668         CreateYuvSample(kWidth, kHeight, 12));
669     ASSERT_TRUE(ms.get() != NULL);
670     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_NV21,
671                           kWidth, kHeight, &frame));
672   }
673 
ConstructNV12()674   void ConstructNV12() {
675     T frame;
676     rtc::scoped_ptr<rtc::MemoryStream> ms(
677         CreateYuvSample(kWidth, kHeight, 12));
678     ASSERT_TRUE(ms.get() != NULL);
679     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_NV12,
680                           kWidth, kHeight, &frame));
681   }
682 
683   // Test constructing an image from a ABGR buffer
684   // Due to rounding, some pixels may differ slightly from the VideoFrame impl.
ConstructABGR()685   void ConstructABGR() {
686     T frame1, frame2;
687     rtc::scoped_ptr<rtc::MemoryStream> ms(
688         CreateRgbSample(cricket::FOURCC_ABGR, kWidth, kHeight));
689     ASSERT_TRUE(ms.get() != NULL);
690     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ABGR, kWidth, kHeight,
691                            &frame1));
692     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ABGR,
693                           kWidth, kHeight, &frame2));
694     EXPECT_TRUE(IsEqual(frame1, frame2, 2));
695   }
696 
697   // Test constructing an image from a ARGB buffer
698   // Due to rounding, some pixels may differ slightly from the VideoFrame impl.
ConstructARGB()699   void ConstructARGB() {
700     T frame1, frame2;
701     rtc::scoped_ptr<rtc::MemoryStream> ms(
702         CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));
703     ASSERT_TRUE(ms.get() != NULL);
704     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight,
705                            &frame1));
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
ConstructARGBWide()713   void ConstructARGBWide() {
714     T frame1, frame2;
715     rtc::scoped_ptr<rtc::MemoryStream> ms(
716         CreateRgbSample(cricket::FOURCC_ARGB, kWidth * 10, kHeight / 10));
717     ASSERT_TRUE(ms.get() != NULL);
718     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB,
719                            kWidth * 10, kHeight / 10, &frame1));
720     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB,
721                           kWidth * 10, kHeight / 10, &frame2));
722     EXPECT_TRUE(IsEqual(frame1, frame2, 2));
723   }
724 
725   // Test constructing an image from an BGRA buffer.
726   // Due to rounding, some pixels may differ slightly from the VideoFrame impl.
ConstructBGRA()727   void ConstructBGRA() {
728     T frame1, frame2;
729     rtc::scoped_ptr<rtc::MemoryStream> ms(
730         CreateRgbSample(cricket::FOURCC_BGRA, kWidth, kHeight));
731     ASSERT_TRUE(ms.get() != NULL);
732     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_BGRA, kWidth, kHeight,
733                            &frame1));
734     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_BGRA,
735                           kWidth, kHeight, &frame2));
736     EXPECT_TRUE(IsEqual(frame1, frame2, 2));
737   }
738 
739   // Test constructing an image from a 24BG buffer.
740   // Due to rounding, some pixels may differ slightly from the VideoFrame impl.
Construct24BG()741   void Construct24BG() {
742     T frame1, frame2;
743     rtc::scoped_ptr<rtc::MemoryStream> ms(
744         CreateRgbSample(cricket::FOURCC_24BG, kWidth, kHeight));
745     ASSERT_TRUE(ms.get() != NULL);
746     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_24BG, kWidth, kHeight,
747                            &frame1));
748     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_24BG,
749                           kWidth, kHeight, &frame2));
750     EXPECT_TRUE(IsEqual(frame1, frame2, 2));
751   }
752 
753   // Test constructing an image from a raw RGB buffer.
754   // Due to rounding, some pixels may differ slightly from the VideoFrame impl.
ConstructRaw()755   void ConstructRaw() {
756     T frame1, frame2;
757     rtc::scoped_ptr<rtc::MemoryStream> ms(
758         CreateRgbSample(cricket::FOURCC_RAW, kWidth, kHeight));
759     ASSERT_TRUE(ms.get() != NULL);
760     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_RAW, kWidth, kHeight,
761                            &frame1));
762     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_RAW,
763                           kWidth, kHeight, &frame2));
764     EXPECT_TRUE(IsEqual(frame1, frame2, 2));
765   }
766 
767   // Test constructing an image from a RGB565 buffer
ConstructRGB565()768   void ConstructRGB565() {
769     T frame1, frame2;
770     size_t out_size = kWidth * kHeight * 2;
771     rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]);
772     uint8_t* out = ALIGNP(outbuf.get(), kAlignment);
773     T frame;
774     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
775     EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(cricket::FOURCC_RGBP,
776                                                  out,
777                                                  out_size, kWidth * 2));
778     EXPECT_TRUE(LoadFrame(out, out_size, cricket::FOURCC_RGBP,
779                           kWidth, kHeight, &frame2));
780     EXPECT_TRUE(IsEqual(frame1, frame2, 20));
781   }
782 
783   // Test constructing an image from a ARGB1555 buffer
ConstructARGB1555()784   void ConstructARGB1555() {
785     T frame1, frame2;
786     size_t out_size = kWidth * kHeight * 2;
787     rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]);
788     uint8_t* out = ALIGNP(outbuf.get(), kAlignment);
789     T frame;
790     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
791     EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(cricket::FOURCC_RGBO,
792                                                  out,
793                                                  out_size, kWidth * 2));
794     EXPECT_TRUE(LoadFrame(out, out_size, cricket::FOURCC_RGBO,
795                           kWidth, kHeight, &frame2));
796     EXPECT_TRUE(IsEqual(frame1, frame2, 20));
797   }
798 
799   // Test constructing an image from a ARGB4444 buffer
ConstructARGB4444()800   void ConstructARGB4444() {
801     T frame1, frame2;
802     size_t out_size = kWidth * kHeight * 2;
803     rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]);
804     uint8_t* out = ALIGNP(outbuf.get(), kAlignment);
805     T frame;
806     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
807     EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(cricket::FOURCC_R444,
808                                                  out,
809                                                  out_size, kWidth * 2));
810     EXPECT_TRUE(LoadFrame(out, out_size, cricket::FOURCC_R444,
811                           kWidth, kHeight, &frame2));
812     EXPECT_TRUE(IsEqual(frame1, frame2, 20));
813   }
814 
815 // Macro to help test different rotations
816 #define TEST_MIRROR(FOURCC, BPP)                                               \
817   void Construct##FOURCC##Mirror() {                                           \
818     T frame1, frame2, frame3;                                                  \
819     rtc::scoped_ptr<rtc::MemoryStream> ms(                                     \
820         CreateYuvSample(kWidth, kHeight, BPP));                                \
821     ASSERT_TRUE(ms.get() != NULL);                                             \
822     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_##FOURCC, kWidth,          \
823                           -kHeight, kWidth, kHeight,                           \
824                           webrtc::kVideoRotation_180, &frame1));               \
825     size_t data_size;                                                          \
826     bool ret = ms->GetSize(&data_size);                                        \
827     EXPECT_TRUE(ret);                                                          \
828     EXPECT_TRUE(frame2.Init(cricket::FOURCC_##FOURCC, kWidth, kHeight, kWidth, \
829                             kHeight,                                           \
830                             reinterpret_cast<uint8_t*>(ms->GetBuffer()),       \
831                             data_size, 1, 1, 0, webrtc::kVideoRotation_0));    \
832     int width_rotate = static_cast<int>(frame1.GetWidth());                    \
833     int height_rotate = static_cast<int>(frame1.GetHeight());                  \
834     EXPECT_TRUE(frame3.InitToBlack(width_rotate, height_rotate, 1, 1, 0));     \
835     libyuv::I420Mirror(                                                        \
836         frame2.GetYPlane(), frame2.GetYPitch(), frame2.GetUPlane(),            \
837         frame2.GetUPitch(), frame2.GetVPlane(), frame2.GetVPitch(),            \
838         frame3.GetYPlane(), frame3.GetYPitch(), frame3.GetUPlane(),            \
839         frame3.GetUPitch(), frame3.GetVPlane(), frame3.GetVPitch(), kWidth,    \
840         kHeight);                                                              \
841     EXPECT_TRUE(IsEqual(frame1, frame3, 0));                                   \
842   }
843 
844   TEST_MIRROR(I420, 420)
845 
846 // Macro to help test different rotations
847 #define TEST_ROTATE(FOURCC, BPP, ROTATE)                                       \
848   void Construct##FOURCC##Rotate##ROTATE() {                                   \
849     T frame1, frame2, frame3;                                                  \
850     rtc::scoped_ptr<rtc::MemoryStream> ms(                                     \
851         CreateYuvSample(kWidth, kHeight, BPP));                                \
852     ASSERT_TRUE(ms.get() != NULL);                                             \
853     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_##FOURCC, kWidth, kHeight, \
854                           kWidth, kHeight, webrtc::kVideoRotation_##ROTATE,    \
855                           &frame1));                                           \
856     size_t data_size;                                                          \
857     bool ret = ms->GetSize(&data_size);                                        \
858     EXPECT_TRUE(ret);                                                          \
859     EXPECT_TRUE(frame2.Init(cricket::FOURCC_##FOURCC, kWidth, kHeight, kWidth, \
860                             kHeight,                                           \
861                             reinterpret_cast<uint8_t*>(ms->GetBuffer()),       \
862                             data_size, 1, 1, 0, webrtc::kVideoRotation_0));    \
863     int width_rotate = static_cast<int>(frame1.GetWidth());                    \
864     int height_rotate = static_cast<int>(frame1.GetHeight());                  \
865     EXPECT_TRUE(frame3.InitToBlack(width_rotate, height_rotate, 1, 1, 0));     \
866     libyuv::I420Rotate(                                                        \
867         frame2.GetYPlane(), frame2.GetYPitch(), frame2.GetUPlane(),            \
868         frame2.GetUPitch(), frame2.GetVPlane(), frame2.GetVPitch(),            \
869         frame3.GetYPlane(), frame3.GetYPitch(), frame3.GetUPlane(),            \
870         frame3.GetUPitch(), frame3.GetVPlane(), frame3.GetVPitch(), kWidth,    \
871         kHeight, libyuv::kRotate##ROTATE);                                     \
872     EXPECT_TRUE(IsEqual(frame1, frame3, 0));                                   \
873   }
874 
875   // Test constructing an image with rotation.
876   TEST_ROTATE(I420, 12, 0)
877   TEST_ROTATE(I420, 12, 90)
878   TEST_ROTATE(I420, 12, 180)
879   TEST_ROTATE(I420, 12, 270)
880   TEST_ROTATE(YV12, 12, 0)
881   TEST_ROTATE(YV12, 12, 90)
882   TEST_ROTATE(YV12, 12, 180)
883   TEST_ROTATE(YV12, 12, 270)
884   TEST_ROTATE(NV12, 12, 0)
885   TEST_ROTATE(NV12, 12, 90)
886   TEST_ROTATE(NV12, 12, 180)
887   TEST_ROTATE(NV12, 12, 270)
888   TEST_ROTATE(NV21, 12, 0)
889   TEST_ROTATE(NV21, 12, 90)
890   TEST_ROTATE(NV21, 12, 180)
891   TEST_ROTATE(NV21, 12, 270)
892   TEST_ROTATE(UYVY, 16, 0)
893   TEST_ROTATE(UYVY, 16, 90)
894   TEST_ROTATE(UYVY, 16, 180)
895   TEST_ROTATE(UYVY, 16, 270)
896   TEST_ROTATE(YUY2, 16, 0)
897   TEST_ROTATE(YUY2, 16, 90)
898   TEST_ROTATE(YUY2, 16, 180)
899   TEST_ROTATE(YUY2, 16, 270)
900 
901   // Test constructing an image from a UYVY buffer rotated 90 degrees.
ConstructUyvyRotate90()902   void ConstructUyvyRotate90() {
903     T frame2;
904     rtc::scoped_ptr<rtc::MemoryStream> ms(
905         CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight));
906     ASSERT_TRUE(ms.get() != NULL);
907     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight,
908                           kWidth, kHeight, webrtc::kVideoRotation_90, &frame2));
909   }
910 
911   // Test constructing an image from a UYVY buffer rotated 180 degrees.
ConstructUyvyRotate180()912   void ConstructUyvyRotate180() {
913     T frame2;
914     rtc::scoped_ptr<rtc::MemoryStream> ms(
915         CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight));
916     ASSERT_TRUE(ms.get() != NULL);
917     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight,
918                           kWidth, kHeight, webrtc::kVideoRotation_180,
919                           &frame2));
920   }
921 
922   // Test constructing an image from a UYVY buffer rotated 270 degrees.
ConstructUyvyRotate270()923   void ConstructUyvyRotate270() {
924     T frame2;
925     rtc::scoped_ptr<rtc::MemoryStream> ms(
926         CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight));
927     ASSERT_TRUE(ms.get() != NULL);
928     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight,
929                           kWidth, kHeight, webrtc::kVideoRotation_270,
930                           &frame2));
931   }
932 
933   // Test constructing an image from a YUY2 buffer rotated 90 degrees.
ConstructYuy2Rotate90()934   void ConstructYuy2Rotate90() {
935     T frame2;
936     rtc::scoped_ptr<rtc::MemoryStream> ms(
937         CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight));
938     ASSERT_TRUE(ms.get() != NULL);
939     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
940                           kWidth, kHeight, webrtc::kVideoRotation_90, &frame2));
941   }
942 
943   // Test constructing an image from a YUY2 buffer rotated 180 degrees.
ConstructYuy2Rotate180()944   void ConstructYuy2Rotate180() {
945     T frame2;
946     rtc::scoped_ptr<rtc::MemoryStream> ms(
947         CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight));
948     ASSERT_TRUE(ms.get() != NULL);
949     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
950                           kWidth, kHeight, webrtc::kVideoRotation_180,
951                           &frame2));
952   }
953 
954   // Test constructing an image from a YUY2 buffer rotated 270 degrees.
ConstructYuy2Rotate270()955   void ConstructYuy2Rotate270() {
956     T frame2;
957     rtc::scoped_ptr<rtc::MemoryStream> ms(
958         CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight));
959     ASSERT_TRUE(ms.get() != NULL);
960     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
961                           kWidth, kHeight, webrtc::kVideoRotation_270,
962                           &frame2));
963   }
964 
965   // Test 1 pixel edge case image I420 buffer.
ConstructI4201Pixel()966   void ConstructI4201Pixel() {
967     T frame;
968     uint8_t pixel[3] = {1, 2, 3};
969     for (int i = 0; i < repeat_; ++i) {
970       EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 1, 1, 1, 1, pixel,
971                              sizeof(pixel), 1, 1, 0, webrtc::kVideoRotation_0));
972     }
973     const uint8_t* y = pixel;
974     const uint8_t* u = y + 1;
975     const uint8_t* v = u + 1;
976     EXPECT_TRUE(IsEqual(frame, 1, 1, 1, 1, 0, y, 1, u, 1, v, 1, 0));
977   }
978 
979   // Test 5 pixel edge case image.
ConstructI4205Pixel()980   void ConstructI4205Pixel() {
981     T frame;
982     uint8_t pixels5x5[5 * 5 + ((5 + 1) / 2 * (5 + 1) / 2) * 2];
983     memset(pixels5x5, 1, 5 * 5 + ((5 + 1) / 2 * (5 + 1) / 2) *  2);
984     for (int i = 0; i < repeat_; ++i) {
985       EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 5, 5, 5, 5, pixels5x5,
986                              sizeof(pixels5x5), 1, 1, 0,
987                              webrtc::kVideoRotation_0));
988     }
989     EXPECT_EQ(5u, frame.GetWidth());
990     EXPECT_EQ(5u, frame.GetHeight());
991     EXPECT_EQ(5, frame.GetYPitch());
992     EXPECT_EQ(3, frame.GetUPitch());
993     EXPECT_EQ(3, frame.GetVPitch());
994   }
995 
996   // Test 1 pixel edge case image ARGB buffer.
ConstructARGB1Pixel()997   void ConstructARGB1Pixel() {
998     T frame;
999     uint8_t pixel[4] = {64, 128, 192, 255};
1000     for (int i = 0; i < repeat_; ++i) {
1001       EXPECT_TRUE(frame.Init(cricket::FOURCC_ARGB, 1, 1, 1, 1, pixel,
1002                              sizeof(pixel), 1, 1, 0,
1003                              webrtc::kVideoRotation_0));
1004     }
1005     // Convert back to ARGB.
1006     size_t out_size = 4;
1007     rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]);
1008     uint8_t* out = ALIGNP(outbuf.get(), kAlignment);
1009 
1010     EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB,
1011                                                  out,
1012                                                  out_size,    // buffer size
1013                                                  out_size));  // stride
1014   #ifdef USE_LMI_CONVERT
1015     // TODO(fbarchard): Expected to fail, but not crash.
1016     EXPECT_FALSE(IsPlaneEqual("argb", pixel, 4, out, 4, 3, 1, 2));
1017   #else
1018     // TODO(fbarchard): Check for overwrite.
1019     EXPECT_TRUE(IsPlaneEqual("argb", pixel, 4, out, 4, 3, 1, 2));
1020   #endif
1021   }
1022 
1023   // Test Black, White and Grey pixels.
ConstructARGBBlackWhitePixel()1024   void ConstructARGBBlackWhitePixel() {
1025     T frame;
1026     uint8_t pixel[10 * 4] = {0,   0,   0,   255,   // Black.
1027                              0,   0,   0,   255,   // Black.
1028                              64,  64,  64,  255,   // Dark Grey.
1029                              64,  64,  64,  255,   // Dark Grey.
1030                              128, 128, 128, 255,   // Grey.
1031                              128, 128, 128, 255,   // Grey.
1032                              196, 196, 196, 255,   // Light Grey.
1033                              196, 196, 196, 255,   // Light Grey.
1034                              255, 255, 255, 255,   // White.
1035                              255, 255, 255, 255};  // White.
1036 
1037     for (int i = 0; i < repeat_; ++i) {
1038       EXPECT_TRUE(frame.Init(cricket::FOURCC_ARGB, 10, 1, 10, 1, pixel,
1039                              sizeof(pixel), 1, 1, 0,
1040                              webrtc::kVideoRotation_0));
1041     }
1042     // Convert back to ARGB
1043     size_t out_size = 10 * 4;
1044     rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment]);
1045     uint8_t* out = ALIGNP(outbuf.get(), kAlignment);
1046 
1047     EXPECT_EQ(out_size, frame.ConvertToRgbBuffer(cricket::FOURCC_ARGB,
1048                                                  out,
1049                                                  out_size,    // buffer size.
1050                                                  out_size));  // stride.
1051     EXPECT_TRUE(IsPlaneEqual("argb", pixel, out_size,
1052                              out, out_size,
1053                              out_size, 1, 2));
1054   }
1055 
1056   // Test constructing an image from an I420 buffer with horizontal cropping.
ConstructI420CropHorizontal()1057   void ConstructI420CropHorizontal() {
1058     T frame1, frame2;
1059     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1060     ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_I420, kWidth, kHeight,
1061                           kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0,
1062                           &frame2));
1063     EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 0));
1064   }
1065 
1066   // Test constructing an image from a YUY2 buffer with horizontal cropping.
ConstructYuy2CropHorizontal()1067   void ConstructYuy2CropHorizontal() {
1068     T frame1, frame2;
1069     rtc::scoped_ptr<rtc::MemoryStream> ms(
1070         CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight));
1071     ASSERT_TRUE(ms.get() != NULL);
1072     EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
1073                               &frame1));
1074     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
1075                           kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0,
1076                           &frame2));
1077     EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 0));
1078   }
1079 
1080   // Test constructing an image from an ARGB buffer with horizontal cropping.
ConstructARGBCropHorizontal()1081   void ConstructARGBCropHorizontal() {
1082     T frame1, frame2;
1083     rtc::scoped_ptr<rtc::MemoryStream> ms(
1084         CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));
1085     ASSERT_TRUE(ms.get() != NULL);
1086     EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight,
1087                            &frame1));
1088     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB, kWidth, kHeight,
1089                           kWidth * 3 / 4, kHeight, webrtc::kVideoRotation_0,
1090                           &frame2));
1091     EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, kWidth / 8, 0, 2));
1092   }
1093 
1094   // Test constructing an image from an I420 buffer, cropping top and bottom.
ConstructI420CropVertical()1095   void ConstructI420CropVertical() {
1096     T frame1, frame2;
1097     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1098     ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_I420, kWidth, kHeight,
1099                           kWidth, kHeight * 3 / 4, webrtc::kVideoRotation_0,
1100                           &frame2));
1101     EXPECT_TRUE(IsEqualWithCrop(frame2, frame1, 0, kHeight / 8, 0));
1102   }
1103 
1104   // Test constructing an image from I420 synonymous formats.
ConstructI420Aliases()1105   void ConstructI420Aliases() {
1106     T frame1, frame2, frame3;
1107     ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_I420, kWidth, kHeight,
1108                           &frame1));
1109     ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_IYUV, kWidth, kHeight,
1110                           &frame2));
1111     ASSERT_TRUE(LoadFrame(kImageFilename, cricket::FOURCC_YU12, kWidth, kHeight,
1112                           &frame3));
1113     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1114     EXPECT_TRUE(IsEqual(frame1, frame3, 0));
1115   }
1116 
1117   // Test constructing an image from an I420 MJPG buffer.
ConstructMjpgI420()1118   void ConstructMjpgI420() {
1119     T frame1, frame2;
1120     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1121     ASSERT_TRUE(LoadFrame(kJpeg420Filename,
1122                           cricket::FOURCC_MJPG, kWidth, kHeight, &frame2));
1123     EXPECT_TRUE(IsEqual(frame1, frame2, 32));
1124   }
1125 
1126   // Test constructing an image from an I422 MJPG buffer.
ConstructMjpgI422()1127   void ConstructMjpgI422() {
1128     T frame1, frame2;
1129     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1130     ASSERT_TRUE(LoadFrame(kJpeg422Filename,
1131                           cricket::FOURCC_MJPG, kWidth, kHeight, &frame2));
1132     EXPECT_TRUE(IsEqual(frame1, frame2, 32));
1133   }
1134 
1135   // Test constructing an image from an I444 MJPG buffer.
ConstructMjpgI444()1136   void ConstructMjpgI444() {
1137     T frame1, frame2;
1138     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1139     ASSERT_TRUE(LoadFrame(kJpeg444Filename,
1140                           cricket::FOURCC_MJPG, kWidth, kHeight, &frame2));
1141     EXPECT_TRUE(IsEqual(frame1, frame2, 32));
1142   }
1143 
1144   // Test constructing an image from an I444 MJPG buffer.
ConstructMjpgI411()1145   void ConstructMjpgI411() {
1146     T frame1, frame2;
1147     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1148     ASSERT_TRUE(LoadFrame(kJpeg411Filename,
1149                           cricket::FOURCC_MJPG, kWidth, kHeight, &frame2));
1150     EXPECT_TRUE(IsEqual(frame1, frame2, 32));
1151   }
1152 
1153   // Test constructing an image from an I400 MJPG buffer.
1154   // TODO(fbarchard): Stronger compare on chroma.  Compare agaisnt a grey image.
ConstructMjpgI400()1155   void ConstructMjpgI400() {
1156     T frame1, frame2;
1157     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1158     ASSERT_TRUE(LoadFrame(kJpeg400Filename,
1159                           cricket::FOURCC_MJPG, kWidth, kHeight, &frame2));
1160     EXPECT_TRUE(IsPlaneEqual("y", frame1.GetYPlane(), frame1.GetYPitch(),
1161                              frame2.GetYPlane(), frame2.GetYPitch(),
1162                              kWidth, kHeight, 32));
1163     EXPECT_TRUE(IsEqual(frame1, frame2, 128));
1164   }
1165 
1166   // Test constructing an image from an I420 MJPG buffer.
ValidateFrame(const char * name,uint32_t fourcc,int data_adjust,int size_adjust,bool expected_result)1167   void ValidateFrame(const char* name,
1168                      uint32_t fourcc,
1169                      int data_adjust,
1170                      int size_adjust,
1171                      bool expected_result) {
1172     T frame;
1173     rtc::scoped_ptr<rtc::MemoryStream> ms(LoadSample(name));
1174     ASSERT_TRUE(ms.get() != NULL);
1175     const uint8_t* sample =
1176         reinterpret_cast<const uint8_t*>(ms.get()->GetBuffer());
1177     size_t sample_size;
1178     ms->GetSize(&sample_size);
1179     // Optional adjust size to test invalid size.
1180     size_t data_size = sample_size + data_adjust;
1181 
1182     // Allocate a buffer with end page aligned.
1183     const int kPadToHeapSized = 16 * 1024 * 1024;
1184     rtc::scoped_ptr<uint8_t[]> page_buffer(
1185         new uint8_t[((data_size + kPadToHeapSized + 4095) & ~4095)]);
1186     uint8_t* data_ptr = page_buffer.get();
1187     if (!data_ptr) {
1188       LOG(LS_WARNING) << "Failed to allocate memory for ValidateFrame test.";
1189       EXPECT_FALSE(expected_result);  // NULL is okay if failure was expected.
1190       return;
1191     }
1192     data_ptr += kPadToHeapSized + (-(static_cast<int>(data_size)) & 4095);
1193     memcpy(data_ptr, sample, std::min(data_size, sample_size));
1194     for (int i = 0; i < repeat_; ++i) {
1195       EXPECT_EQ(expected_result, frame.Validate(fourcc, kWidth, kHeight,
1196                                                 data_ptr,
1197                                                 sample_size + size_adjust));
1198     }
1199   }
1200 
1201   // Test validate for I420 MJPG buffer.
ValidateMjpgI420()1202   void ValidateMjpgI420() {
1203     ValidateFrame(kJpeg420Filename, cricket::FOURCC_MJPG, 0, 0, true);
1204   }
1205 
1206   // Test validate for I422 MJPG buffer.
ValidateMjpgI422()1207   void ValidateMjpgI422() {
1208     ValidateFrame(kJpeg422Filename, cricket::FOURCC_MJPG, 0, 0, true);
1209   }
1210 
1211   // Test validate for I444 MJPG buffer.
ValidateMjpgI444()1212   void ValidateMjpgI444() {
1213     ValidateFrame(kJpeg444Filename, cricket::FOURCC_MJPG, 0, 0, true);
1214   }
1215 
1216   // Test validate for I411 MJPG buffer.
ValidateMjpgI411()1217   void ValidateMjpgI411() {
1218     ValidateFrame(kJpeg411Filename, cricket::FOURCC_MJPG, 0, 0, true);
1219   }
1220 
1221   // Test validate for I400 MJPG buffer.
ValidateMjpgI400()1222   void ValidateMjpgI400() {
1223     ValidateFrame(kJpeg400Filename, cricket::FOURCC_MJPG, 0, 0, true);
1224   }
1225 
1226   // Test validate for I420 buffer.
ValidateI420()1227   void ValidateI420() {
1228     ValidateFrame(kImageFilename, cricket::FOURCC_I420, 0, 0, true);
1229   }
1230 
1231   // Test validate for I420 buffer where size is too small
ValidateI420SmallSize()1232   void ValidateI420SmallSize() {
1233     ValidateFrame(kImageFilename, cricket::FOURCC_I420, 0, -16384, false);
1234   }
1235 
1236   // Test validate for I420 buffer where size is too large (16 MB)
1237   // Will produce warning but pass.
ValidateI420LargeSize()1238   void ValidateI420LargeSize() {
1239     ValidateFrame(kImageFilename, cricket::FOURCC_I420, 16000000, 16000000,
1240                   true);
1241   }
1242 
1243   // Test validate for I420 buffer where size is 1 GB (not reasonable).
ValidateI420HugeSize()1244   void ValidateI420HugeSize() {
1245 #ifndef WIN32  // TODO(fbarchard): Reenable when fixing bug 9603762.
1246     ValidateFrame(kImageFilename, cricket::FOURCC_I420, 1000000000u,
1247                   1000000000u, false);
1248 #endif
1249   }
1250 
1251   // The following test that Validate crashes if the size is greater than the
1252   // actual buffer size.
1253   // TODO(fbarchard): Consider moving a filter into the capturer/plugin.
1254 #if defined(_MSC_VER) && !defined(NDEBUG)
ExceptionFilter(unsigned int code,struct _EXCEPTION_POINTERS * ep)1255   int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
1256     if (code == EXCEPTION_ACCESS_VIOLATION) {
1257       LOG(LS_INFO) << "Caught EXCEPTION_ACCESS_VIOLATION as expected.";
1258       return EXCEPTION_EXECUTE_HANDLER;
1259     } else {
1260       LOG(LS_INFO) << "Did not catch EXCEPTION_ACCESS_VIOLATION.  Unexpected.";
1261       return EXCEPTION_CONTINUE_SEARCH;
1262     }
1263   }
1264 
1265   // Test validate fails for truncated MJPG data buffer.  If ValidateFrame
1266   // crashes the exception handler will return and unittest passes with OK.
ValidateMjpgI420InvalidSize()1267   void ValidateMjpgI420InvalidSize() {
1268     __try {
1269       ValidateFrame(kJpeg420Filename, cricket::FOURCC_MJPG, -16384, 0, false);
1270       FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION.";
1271     } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
1272       return;  // Successfully crashed in ValidateFrame.
1273     }
1274   }
1275 
1276   // Test validate fails for truncated I420 buffer.
ValidateI420InvalidSize()1277   void ValidateI420InvalidSize() {
1278     __try {
1279       ValidateFrame(kImageFilename, cricket::FOURCC_I420, -16384, 0, false);
1280       FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION.";
1281     } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
1282       return;  // Successfully crashed in ValidateFrame.
1283     }
1284   }
1285 #endif
1286 
1287   // Test constructing an image from a YUY2 buffer (and synonymous formats).
ConstructYuy2Aliases()1288   void ConstructYuy2Aliases() {
1289     T frame1, frame2, frame3, frame4;
1290     rtc::scoped_ptr<rtc::MemoryStream> ms(
1291         CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight));
1292     ASSERT_TRUE(ms.get() != NULL);
1293     EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
1294                               &frame1));
1295     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2,
1296                           kWidth, kHeight, &frame2));
1297     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUVS,
1298                           kWidth, kHeight, &frame3));
1299     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUYV,
1300                           kWidth, kHeight, &frame4));
1301     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1302     EXPECT_TRUE(IsEqual(frame1, frame3, 0));
1303     EXPECT_TRUE(IsEqual(frame1, frame4, 0));
1304   }
1305 
1306   // Test constructing an image from a UYVY buffer (and synonymous formats).
ConstructUyvyAliases()1307   void ConstructUyvyAliases() {
1308     T frame1, frame2, frame3, frame4;
1309     rtc::scoped_ptr<rtc::MemoryStream> ms(
1310         CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight));
1311     ASSERT_TRUE(ms.get() != NULL);
1312     EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight,
1313                               &frame1));
1314     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY,
1315                           kWidth, kHeight, &frame2));
1316     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_2VUY,
1317                           kWidth, kHeight, &frame3));
1318     EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_HDYC,
1319                           kWidth, kHeight, &frame4));
1320     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1321     EXPECT_TRUE(IsEqual(frame1, frame3, 0));
1322     EXPECT_TRUE(IsEqual(frame1, frame4, 0));
1323   }
1324 
1325   // Test creating a copy.
ConstructCopy()1326   void ConstructCopy() {
1327     T frame1, frame2;
1328     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1329     for (int i = 0; i < repeat_; ++i) {
1330       EXPECT_TRUE(frame2.Init(frame1));
1331     }
1332     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1333   }
1334 
1335   // Test creating a copy and check that it just increments the refcount.
ConstructCopyIsRef()1336   void ConstructCopyIsRef() {
1337     T frame1, frame2;
1338     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1339     for (int i = 0; i < repeat_; ++i) {
1340       EXPECT_TRUE(frame2.Init(frame1));
1341     }
1342     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1343     EXPECT_EQ(frame1.GetYPlane(), frame2.GetYPlane());
1344     EXPECT_EQ(frame1.GetUPlane(), frame2.GetUPlane());
1345     EXPECT_EQ(frame1.GetVPlane(), frame2.GetVPlane());
1346   }
1347 
1348   // Test creating an empty image and initing it to black.
ConstructBlack()1349   void ConstructBlack() {
1350     T frame;
1351     for (int i = 0; i < repeat_; ++i) {
1352       EXPECT_TRUE(frame.InitToBlack(kWidth, kHeight, 1, 1, 0));
1353     }
1354     EXPECT_TRUE(IsSize(frame, kWidth, kHeight));
1355     EXPECT_TRUE(IsBlack(frame));
1356   }
1357 
1358   // Test constructing an image from a YUY2 buffer with a range of sizes.
1359   // Only tests that conversion does not crash or corrupt heap.
ConstructYuy2AllSizes()1360   void ConstructYuy2AllSizes() {
1361     T frame1, frame2;
1362     for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) {
1363       for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) {
1364         rtc::scoped_ptr<rtc::MemoryStream> ms(
1365             CreateYuv422Sample(cricket::FOURCC_YUY2, width, height));
1366         ASSERT_TRUE(ms.get() != NULL);
1367         EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, width, height,
1368                                   &frame1));
1369         EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2,
1370                               width, height, &frame2));
1371         EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1372       }
1373     }
1374   }
1375 
1376   // Test constructing an image from a ARGB buffer with a range of sizes.
1377   // Only tests that conversion does not crash or corrupt heap.
ConstructARGBAllSizes()1378   void ConstructARGBAllSizes() {
1379     T frame1, frame2;
1380     for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) {
1381       for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) {
1382         rtc::scoped_ptr<rtc::MemoryStream> ms(
1383             CreateRgbSample(cricket::FOURCC_ARGB, width, height));
1384         ASSERT_TRUE(ms.get() != NULL);
1385         EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, width, height,
1386                                &frame1));
1387         EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB,
1388                               width, height, &frame2));
1389         EXPECT_TRUE(IsEqual(frame1, frame2, 64));
1390       }
1391     }
1392     // Test a practical window size for screencasting usecase.
1393     const int kOddWidth = 1228;
1394     const int kOddHeight = 260;
1395     for (int j = 0; j < 2; ++j) {
1396       for (int i = 0; i < 2; ++i) {
1397         rtc::scoped_ptr<rtc::MemoryStream> ms(
1398         CreateRgbSample(cricket::FOURCC_ARGB, kOddWidth + i, kOddHeight + j));
1399         ASSERT_TRUE(ms.get() != NULL);
1400         EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB,
1401                                kOddWidth + i, kOddHeight + j,
1402                                &frame1));
1403         EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB,
1404                               kOddWidth + i, kOddHeight + j, &frame2));
1405         EXPECT_TRUE(IsEqual(frame1, frame2, 64));
1406       }
1407     }
1408   }
1409 
1410   // Tests re-initing an existing image.
Reset(webrtc::VideoRotation rotation,bool apply_rotation)1411   void Reset(webrtc::VideoRotation rotation, bool apply_rotation) {
1412     T frame1, frame2;
1413     rtc::scoped_ptr<rtc::MemoryStream> ms(
1414         LoadSample(kImageFilename));
1415     ASSERT_TRUE(ms.get() != NULL);
1416     size_t data_size;
1417     ms->GetSize(&data_size);
1418     EXPECT_TRUE(frame1.InitToBlack(kWidth, kHeight, 1, 1, 0));
1419     EXPECT_TRUE(frame2.InitToBlack(kWidth, kHeight, 1, 1, 0));
1420     EXPECT_TRUE(IsBlack(frame1));
1421     EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1422     EXPECT_TRUE(frame1.Reset(cricket::FOURCC_I420, kWidth, kHeight, kWidth,
1423                              kHeight,
1424                              reinterpret_cast<uint8_t*>(ms->GetBuffer()),
1425                              data_size, 1, 1, 0, rotation, apply_rotation));
1426     if (apply_rotation)
1427       EXPECT_EQ(webrtc::kVideoRotation_0, frame1.GetVideoRotation());
1428     else
1429       EXPECT_EQ(rotation, frame1.GetVideoRotation());
1430 
1431     // Swapp width and height if the frame is rotated 90 or 270 degrees.
1432     if (apply_rotation && (rotation == webrtc::kVideoRotation_90
1433         || rotation == webrtc::kVideoRotation_270)) {
1434       EXPECT_TRUE(kHeight == frame1.GetWidth());
1435       EXPECT_TRUE(kWidth == frame1.GetHeight());
1436     } else {
1437       EXPECT_TRUE(kWidth == frame1.GetWidth());
1438       EXPECT_TRUE(kHeight == frame1.GetHeight());
1439     }
1440     EXPECT_FALSE(IsBlack(frame1));
1441     EXPECT_FALSE(IsEqual(frame1, frame2, 0));
1442   }
1443 
ResetAndApplyRotation()1444   void ResetAndApplyRotation() {
1445     Reset(webrtc::kVideoRotation_90, true);
1446   }
1447 
ResetAndDontApplyRotation()1448   void ResetAndDontApplyRotation() {
1449     Reset(webrtc::kVideoRotation_90, false);
1450   }
1451 
1452   //////////////////////
1453   // Conversion tests //
1454   //////////////////////
1455 
1456   enum ToFrom { TO, FROM };
1457 
1458   // Helper function for test converting from I420 to packed formats.
ConvertToBuffer(int bpp,int rowpad,bool invert,ToFrom to_from,int error,uint32_t fourcc,int (* RGBToI420)(const uint8_t * src_frame,int src_stride_frame,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int width,int height))1459   inline void ConvertToBuffer(int bpp,
1460                               int rowpad,
1461                               bool invert,
1462                               ToFrom to_from,
1463                               int error,
1464                               uint32_t fourcc,
1465                               int (*RGBToI420)(const uint8_t* src_frame,
1466                                                int src_stride_frame,
1467                                                uint8_t* dst_y,
1468                                                int dst_stride_y,
1469                                                uint8_t* dst_u,
1470                                                int dst_stride_u,
1471                                                uint8_t* dst_v,
1472                                                int dst_stride_v,
1473                                                int width,
1474                                                int height)) {
1475     T frame1, frame2;
1476     int repeat_to = (to_from == TO) ? repeat_ : 1;
1477     int repeat_from  = (to_from == FROM) ? repeat_ : 1;
1478 
1479     int astride = kWidth * bpp + rowpad;
1480     size_t out_size = astride * kHeight;
1481     rtc::scoped_ptr<uint8_t[]> outbuf(new uint8_t[out_size + kAlignment + 1]);
1482     memset(outbuf.get(), 0, out_size + kAlignment + 1);
1483     uint8_t* outtop = ALIGNP(outbuf.get(), kAlignment);
1484     uint8_t* out = outtop;
1485     int stride = astride;
1486     if (invert) {
1487       out += (kHeight - 1) * stride;  // Point to last row.
1488       stride = -stride;
1489     }
1490     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1491 
1492     for (int i = 0; i < repeat_to; ++i) {
1493       EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(fourcc,
1494                                                     out,
1495                                                     out_size, stride));
1496     }
1497     EXPECT_TRUE(frame2.InitToBlack(kWidth, kHeight, 1, 1, 0));
1498     for (int i = 0; i < repeat_from; ++i) {
1499       EXPECT_EQ(0, RGBToI420(out, stride,
1500                              frame2.GetYPlane(), frame2.GetYPitch(),
1501                              frame2.GetUPlane(), frame2.GetUPitch(),
1502                              frame2.GetVPlane(), frame2.GetVPitch(),
1503                              kWidth, kHeight));
1504     }
1505     if (rowpad) {
1506       EXPECT_EQ(0, outtop[kWidth * bpp]);  // Ensure stride skipped end of row.
1507       EXPECT_NE(0, outtop[astride]);       // Ensure pixel at start of 2nd row.
1508     } else {
1509       EXPECT_NE(0, outtop[kWidth * bpp]);  // Expect something to be here.
1510     }
1511     EXPECT_EQ(0, outtop[out_size]);      // Ensure no overrun.
1512     EXPECT_TRUE(IsEqual(frame1, frame2, error));
1513   }
1514 
1515   static const int kError = 20;
1516   static const int kErrorHigh = 40;
1517   static const int kOddStride = 23;
1518 
1519   // Tests ConvertToRGBBuffer formats.
ConvertToARGBBuffer()1520   void ConvertToARGBBuffer() {
1521     ConvertToBuffer(4, 0, false, TO, kError,
1522                     cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1523   }
ConvertToBGRABuffer()1524   void ConvertToBGRABuffer() {
1525     ConvertToBuffer(4, 0, false, TO, kError,
1526                     cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1527   }
ConvertToABGRBuffer()1528   void ConvertToABGRBuffer() {
1529     ConvertToBuffer(4, 0, false, TO, kError,
1530                     cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1531   }
ConvertToRGB24Buffer()1532   void ConvertToRGB24Buffer() {
1533     ConvertToBuffer(3, 0, false, TO, kError,
1534                     cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1535   }
ConvertToRAWBuffer()1536   void ConvertToRAWBuffer() {
1537     ConvertToBuffer(3, 0, false, TO, kError,
1538                     cricket::FOURCC_RAW, libyuv::RAWToI420);
1539   }
ConvertToRGB565Buffer()1540   void ConvertToRGB565Buffer() {
1541     ConvertToBuffer(2, 0, false, TO, kError,
1542                     cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1543   }
ConvertToARGB1555Buffer()1544   void ConvertToARGB1555Buffer() {
1545     ConvertToBuffer(2, 0, false, TO, kError,
1546                     cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1547   }
ConvertToARGB4444Buffer()1548   void ConvertToARGB4444Buffer() {
1549     ConvertToBuffer(2, 0, false, TO, kError,
1550                     cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1551   }
ConvertToI400Buffer()1552   void ConvertToI400Buffer() {
1553     ConvertToBuffer(1, 0, false, TO, 128,
1554                     cricket::FOURCC_I400, libyuv::I400ToI420);
1555   }
ConvertToYUY2Buffer()1556   void ConvertToYUY2Buffer() {
1557     ConvertToBuffer(2, 0, false, TO, kError,
1558                     cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1559   }
ConvertToUYVYBuffer()1560   void ConvertToUYVYBuffer() {
1561     ConvertToBuffer(2, 0, false, TO, kError,
1562                     cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1563   }
1564 
1565   // Tests ConvertToRGBBuffer formats with odd stride.
ConvertToARGBBufferStride()1566   void ConvertToARGBBufferStride() {
1567     ConvertToBuffer(4, kOddStride, false, TO, kError,
1568                     cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1569   }
ConvertToBGRABufferStride()1570   void ConvertToBGRABufferStride() {
1571     ConvertToBuffer(4, kOddStride, false, TO, kError,
1572                     cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1573   }
ConvertToABGRBufferStride()1574   void ConvertToABGRBufferStride() {
1575     ConvertToBuffer(4, kOddStride, false, TO, kError,
1576                     cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1577   }
ConvertToRGB24BufferStride()1578   void ConvertToRGB24BufferStride() {
1579     ConvertToBuffer(3, kOddStride, false, TO, kError,
1580                     cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1581   }
ConvertToRAWBufferStride()1582   void ConvertToRAWBufferStride() {
1583     ConvertToBuffer(3, kOddStride, false, TO, kError,
1584                     cricket::FOURCC_RAW, libyuv::RAWToI420);
1585   }
ConvertToRGB565BufferStride()1586   void ConvertToRGB565BufferStride() {
1587     ConvertToBuffer(2, kOddStride, false, TO, kError,
1588                     cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1589   }
ConvertToARGB1555BufferStride()1590   void ConvertToARGB1555BufferStride() {
1591     ConvertToBuffer(2, kOddStride, false, TO, kError,
1592                     cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1593   }
ConvertToARGB4444BufferStride()1594   void ConvertToARGB4444BufferStride() {
1595     ConvertToBuffer(2, kOddStride, false, TO, kError,
1596                     cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1597   }
ConvertToI400BufferStride()1598   void ConvertToI400BufferStride() {
1599     ConvertToBuffer(1, kOddStride, false, TO, 128,
1600                     cricket::FOURCC_I400, libyuv::I400ToI420);
1601   }
ConvertToYUY2BufferStride()1602   void ConvertToYUY2BufferStride() {
1603     ConvertToBuffer(2, kOddStride, false, TO, kError,
1604                     cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1605   }
ConvertToUYVYBufferStride()1606   void ConvertToUYVYBufferStride() {
1607     ConvertToBuffer(2, kOddStride, false, TO, kError,
1608                     cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1609   }
1610 
1611   // Tests ConvertToRGBBuffer formats with negative stride to invert image.
ConvertToARGBBufferInverted()1612   void ConvertToARGBBufferInverted() {
1613     ConvertToBuffer(4, 0, true, TO, kError,
1614                     cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1615   }
ConvertToBGRABufferInverted()1616   void ConvertToBGRABufferInverted() {
1617     ConvertToBuffer(4, 0, true, TO, kError,
1618                     cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1619   }
ConvertToABGRBufferInverted()1620   void ConvertToABGRBufferInverted() {
1621     ConvertToBuffer(4, 0, true, TO, kError,
1622                     cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1623   }
ConvertToRGB24BufferInverted()1624   void ConvertToRGB24BufferInverted() {
1625     ConvertToBuffer(3, 0, true, TO, kError,
1626                     cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1627   }
ConvertToRAWBufferInverted()1628   void ConvertToRAWBufferInverted() {
1629     ConvertToBuffer(3, 0, true, TO, kError,
1630                     cricket::FOURCC_RAW, libyuv::RAWToI420);
1631   }
ConvertToRGB565BufferInverted()1632   void ConvertToRGB565BufferInverted() {
1633     ConvertToBuffer(2, 0, true, TO, kError,
1634                     cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1635   }
ConvertToARGB1555BufferInverted()1636   void ConvertToARGB1555BufferInverted() {
1637     ConvertToBuffer(2, 0, true, TO, kError,
1638                     cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1639   }
ConvertToARGB4444BufferInverted()1640   void ConvertToARGB4444BufferInverted() {
1641     ConvertToBuffer(2, 0, true, TO, kError,
1642                     cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1643   }
ConvertToI400BufferInverted()1644   void ConvertToI400BufferInverted() {
1645     ConvertToBuffer(1, 0, true, TO, 128,
1646                     cricket::FOURCC_I400, libyuv::I400ToI420);
1647   }
ConvertToYUY2BufferInverted()1648   void ConvertToYUY2BufferInverted() {
1649     ConvertToBuffer(2, 0, true, TO, kError,
1650                     cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1651   }
ConvertToUYVYBufferInverted()1652   void ConvertToUYVYBufferInverted() {
1653     ConvertToBuffer(2, 0, true, TO, kError,
1654                     cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1655   }
1656 
1657   // Tests ConvertFrom formats.
ConvertFromARGBBuffer()1658   void ConvertFromARGBBuffer() {
1659     ConvertToBuffer(4, 0, false, FROM, kError,
1660                     cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1661   }
ConvertFromBGRABuffer()1662   void ConvertFromBGRABuffer() {
1663     ConvertToBuffer(4, 0, false, FROM, kError,
1664                     cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1665   }
ConvertFromABGRBuffer()1666   void ConvertFromABGRBuffer() {
1667     ConvertToBuffer(4, 0, false, FROM, kError,
1668                     cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1669   }
ConvertFromRGB24Buffer()1670   void ConvertFromRGB24Buffer() {
1671     ConvertToBuffer(3, 0, false, FROM, kError,
1672                     cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1673   }
ConvertFromRAWBuffer()1674   void ConvertFromRAWBuffer() {
1675     ConvertToBuffer(3, 0, false, FROM, kError,
1676                     cricket::FOURCC_RAW, libyuv::RAWToI420);
1677   }
ConvertFromRGB565Buffer()1678   void ConvertFromRGB565Buffer() {
1679     ConvertToBuffer(2, 0, false, FROM, kError,
1680                     cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1681   }
ConvertFromARGB1555Buffer()1682   void ConvertFromARGB1555Buffer() {
1683     ConvertToBuffer(2, 0, false, FROM, kError,
1684                     cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1685   }
ConvertFromARGB4444Buffer()1686   void ConvertFromARGB4444Buffer() {
1687     ConvertToBuffer(2, 0, false, FROM, kError,
1688                     cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1689   }
ConvertFromI400Buffer()1690   void ConvertFromI400Buffer() {
1691     ConvertToBuffer(1, 0, false, FROM, 128,
1692                     cricket::FOURCC_I400, libyuv::I400ToI420);
1693   }
ConvertFromYUY2Buffer()1694   void ConvertFromYUY2Buffer() {
1695     ConvertToBuffer(2, 0, false, FROM, kError,
1696                     cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1697   }
ConvertFromUYVYBuffer()1698   void ConvertFromUYVYBuffer() {
1699     ConvertToBuffer(2, 0, false, FROM, kError,
1700                     cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1701   }
1702 
1703   // Tests ConvertFrom formats with odd stride.
ConvertFromARGBBufferStride()1704   void ConvertFromARGBBufferStride() {
1705     ConvertToBuffer(4, kOddStride, false, FROM, kError,
1706                     cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1707   }
ConvertFromBGRABufferStride()1708   void ConvertFromBGRABufferStride() {
1709     ConvertToBuffer(4, kOddStride, false, FROM, kError,
1710                     cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1711   }
ConvertFromABGRBufferStride()1712   void ConvertFromABGRBufferStride() {
1713     ConvertToBuffer(4, kOddStride, false, FROM, kError,
1714                     cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1715   }
ConvertFromRGB24BufferStride()1716   void ConvertFromRGB24BufferStride() {
1717     ConvertToBuffer(3, kOddStride, false, FROM, kError,
1718                     cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1719   }
ConvertFromRAWBufferStride()1720   void ConvertFromRAWBufferStride() {
1721     ConvertToBuffer(3, kOddStride, false, FROM, kError,
1722                     cricket::FOURCC_RAW, libyuv::RAWToI420);
1723   }
ConvertFromRGB565BufferStride()1724   void ConvertFromRGB565BufferStride() {
1725     ConvertToBuffer(2, kOddStride, false, FROM, kError,
1726                     cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1727   }
ConvertFromARGB1555BufferStride()1728   void ConvertFromARGB1555BufferStride() {
1729     ConvertToBuffer(2, kOddStride, false, FROM, kError,
1730                     cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1731   }
ConvertFromARGB4444BufferStride()1732   void ConvertFromARGB4444BufferStride() {
1733     ConvertToBuffer(2, kOddStride, false, FROM, kError,
1734                     cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1735   }
ConvertFromI400BufferStride()1736   void ConvertFromI400BufferStride() {
1737     ConvertToBuffer(1, kOddStride, false, FROM, 128,
1738                     cricket::FOURCC_I400, libyuv::I400ToI420);
1739   }
ConvertFromYUY2BufferStride()1740   void ConvertFromYUY2BufferStride() {
1741     ConvertToBuffer(2, kOddStride, false, FROM, kError,
1742                     cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1743   }
ConvertFromUYVYBufferStride()1744   void ConvertFromUYVYBufferStride() {
1745     ConvertToBuffer(2, kOddStride, false, FROM, kError,
1746                     cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1747   }
1748 
1749   // Tests ConvertFrom formats with negative stride to invert image.
ConvertFromARGBBufferInverted()1750   void ConvertFromARGBBufferInverted() {
1751     ConvertToBuffer(4, 0, true, FROM, kError,
1752                     cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1753   }
ConvertFromBGRABufferInverted()1754   void ConvertFromBGRABufferInverted() {
1755     ConvertToBuffer(4, 0, true, FROM, kError,
1756                     cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1757   }
ConvertFromABGRBufferInverted()1758   void ConvertFromABGRBufferInverted() {
1759     ConvertToBuffer(4, 0, true, FROM, kError,
1760                     cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1761   }
ConvertFromRGB24BufferInverted()1762   void ConvertFromRGB24BufferInverted() {
1763     ConvertToBuffer(3, 0, true, FROM, kError,
1764                     cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1765   }
ConvertFromRAWBufferInverted()1766   void ConvertFromRAWBufferInverted() {
1767     ConvertToBuffer(3, 0, true, FROM, kError,
1768                     cricket::FOURCC_RAW, libyuv::RAWToI420);
1769   }
ConvertFromRGB565BufferInverted()1770   void ConvertFromRGB565BufferInverted() {
1771     ConvertToBuffer(2, 0, true, FROM, kError,
1772                     cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1773   }
ConvertFromARGB1555BufferInverted()1774   void ConvertFromARGB1555BufferInverted() {
1775     ConvertToBuffer(2, 0, true, FROM, kError,
1776                     cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1777   }
ConvertFromARGB4444BufferInverted()1778   void ConvertFromARGB4444BufferInverted() {
1779     ConvertToBuffer(2, 0, true, FROM, kError,
1780                     cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1781   }
ConvertFromI400BufferInverted()1782   void ConvertFromI400BufferInverted() {
1783     ConvertToBuffer(1, 0, true, FROM, 128,
1784                     cricket::FOURCC_I400, libyuv::I400ToI420);
1785   }
ConvertFromYUY2BufferInverted()1786   void ConvertFromYUY2BufferInverted() {
1787     ConvertToBuffer(2, 0, true, FROM, kError,
1788                     cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1789   }
ConvertFromUYVYBufferInverted()1790   void ConvertFromUYVYBufferInverted() {
1791     ConvertToBuffer(2, 0, true, FROM, kError,
1792                     cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1793   }
1794 
1795   // Test converting from I420 to I422.
ConvertToI422Buffer()1796   void ConvertToI422Buffer() {
1797     T frame1, frame2;
1798     size_t out_size = kWidth * kHeight * 2;
1799     rtc::scoped_ptr<uint8_t[]> buf(new uint8_t[out_size + kAlignment]);
1800     uint8_t* y = ALIGNP(buf.get(), kAlignment);
1801     uint8_t* u = y + kWidth * kHeight;
1802     uint8_t* v = u + (kWidth / 2) * kHeight;
1803     ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1804     for (int i = 0; i < repeat_; ++i) {
1805       EXPECT_EQ(0, libyuv::I420ToI422(frame1.GetYPlane(), frame1.GetYPitch(),
1806                                       frame1.GetUPlane(), frame1.GetUPitch(),
1807                                       frame1.GetVPlane(), frame1.GetVPitch(),
1808                                       y, kWidth,
1809                                       u, kWidth / 2,
1810                                       v, kWidth / 2,
1811                                       kWidth, kHeight));
1812     }
1813     EXPECT_TRUE(frame2.Init(cricket::FOURCC_I422, kWidth, kHeight, kWidth,
1814                             kHeight, y, out_size, 1, 1, 0,
1815                             webrtc::kVideoRotation_0));
1816     EXPECT_TRUE(IsEqual(frame1, frame2, 1));
1817   }
1818 
1819   ///////////////////
1820   // General tests //
1821   ///////////////////
1822 
Copy()1823   void Copy() {
1824     rtc::scoped_ptr<T> source(new T);
1825     rtc::scoped_ptr<cricket::VideoFrame> target;
1826     ASSERT_TRUE(LoadFrameNoRepeat(source.get()));
1827     target.reset(source->Copy());
1828     EXPECT_TRUE(IsEqual(*source, *target, 0));
1829     source.reset();
1830     EXPECT_TRUE(target->GetYPlane() != NULL);
1831   }
1832 
CopyIsRef()1833   void CopyIsRef() {
1834     rtc::scoped_ptr<T> source(new T);
1835     rtc::scoped_ptr<const cricket::VideoFrame> target;
1836     ASSERT_TRUE(LoadFrameNoRepeat(source.get()));
1837     target.reset(source->Copy());
1838     EXPECT_TRUE(IsEqual(*source, *target, 0));
1839     const T* const_source = source.get();
1840     EXPECT_EQ(const_source->GetYPlane(), target->GetYPlane());
1841     EXPECT_EQ(const_source->GetUPlane(), target->GetUPlane());
1842     EXPECT_EQ(const_source->GetVPlane(), target->GetVPlane());
1843   }
1844 
MakeExclusive()1845   void MakeExclusive() {
1846     rtc::scoped_ptr<T> source(new T);
1847     rtc::scoped_ptr<cricket::VideoFrame> target;
1848     ASSERT_TRUE(LoadFrameNoRepeat(source.get()));
1849     target.reset(source->Copy());
1850     EXPECT_TRUE(target->MakeExclusive());
1851     EXPECT_TRUE(IsEqual(*source, *target, 0));
1852     EXPECT_NE(target->GetYPlane(), source->GetYPlane());
1853     EXPECT_NE(target->GetUPlane(), source->GetUPlane());
1854     EXPECT_NE(target->GetVPlane(), source->GetVPlane());
1855   }
1856 
CopyToBuffer()1857   void CopyToBuffer() {
1858     T frame;
1859     rtc::scoped_ptr<rtc::MemoryStream> ms(
1860         LoadSample(kImageFilename));
1861     ASSERT_TRUE(ms.get() != NULL);
1862     ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight,
1863                           &frame));
1864     size_t out_size = kWidth * kHeight * 3 / 2;
1865     rtc::scoped_ptr<uint8_t[]> out(new uint8_t[out_size]);
1866     for (int i = 0; i < repeat_; ++i) {
1867       EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size));
1868     }
1869     EXPECT_EQ(0, memcmp(out.get(), ms->GetBuffer(), out_size));
1870   }
1871 
CopyToFrame()1872   void CopyToFrame() {
1873     T source;
1874     rtc::scoped_ptr<rtc::MemoryStream> ms(
1875         LoadSample(kImageFilename));
1876     ASSERT_TRUE(ms.get() != NULL);
1877     ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight,
1878                           &source));
1879 
1880     // Create the target frame by loading from a file.
1881     T target;
1882     ASSERT_TRUE(LoadFrameNoRepeat(&target));
1883     EXPECT_FALSE(IsBlack(target));
1884 
1885     // Stretch and check if the stretched target is black.
1886     source.CopyToFrame(&target);
1887 
1888     EXPECT_TRUE(IsEqual(source, target, 0));
1889   }
1890 
Write()1891   void Write() {
1892     T frame;
1893     rtc::scoped_ptr<rtc::MemoryStream> ms(
1894         LoadSample(kImageFilename));
1895     ASSERT_TRUE(ms.get() != NULL);
1896     rtc::MemoryStream ms2;
1897     size_t size;
1898     ASSERT_TRUE(ms->GetSize(&size));
1899     ASSERT_TRUE(ms2.ReserveSize(size));
1900     ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight,
1901                           &frame));
1902     for (int i = 0; i < repeat_; ++i) {
1903       ms2.SetPosition(0u);  // Useful when repeat_ > 1.
1904       int error;
1905       EXPECT_EQ(rtc::SR_SUCCESS, frame.Write(&ms2, &error));
1906     }
1907     size_t out_size = cricket::VideoFrame::SizeOf(kWidth, kHeight);
1908     EXPECT_EQ(0, memcmp(ms2.GetBuffer(), ms->GetBuffer(), out_size));
1909   }
1910 
CopyToBuffer1Pixel()1911   void CopyToBuffer1Pixel() {
1912     size_t out_size = 3;
1913     rtc::scoped_ptr<uint8_t[]> out(new uint8_t[out_size + 1]);
1914     memset(out.get(), 0xfb, out_size + 1);  // Fill buffer
1915     uint8_t pixel[3] = {1, 2, 3};
1916     T frame;
1917     EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 1, 1, 1, 1, pixel,
1918                            sizeof(pixel), 1, 1, 0,
1919                            webrtc::kVideoRotation_0));
1920     for (int i = 0; i < repeat_; ++i) {
1921       EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size));
1922     }
1923     EXPECT_EQ(1, out.get()[0]);  // Check Y.  Should be 1.
1924     EXPECT_EQ(2, out.get()[1]);  // Check U.  Should be 2.
1925     EXPECT_EQ(3, out.get()[2]);  // Check V.  Should be 3.
1926     EXPECT_EQ(0xfb, out.get()[3]);  // Check sentinel is still intact.
1927   }
1928 
StretchToFrame()1929   void StretchToFrame() {
1930     // Create the source frame as a black frame.
1931     T source;
1932     EXPECT_TRUE(source.InitToBlack(kWidth * 2, kHeight * 2, 1, 1, 0));
1933     EXPECT_TRUE(IsSize(source, kWidth * 2, kHeight * 2));
1934 
1935     // Create the target frame by loading from a file.
1936     T target1;
1937     ASSERT_TRUE(LoadFrameNoRepeat(&target1));
1938     EXPECT_FALSE(IsBlack(target1));
1939 
1940     // Stretch and check if the stretched target is black.
1941     source.StretchToFrame(&target1, true, false);
1942     EXPECT_TRUE(IsBlack(target1));
1943 
1944     // Crop and stretch and check if the stretched target is black.
1945     T target2;
1946     ASSERT_TRUE(LoadFrameNoRepeat(&target2));
1947     source.StretchToFrame(&target2, true, true);
1948     EXPECT_TRUE(IsBlack(target2));
1949     EXPECT_EQ(source.GetTimeStamp(), target2.GetTimeStamp());
1950   }
1951 
1952   int repeat_;
1953 };
1954 
1955 #endif  // TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_
1956