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