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.
60template <class T>
61class VideoFrameTest : public testing::Test {
62 public:
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.
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
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  }
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.
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  }
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.
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  }
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
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.
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_array<uint8> out(new uint8[out_size]);
161    frame.CopyToBuffer(out.get(), out_size);
162    return DumpSample(filename, out.get(), out_size);
163  }
164
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.
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.
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
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.
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.
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.
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
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
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.
378  static bool IsNull(const cricket::VideoFrame& frame) {
379    return !frame.GetYPlane();
380  }
381
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
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
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
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
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
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.
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.
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.
513  void ConstructI422() {
514    T frame1, frame2;
515    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
516    size_t buf_size = kWidth * kHeight * 2;
517    talk_base::scoped_array<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, 0));
531  }
532
533  // Test constructing an image from a YUY2 buffer.
534  void ConstructYuy2() {
535    T frame1, frame2;
536    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
537    size_t buf_size = kWidth * kHeight * 2;
538    talk_base::scoped_array<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.
551  void ConstructYuy2Unaligned() {
552    T frame1, frame2;
553    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
554    size_t buf_size = kWidth * kHeight * 2;
555    talk_base::scoped_array<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
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.
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.
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
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
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
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.
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.
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
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.
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.
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.
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
718  void ConstructRGB565() {
719    T frame1, frame2;
720    size_t out_size = kWidth * kHeight * 2;
721    talk_base::scoped_array<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
734  void ConstructARGB1555() {
735    T frame1, frame2;
736    size_t out_size = kWidth * kHeight * 2;
737    talk_base::scoped_array<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
750  void ConstructARGB4444() {
751    T frame1, frame2;
752    size_t out_size = kWidth * kHeight * 2;
753    talk_base::scoped_array<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_array<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.
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)                                               \
799void 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)                                       \
832void 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.
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.
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.
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.
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.
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.
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.
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.
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_array<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.
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_array<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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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    const uint8* sample = reinterpret_cast<const uint8*>(ms.get()->GetBuffer());
1158    size_t sample_size;
1159    ms->GetSize(&sample_size);
1160    // Optional adjust size to test invalid size.
1161    size_t data_size = sample_size + data_adjust;
1162
1163    // Allocate a buffer with end page aligned.
1164    const int kPadToHeapSized = 16 * 1024 * 1024;
1165    talk_base::scoped_array<uint8> page_buffer(
1166        new uint8[((data_size + kPadToHeapSized + 4095) & ~4095)]);
1167    uint8* data_ptr = page_buffer.get();
1168    if (!data_ptr) {
1169      LOG(LS_WARNING) << "Failed to allocate memory for ValidateFrame test.";
1170      EXPECT_FALSE(expected_result);  // NULL is okay if failure was expected.
1171      return;
1172    }
1173    data_ptr += kPadToHeapSized + (-(static_cast<int>(data_size)) & 4095);
1174    memcpy(data_ptr, sample, talk_base::_min(data_size, sample_size));
1175    for (int i = 0; i < repeat_; ++i) {
1176      EXPECT_EQ(expected_result, frame.Validate(fourcc, kWidth, kHeight,
1177                                                data_ptr,
1178                                                sample_size + size_adjust));
1179    }
1180  }
1181
1182  // Test validate for I420 MJPG buffer.
1183  void ValidateMjpgI420() {
1184    ValidateFrame(kJpeg420Filename, cricket::FOURCC_MJPG, 0, 0, true);
1185  }
1186
1187  // Test validate for I422 MJPG buffer.
1188  void ValidateMjpgI422() {
1189    ValidateFrame(kJpeg422Filename, cricket::FOURCC_MJPG, 0, 0, true);
1190  }
1191
1192  // Test validate for I444 MJPG buffer.
1193  void ValidateMjpgI444() {
1194    ValidateFrame(kJpeg444Filename, cricket::FOURCC_MJPG, 0, 0, true);
1195  }
1196
1197  // Test validate for I411 MJPG buffer.
1198  void ValidateMjpgI411() {
1199    ValidateFrame(kJpeg411Filename, cricket::FOURCC_MJPG, 0, 0, true);
1200  }
1201
1202  // Test validate for I400 MJPG buffer.
1203  void ValidateMjpgI400() {
1204    ValidateFrame(kJpeg400Filename, cricket::FOURCC_MJPG, 0, 0, true);
1205  }
1206
1207  // Test validate for I420 buffer.
1208  void ValidateI420() {
1209    ValidateFrame(kImageFilename, cricket::FOURCC_I420, 0, 0, true);
1210  }
1211
1212  // Test validate for I420 buffer where size is too small
1213  void ValidateI420SmallSize() {
1214    ValidateFrame(kImageFilename, cricket::FOURCC_I420, 0, -16384, false);
1215  }
1216
1217  // Test validate for I420 buffer where size is too large (16 MB)
1218  // Will produce warning but pass.
1219  void ValidateI420LargeSize() {
1220    ValidateFrame(kImageFilename, cricket::FOURCC_I420, 16000000, 16000000,
1221                  true);
1222  }
1223
1224  // Test validate for I420 buffer where size is 1 GB (not reasonable).
1225  void ValidateI420HugeSize() {
1226#ifndef WIN32  // TODO(fbarchard): Reenable when fixing bug 9603762.
1227    ValidateFrame(kImageFilename, cricket::FOURCC_I420, 1000000000u,
1228                  1000000000u, false);
1229#endif
1230  }
1231
1232  // The following test that Validate crashes if the size is greater than the
1233  // actual buffer size.
1234  // TODO(fbarchard): Consider moving a filter into the capturer/plugin.
1235#if defined(_MSC_VER) && defined(_DEBUG)
1236  int ExceptionFilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
1237    if (code == EXCEPTION_ACCESS_VIOLATION) {
1238      LOG(LS_INFO) << "Caught EXCEPTION_ACCESS_VIOLATION as expected.";
1239      return EXCEPTION_EXECUTE_HANDLER;
1240    } else {
1241      LOG(LS_INFO) << "Did not catch EXCEPTION_ACCESS_VIOLATION.  Unexpected.";
1242      return EXCEPTION_CONTINUE_SEARCH;
1243    }
1244  }
1245
1246  // Test validate fails for truncated MJPG data buffer.  If ValidateFrame
1247  // crashes the exception handler will return and unittest passes with OK.
1248  void ValidateMjpgI420InvalidSize() {
1249    __try {
1250      ValidateFrame(kJpeg420Filename, cricket::FOURCC_MJPG, -16384, 0, false);
1251      FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION.";
1252    } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
1253      return;  // Successfully crashed in ValidateFrame.
1254    }
1255  }
1256
1257  // Test validate fails for truncated I420 buffer.
1258  void ValidateI420InvalidSize() {
1259    __try {
1260      ValidateFrame(kImageFilename, cricket::FOURCC_I420, -16384, 0, false);
1261      FAIL() << "Validate was expected to cause EXCEPTION_ACCESS_VIOLATION.";
1262    } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
1263      return;  // Successfully crashed in ValidateFrame.
1264    }
1265  }
1266#endif
1267
1268  // Test constructing an image from a YUY2 buffer (and synonymous formats).
1269  void ConstructYuy2Aliases() {
1270    T frame1, frame2, frame3, frame4;
1271    talk_base::scoped_ptr<talk_base::MemoryStream> ms(
1272        CreateYuv422Sample(cricket::FOURCC_YUY2, kWidth, kHeight));
1273    ASSERT_TRUE(ms.get() != NULL);
1274    EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, kWidth, kHeight,
1275                              &frame1));
1276    EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2,
1277                          kWidth, kHeight, &frame2));
1278    EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUVS,
1279                          kWidth, kHeight, &frame3));
1280    EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUYV,
1281                          kWidth, kHeight, &frame4));
1282    EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1283    EXPECT_TRUE(IsEqual(frame1, frame3, 0));
1284    EXPECT_TRUE(IsEqual(frame1, frame4, 0));
1285  }
1286
1287  // Test constructing an image from a UYVY buffer (and synonymous formats).
1288  void ConstructUyvyAliases() {
1289    T frame1, frame2, frame3, frame4;
1290    talk_base::scoped_ptr<talk_base::MemoryStream> ms(
1291        CreateYuv422Sample(cricket::FOURCC_UYVY, kWidth, kHeight));
1292    ASSERT_TRUE(ms.get() != NULL);
1293    EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_UYVY, kWidth, kHeight,
1294                              &frame1));
1295    EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_UYVY,
1296                          kWidth, kHeight, &frame2));
1297    EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_2VUY,
1298                          kWidth, kHeight, &frame3));
1299    EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_HDYC,
1300                          kWidth, kHeight, &frame4));
1301    EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1302    EXPECT_TRUE(IsEqual(frame1, frame3, 0));
1303    EXPECT_TRUE(IsEqual(frame1, frame4, 0));
1304  }
1305
1306  // Test creating a copy.
1307  void ConstructCopy() {
1308    T frame1, frame2;
1309    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1310    for (int i = 0; i < repeat_; ++i) {
1311      EXPECT_TRUE(frame2.Init(frame1));
1312    }
1313    EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1314  }
1315
1316  // Test creating a copy and check that it just increments the refcount.
1317  void ConstructCopyIsRef() {
1318    T frame1, frame2;
1319    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1320    for (int i = 0; i < repeat_; ++i) {
1321      EXPECT_TRUE(frame2.Init(frame1));
1322    }
1323    EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1324    EXPECT_EQ(frame1.GetYPlane(), frame2.GetYPlane());
1325    EXPECT_EQ(frame1.GetUPlane(), frame2.GetUPlane());
1326    EXPECT_EQ(frame1.GetVPlane(), frame2.GetVPlane());
1327  }
1328
1329  // Test creating an empty image and initing it to black.
1330  void ConstructBlack() {
1331    T frame;
1332    for (int i = 0; i < repeat_; ++i) {
1333      EXPECT_TRUE(frame.InitToBlack(kWidth, kHeight, 1, 1, 0, 0));
1334    }
1335    EXPECT_TRUE(IsSize(frame, kWidth, kHeight));
1336    EXPECT_TRUE(IsBlack(frame));
1337  }
1338
1339  // Test constructing an image from a YUY2 buffer with a range of sizes.
1340  // Only tests that conversion does not crash or corrupt heap.
1341  void ConstructYuy2AllSizes() {
1342    T frame1, frame2;
1343    for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) {
1344      for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) {
1345        talk_base::scoped_ptr<talk_base::MemoryStream> ms(
1346            CreateYuv422Sample(cricket::FOURCC_YUY2, width, height));
1347        ASSERT_TRUE(ms.get() != NULL);
1348        EXPECT_TRUE(ConvertYuv422(ms.get(), cricket::FOURCC_YUY2, width, height,
1349                                  &frame1));
1350        EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_YUY2,
1351                              width, height, &frame2));
1352        EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1353      }
1354    }
1355  }
1356
1357  // Test constructing an image from a ARGB buffer with a range of sizes.
1358  // Only tests that conversion does not crash or corrupt heap.
1359  void ConstructARGBAllSizes() {
1360    T frame1, frame2;
1361    for (int height = kMinHeightAll; height <= kMaxHeightAll; ++height) {
1362      for (int width = kMinWidthAll; width <= kMaxWidthAll; ++width) {
1363        talk_base::scoped_ptr<talk_base::MemoryStream> ms(
1364            CreateRgbSample(cricket::FOURCC_ARGB, width, height));
1365        ASSERT_TRUE(ms.get() != NULL);
1366        EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB, width, height,
1367                               &frame1));
1368        EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB,
1369                              width, height, &frame2));
1370        EXPECT_TRUE(IsEqual(frame1, frame2, 64));
1371      }
1372    }
1373    // Test a practical window size for screencasting usecase.
1374    const int kOddWidth = 1228;
1375    const int kOddHeight = 260;
1376    for (int j = 0; j < 2; ++j) {
1377      for (int i = 0; i < 2; ++i) {
1378        talk_base::scoped_ptr<talk_base::MemoryStream> ms(
1379        CreateRgbSample(cricket::FOURCC_ARGB, kOddWidth + i, kOddHeight + j));
1380        ASSERT_TRUE(ms.get() != NULL);
1381        EXPECT_TRUE(ConvertRgb(ms.get(), cricket::FOURCC_ARGB,
1382                               kOddWidth + i, kOddHeight + j,
1383                               &frame1));
1384        EXPECT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_ARGB,
1385                              kOddWidth + i, kOddHeight + j, &frame2));
1386        EXPECT_TRUE(IsEqual(frame1, frame2, 64));
1387      }
1388    }
1389  }
1390
1391  // Tests re-initing an existing image.
1392  void Reset() {
1393    T frame1, frame2;
1394    talk_base::scoped_ptr<talk_base::MemoryStream> ms(
1395        LoadSample(kImageFilename));
1396    size_t data_size;
1397    ms->GetSize(&data_size);
1398    EXPECT_TRUE(frame1.InitToBlack(kWidth, kHeight, 1, 1, 0, 0));
1399    EXPECT_TRUE(frame2.InitToBlack(kWidth, kHeight, 1, 1, 0, 0));
1400    EXPECT_TRUE(IsBlack(frame1));
1401    EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1402    EXPECT_TRUE(frame1.Reset(cricket::FOURCC_I420,
1403                             kWidth, kHeight, kWidth, kHeight,
1404                             reinterpret_cast<uint8*>(ms->GetBuffer()),
1405                             data_size, 1, 1, 0, 0, 0));
1406    EXPECT_FALSE(IsBlack(frame1));
1407    EXPECT_FALSE(IsEqual(frame1, frame2, 0));
1408  }
1409
1410  //////////////////////
1411  // Conversion tests //
1412  //////////////////////
1413
1414  enum ToFrom { TO, FROM };
1415
1416  // Helper function for test converting from I420 to packed formats.
1417  inline void ConvertToBuffer(int bpp, int rowpad, bool invert, ToFrom to_from,
1418      int error, uint32 fourcc,
1419      int (*RGBToI420)(const uint8* src_frame, int src_stride_frame,
1420                       uint8* dst_y, int dst_stride_y,
1421                       uint8* dst_u, int dst_stride_u,
1422                       uint8* dst_v, int dst_stride_v,
1423                       int width, int height)) {
1424    T frame1, frame2;
1425    int repeat_to = (to_from == TO) ? repeat_ : 1;
1426    int repeat_from  = (to_from == FROM) ? repeat_ : 1;
1427
1428    int astride = kWidth * bpp + rowpad;
1429    size_t out_size = astride * kHeight;
1430    talk_base::scoped_array<uint8> outbuf(new uint8[out_size + kAlignment + 1]);
1431    memset(outbuf.get(), 0, out_size + kAlignment + 1);
1432    uint8 *outtop = ALIGNP(outbuf.get(), kAlignment);
1433    uint8 *out = outtop;
1434    int stride = astride;
1435    if (invert) {
1436      out += (kHeight - 1) * stride;  // Point to last row.
1437      stride = -stride;
1438    }
1439    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1440
1441    for (int i = 0; i < repeat_to; ++i) {
1442      EXPECT_EQ(out_size, frame1.ConvertToRgbBuffer(fourcc,
1443                                                    out,
1444                                                    out_size, stride));
1445    }
1446    EXPECT_TRUE(frame2.InitToBlack(kWidth, kHeight, 1, 1, 0, 0));
1447    for (int i = 0; i < repeat_from; ++i) {
1448      EXPECT_EQ(0, RGBToI420(out, stride,
1449                             frame2.GetYPlane(), frame2.GetYPitch(),
1450                             frame2.GetUPlane(), frame2.GetUPitch(),
1451                             frame2.GetVPlane(), frame2.GetVPitch(),
1452                             kWidth, kHeight));
1453    }
1454    if (rowpad) {
1455      EXPECT_EQ(0, outtop[kWidth * bpp]);  // Ensure stride skipped end of row.
1456      EXPECT_NE(0, outtop[astride]);       // Ensure pixel at start of 2nd row.
1457    } else {
1458      EXPECT_NE(0, outtop[kWidth * bpp]);  // Expect something to be here.
1459    }
1460    EXPECT_EQ(0, outtop[out_size]);      // Ensure no overrun.
1461    EXPECT_TRUE(IsEqual(frame1, frame2, error));
1462  }
1463
1464  static const int kError = 20;
1465  static const int kErrorHigh = 40;
1466  static const int kOddStride = 23;
1467
1468  // Tests ConvertToRGBBuffer formats.
1469  void ConvertToARGBBuffer() {
1470    ConvertToBuffer(4, 0, false, TO, kError,
1471                    cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1472  }
1473  void ConvertToBGRABuffer() {
1474    ConvertToBuffer(4, 0, false, TO, kError,
1475                    cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1476  }
1477  void ConvertToABGRBuffer() {
1478    ConvertToBuffer(4, 0, false, TO, kError,
1479                    cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1480  }
1481  void ConvertToRGB24Buffer() {
1482    ConvertToBuffer(3, 0, false, TO, kError,
1483                    cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1484  }
1485  void ConvertToRAWBuffer() {
1486    ConvertToBuffer(3, 0, false, TO, kError,
1487                    cricket::FOURCC_RAW, libyuv::RAWToI420);
1488  }
1489  void ConvertToRGB565Buffer() {
1490    ConvertToBuffer(2, 0, false, TO, kError,
1491                    cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1492  }
1493  void ConvertToARGB1555Buffer() {
1494    ConvertToBuffer(2, 0, false, TO, kError,
1495                    cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1496  }
1497  void ConvertToARGB4444Buffer() {
1498    ConvertToBuffer(2, 0, false, TO, kError,
1499                    cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1500  }
1501  void ConvertToBayerBGGRBuffer() {
1502    ConvertToBuffer(1, 0, false, TO, kErrorHigh,
1503                    cricket::FOURCC_BGGR, libyuv::BayerBGGRToI420);
1504  }
1505  void ConvertToBayerGBRGBuffer() {
1506    ConvertToBuffer(1, 0, false, TO, kErrorHigh,
1507                    cricket::FOURCC_GBRG, libyuv::BayerGBRGToI420);
1508  }
1509  void ConvertToBayerGRBGBuffer() {
1510    ConvertToBuffer(1, 0, false, TO, kErrorHigh,
1511                    cricket::FOURCC_GRBG, libyuv::BayerGRBGToI420);
1512  }
1513  void ConvertToBayerRGGBBuffer() {
1514    ConvertToBuffer(1, 0, false, TO, kErrorHigh,
1515                    cricket::FOURCC_RGGB, libyuv::BayerRGGBToI420);
1516  }
1517  void ConvertToI400Buffer() {
1518    ConvertToBuffer(1, 0, false, TO, 128,
1519                    cricket::FOURCC_I400, libyuv::I400ToI420);
1520  }
1521  void ConvertToYUY2Buffer() {
1522    ConvertToBuffer(2, 0, false, TO, kError,
1523                    cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1524  }
1525  void ConvertToUYVYBuffer() {
1526    ConvertToBuffer(2, 0, false, TO, kError,
1527                    cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1528  }
1529
1530  // Tests ConvertToRGBBuffer formats with odd stride.
1531  void ConvertToARGBBufferStride() {
1532    ConvertToBuffer(4, kOddStride, false, TO, kError,
1533                    cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1534  }
1535  void ConvertToBGRABufferStride() {
1536    ConvertToBuffer(4, kOddStride, false, TO, kError,
1537                    cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1538  }
1539  void ConvertToABGRBufferStride() {
1540    ConvertToBuffer(4, kOddStride, false, TO, kError,
1541                    cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1542  }
1543  void ConvertToRGB24BufferStride() {
1544    ConvertToBuffer(3, kOddStride, false, TO, kError,
1545                    cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1546  }
1547  void ConvertToRAWBufferStride() {
1548    ConvertToBuffer(3, kOddStride, false, TO, kError,
1549                    cricket::FOURCC_RAW, libyuv::RAWToI420);
1550  }
1551  void ConvertToRGB565BufferStride() {
1552    ConvertToBuffer(2, kOddStride, false, TO, kError,
1553                    cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1554  }
1555  void ConvertToARGB1555BufferStride() {
1556    ConvertToBuffer(2, kOddStride, false, TO, kError,
1557                    cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1558  }
1559  void ConvertToARGB4444BufferStride() {
1560    ConvertToBuffer(2, kOddStride, false, TO, kError,
1561                    cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1562  }
1563  void ConvertToBayerBGGRBufferStride() {
1564    ConvertToBuffer(1, kOddStride, false, TO, kErrorHigh,
1565                    cricket::FOURCC_BGGR, libyuv::BayerBGGRToI420);
1566  }
1567  void ConvertToBayerGBRGBufferStride() {
1568    ConvertToBuffer(1, kOddStride, false, TO, kErrorHigh,
1569                    cricket::FOURCC_GBRG, libyuv::BayerGBRGToI420);
1570  }
1571  void ConvertToBayerGRBGBufferStride() {
1572    ConvertToBuffer(1, kOddStride, false, TO, kErrorHigh,
1573                    cricket::FOURCC_GRBG, libyuv::BayerGRBGToI420);
1574  }
1575  void ConvertToBayerRGGBBufferStride() {
1576    ConvertToBuffer(1, kOddStride, false, TO, kErrorHigh,
1577                    cricket::FOURCC_RGGB, libyuv::BayerRGGBToI420);
1578  }
1579  void ConvertToI400BufferStride() {
1580    ConvertToBuffer(1, kOddStride, false, TO, 128,
1581                    cricket::FOURCC_I400, libyuv::I400ToI420);
1582  }
1583  void ConvertToYUY2BufferStride() {
1584    ConvertToBuffer(2, kOddStride, false, TO, kError,
1585                    cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1586  }
1587  void ConvertToUYVYBufferStride() {
1588    ConvertToBuffer(2, kOddStride, false, TO, kError,
1589                    cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1590  }
1591
1592  // Tests ConvertToRGBBuffer formats with negative stride to invert image.
1593  void ConvertToARGBBufferInverted() {
1594    ConvertToBuffer(4, 0, true, TO, kError,
1595                    cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1596  }
1597  void ConvertToBGRABufferInverted() {
1598    ConvertToBuffer(4, 0, true, TO, kError,
1599                    cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1600  }
1601  void ConvertToABGRBufferInverted() {
1602    ConvertToBuffer(4, 0, true, TO, kError,
1603                    cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1604  }
1605  void ConvertToRGB24BufferInverted() {
1606    ConvertToBuffer(3, 0, true, TO, kError,
1607                    cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1608  }
1609  void ConvertToRAWBufferInverted() {
1610    ConvertToBuffer(3, 0, true, TO, kError,
1611                    cricket::FOURCC_RAW, libyuv::RAWToI420);
1612  }
1613  void ConvertToRGB565BufferInverted() {
1614    ConvertToBuffer(2, 0, true, TO, kError,
1615                    cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1616  }
1617  void ConvertToARGB1555BufferInverted() {
1618    ConvertToBuffer(2, 0, true, TO, kError,
1619                    cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1620  }
1621  void ConvertToARGB4444BufferInverted() {
1622    ConvertToBuffer(2, 0, true, TO, kError,
1623                    cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1624  }
1625  void ConvertToBayerBGGRBufferInverted() {
1626    ConvertToBuffer(1, 0, true, TO, kErrorHigh,
1627                    cricket::FOURCC_BGGR, libyuv::BayerBGGRToI420);
1628  }
1629  void ConvertToBayerGBRGBufferInverted() {
1630    ConvertToBuffer(1, 0, true, TO, kErrorHigh,
1631                    cricket::FOURCC_GBRG, libyuv::BayerGBRGToI420);
1632  }
1633  void ConvertToBayerGRBGBufferInverted() {
1634    ConvertToBuffer(1, 0, true, TO, kErrorHigh,
1635                    cricket::FOURCC_GRBG, libyuv::BayerGRBGToI420);
1636  }
1637  void ConvertToBayerRGGBBufferInverted() {
1638    ConvertToBuffer(1, 0, true, TO, kErrorHigh,
1639                    cricket::FOURCC_RGGB, libyuv::BayerRGGBToI420);
1640  }
1641  void ConvertToI400BufferInverted() {
1642    ConvertToBuffer(1, 0, true, TO, 128,
1643                    cricket::FOURCC_I400, libyuv::I400ToI420);
1644  }
1645  void ConvertToYUY2BufferInverted() {
1646    ConvertToBuffer(2, 0, true, TO, kError,
1647                    cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1648  }
1649  void ConvertToUYVYBufferInverted() {
1650    ConvertToBuffer(2, 0, true, TO, kError,
1651                    cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1652  }
1653
1654  // Tests ConvertFrom formats.
1655  void ConvertFromARGBBuffer() {
1656    ConvertToBuffer(4, 0, false, FROM, kError,
1657                    cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1658  }
1659  void ConvertFromBGRABuffer() {
1660    ConvertToBuffer(4, 0, false, FROM, kError,
1661                    cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1662  }
1663  void ConvertFromABGRBuffer() {
1664    ConvertToBuffer(4, 0, false, FROM, kError,
1665                    cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1666  }
1667  void ConvertFromRGB24Buffer() {
1668    ConvertToBuffer(3, 0, false, FROM, kError,
1669                    cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1670  }
1671  void ConvertFromRAWBuffer() {
1672    ConvertToBuffer(3, 0, false, FROM, kError,
1673                    cricket::FOURCC_RAW, libyuv::RAWToI420);
1674  }
1675  void ConvertFromRGB565Buffer() {
1676    ConvertToBuffer(2, 0, false, FROM, kError,
1677                    cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1678  }
1679  void ConvertFromARGB1555Buffer() {
1680    ConvertToBuffer(2, 0, false, FROM, kError,
1681                    cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1682  }
1683  void ConvertFromARGB4444Buffer() {
1684    ConvertToBuffer(2, 0, false, FROM, kError,
1685                    cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1686  }
1687  void ConvertFromBayerBGGRBuffer() {
1688    ConvertToBuffer(1, 0, false, FROM, kErrorHigh,
1689                    cricket::FOURCC_BGGR, libyuv::BayerBGGRToI420);
1690  }
1691  void ConvertFromBayerGBRGBuffer() {
1692    ConvertToBuffer(1, 0, false, FROM, kErrorHigh,
1693                    cricket::FOURCC_GBRG, libyuv::BayerGBRGToI420);
1694  }
1695  void ConvertFromBayerGRBGBuffer() {
1696    ConvertToBuffer(1, 0, false, FROM, kErrorHigh,
1697                    cricket::FOURCC_GRBG, libyuv::BayerGRBGToI420);
1698  }
1699  void ConvertFromBayerRGGBBuffer() {
1700    ConvertToBuffer(1, 0, false, FROM, kErrorHigh,
1701                    cricket::FOURCC_RGGB, libyuv::BayerRGGBToI420);
1702  }
1703  void ConvertFromI400Buffer() {
1704    ConvertToBuffer(1, 0, false, FROM, 128,
1705                    cricket::FOURCC_I400, libyuv::I400ToI420);
1706  }
1707  void ConvertFromYUY2Buffer() {
1708    ConvertToBuffer(2, 0, false, FROM, kError,
1709                    cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1710  }
1711  void ConvertFromUYVYBuffer() {
1712    ConvertToBuffer(2, 0, false, FROM, kError,
1713                    cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1714  }
1715
1716  // Tests ConvertFrom formats with odd stride.
1717  void ConvertFromARGBBufferStride() {
1718    ConvertToBuffer(4, kOddStride, false, FROM, kError,
1719                    cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1720  }
1721  void ConvertFromBGRABufferStride() {
1722    ConvertToBuffer(4, kOddStride, false, FROM, kError,
1723                    cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1724  }
1725  void ConvertFromABGRBufferStride() {
1726    ConvertToBuffer(4, kOddStride, false, FROM, kError,
1727                    cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1728  }
1729  void ConvertFromRGB24BufferStride() {
1730    ConvertToBuffer(3, kOddStride, false, FROM, kError,
1731                    cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1732  }
1733  void ConvertFromRAWBufferStride() {
1734    ConvertToBuffer(3, kOddStride, false, FROM, kError,
1735                    cricket::FOURCC_RAW, libyuv::RAWToI420);
1736  }
1737  void ConvertFromRGB565BufferStride() {
1738    ConvertToBuffer(2, kOddStride, false, FROM, kError,
1739                    cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1740  }
1741  void ConvertFromARGB1555BufferStride() {
1742    ConvertToBuffer(2, kOddStride, false, FROM, kError,
1743                    cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1744  }
1745  void ConvertFromARGB4444BufferStride() {
1746    ConvertToBuffer(2, kOddStride, false, FROM, kError,
1747                    cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1748  }
1749  void ConvertFromBayerBGGRBufferStride() {
1750    ConvertToBuffer(1, kOddStride, false, FROM, kErrorHigh,
1751                    cricket::FOURCC_BGGR, libyuv::BayerBGGRToI420);
1752  }
1753  void ConvertFromBayerGBRGBufferStride() {
1754    ConvertToBuffer(1, kOddStride, false, FROM, kErrorHigh,
1755                    cricket::FOURCC_GBRG, libyuv::BayerGBRGToI420);
1756  }
1757  void ConvertFromBayerGRBGBufferStride() {
1758    ConvertToBuffer(1, kOddStride, false, FROM, kErrorHigh,
1759                    cricket::FOURCC_GRBG, libyuv::BayerGRBGToI420);
1760  }
1761  void ConvertFromBayerRGGBBufferStride() {
1762    ConvertToBuffer(1, kOddStride, false, FROM, kErrorHigh,
1763                    cricket::FOURCC_RGGB, libyuv::BayerRGGBToI420);
1764  }
1765  void ConvertFromI400BufferStride() {
1766    ConvertToBuffer(1, kOddStride, false, FROM, 128,
1767                    cricket::FOURCC_I400, libyuv::I400ToI420);
1768  }
1769  void ConvertFromYUY2BufferStride() {
1770    ConvertToBuffer(2, kOddStride, false, FROM, kError,
1771                    cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1772  }
1773  void ConvertFromUYVYBufferStride() {
1774    ConvertToBuffer(2, kOddStride, false, FROM, kError,
1775                    cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1776  }
1777
1778  // Tests ConvertFrom formats with negative stride to invert image.
1779  void ConvertFromARGBBufferInverted() {
1780    ConvertToBuffer(4, 0, true, FROM, kError,
1781                    cricket::FOURCC_ARGB, libyuv::ARGBToI420);
1782  }
1783  void ConvertFromBGRABufferInverted() {
1784    ConvertToBuffer(4, 0, true, FROM, kError,
1785                    cricket::FOURCC_BGRA, libyuv::BGRAToI420);
1786  }
1787  void ConvertFromABGRBufferInverted() {
1788    ConvertToBuffer(4, 0, true, FROM, kError,
1789                    cricket::FOURCC_ABGR, libyuv::ABGRToI420);
1790  }
1791  void ConvertFromRGB24BufferInverted() {
1792    ConvertToBuffer(3, 0, true, FROM, kError,
1793                    cricket::FOURCC_24BG, libyuv::RGB24ToI420);
1794  }
1795  void ConvertFromRAWBufferInverted() {
1796    ConvertToBuffer(3, 0, true, FROM, kError,
1797                    cricket::FOURCC_RAW, libyuv::RAWToI420);
1798  }
1799  void ConvertFromRGB565BufferInverted() {
1800    ConvertToBuffer(2, 0, true, FROM, kError,
1801                    cricket::FOURCC_RGBP, libyuv::RGB565ToI420);
1802  }
1803  void ConvertFromARGB1555BufferInverted() {
1804    ConvertToBuffer(2, 0, true, FROM, kError,
1805                    cricket::FOURCC_RGBO, libyuv::ARGB1555ToI420);
1806  }
1807  void ConvertFromARGB4444BufferInverted() {
1808    ConvertToBuffer(2, 0, true, FROM, kError,
1809                    cricket::FOURCC_R444, libyuv::ARGB4444ToI420);
1810  }
1811  void ConvertFromBayerBGGRBufferInverted() {
1812    ConvertToBuffer(1, 0, true, FROM, kErrorHigh,
1813                    cricket::FOURCC_BGGR, libyuv::BayerBGGRToI420);
1814  }
1815  void ConvertFromBayerGBRGBufferInverted() {
1816    ConvertToBuffer(1, 0, true, FROM, kErrorHigh,
1817                    cricket::FOURCC_GBRG, libyuv::BayerGBRGToI420);
1818  }
1819  void ConvertFromBayerGRBGBufferInverted() {
1820    ConvertToBuffer(1, 0, true, FROM, kErrorHigh,
1821                    cricket::FOURCC_GRBG, libyuv::BayerGRBGToI420);
1822  }
1823  void ConvertFromBayerRGGBBufferInverted() {
1824    ConvertToBuffer(1, 0, true, FROM, kErrorHigh,
1825                    cricket::FOURCC_RGGB, libyuv::BayerRGGBToI420);
1826  }
1827  void ConvertFromI400BufferInverted() {
1828    ConvertToBuffer(1, 0, true, FROM, 128,
1829                    cricket::FOURCC_I400, libyuv::I400ToI420);
1830  }
1831  void ConvertFromYUY2BufferInverted() {
1832    ConvertToBuffer(2, 0, true, FROM, kError,
1833                    cricket::FOURCC_YUY2, libyuv::YUY2ToI420);
1834  }
1835  void ConvertFromUYVYBufferInverted() {
1836    ConvertToBuffer(2, 0, true, FROM, kError,
1837                    cricket::FOURCC_UYVY, libyuv::UYVYToI420);
1838  }
1839
1840  // Test converting from I420 to I422.
1841  void ConvertToI422Buffer() {
1842    T frame1, frame2;
1843    size_t out_size = kWidth * kHeight * 2;
1844    talk_base::scoped_array<uint8> buf(new uint8[out_size + kAlignment]);
1845    uint8* y = ALIGNP(buf.get(), kAlignment);
1846    uint8* u = y + kWidth * kHeight;
1847    uint8* v = u + (kWidth / 2) * kHeight;
1848    ASSERT_TRUE(LoadFrameNoRepeat(&frame1));
1849    for (int i = 0; i < repeat_; ++i) {
1850      EXPECT_EQ(0, libyuv::I420ToI422(frame1.GetYPlane(), frame1.GetYPitch(),
1851                                      frame1.GetUPlane(), frame1.GetUPitch(),
1852                                      frame1.GetVPlane(), frame1.GetVPitch(),
1853                                      y, kWidth,
1854                                      u, kWidth / 2,
1855                                      v, kWidth / 2,
1856                                      kWidth, kHeight));
1857    }
1858    EXPECT_TRUE(frame2.Init(cricket::FOURCC_I422,
1859                            kWidth, kHeight, kWidth, kHeight,
1860                            y,
1861                            out_size,  1, 1, 0, 0, cricket::ROTATION_0));
1862    EXPECT_TRUE(IsEqual(frame1, frame2, 0));
1863  }
1864
1865  #define TEST_TOBYR(NAME, BAYER)                                              \
1866  void NAME() {                                                                \
1867    size_t bayer_size = kWidth * kHeight;                                      \
1868    talk_base::scoped_array<uint8> bayerbuf(new uint8[                         \
1869        bayer_size + kAlignment]);                                             \
1870    uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment);                         \
1871    T frame;                                                                   \
1872    talk_base::scoped_ptr<talk_base::MemoryStream> ms(                         \
1873        CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));               \
1874    ASSERT_TRUE(ms.get() != NULL);                                             \
1875    for (int i = 0; i < repeat_; ++i) {                                        \
1876      libyuv::ARGBToBayer##BAYER(reinterpret_cast<uint8*>(ms->GetBuffer()),    \
1877                                 kWidth * 4,                                   \
1878                                 bayer, kWidth,                                \
1879                                 kWidth, kHeight);                             \
1880    }                                                                          \
1881    talk_base::scoped_ptr<talk_base::MemoryStream> ms2(                        \
1882        CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));               \
1883    size_t data_size;                                                          \
1884    bool ret = ms2->GetSize(&data_size);                                       \
1885    EXPECT_TRUE(ret);                                                          \
1886    libyuv::Bayer##BAYER##ToARGB(bayer, kWidth,                                \
1887                                 reinterpret_cast<uint8*>(ms2->GetBuffer()),   \
1888                                 kWidth * 4,                                   \
1889                                 kWidth, kHeight);                             \
1890    EXPECT_TRUE(IsPlaneEqual("argb",                                           \
1891        reinterpret_cast<uint8*>(ms->GetBuffer()), kWidth * 4,                 \
1892        reinterpret_cast<uint8*>(ms2->GetBuffer()), kWidth * 4,                \
1893        kWidth * 4, kHeight, 240));                                            \
1894  }                                                                            \
1895  void NAME##Unaligned() {                                                     \
1896    size_t bayer_size = kWidth * kHeight;                                      \
1897    talk_base::scoped_array<uint8> bayerbuf(new uint8[                         \
1898        bayer_size + 1 + kAlignment]);                                         \
1899    uint8 *bayer = ALIGNP(bayerbuf.get(), kAlignment) + 1;                     \
1900    T frame;                                                                   \
1901    talk_base::scoped_ptr<talk_base::MemoryStream> ms(                         \
1902        CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));               \
1903    ASSERT_TRUE(ms.get() != NULL);                                             \
1904    for (int i = 0; i < repeat_; ++i) {                                        \
1905      libyuv::ARGBToBayer##BAYER(reinterpret_cast<uint8*>(ms->GetBuffer()),    \
1906                                 kWidth * 4,                                   \
1907                                 bayer, kWidth,                                \
1908                                 kWidth, kHeight);                             \
1909    }                                                                          \
1910    talk_base::scoped_ptr<talk_base::MemoryStream> ms2(                        \
1911        CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));               \
1912    size_t data_size;                                                          \
1913    bool ret = ms2->GetSize(&data_size);                                       \
1914    EXPECT_TRUE(ret);                                                          \
1915    libyuv::Bayer##BAYER##ToARGB(bayer, kWidth,                                \
1916                           reinterpret_cast<uint8*>(ms2->GetBuffer()),         \
1917                           kWidth * 4,                                         \
1918                           kWidth, kHeight);                                   \
1919    EXPECT_TRUE(IsPlaneEqual("argb",                                           \
1920        reinterpret_cast<uint8*>(ms->GetBuffer()), kWidth * 4,                 \
1921        reinterpret_cast<uint8*>(ms2->GetBuffer()), kWidth * 4,                \
1922        kWidth * 4, kHeight, 240));                                            \
1923  }
1924
1925  // Tests ARGB to Bayer formats.
1926  TEST_TOBYR(ConvertARGBToBayerGRBG, GRBG)
1927  TEST_TOBYR(ConvertARGBToBayerGBRG, GBRG)
1928  TEST_TOBYR(ConvertARGBToBayerBGGR, BGGR)
1929  TEST_TOBYR(ConvertARGBToBayerRGGB, RGGB)
1930
1931  #define TEST_BYRTORGB(NAME, BAYER)                                           \
1932  void NAME() {                                                                \
1933    size_t bayer_size = kWidth * kHeight;                                      \
1934    talk_base::scoped_array<uint8> bayerbuf(new uint8[                         \
1935        bayer_size + kAlignment]);                                             \
1936    uint8 *bayer1 = ALIGNP(bayerbuf.get(), kAlignment);                        \
1937    for (int i = 0; i < kWidth * kHeight; ++i) {                               \
1938      bayer1[i] = static_cast<uint8>(i * 33u + 183u);                          \
1939    }                                                                          \
1940    T frame;                                                                   \
1941    talk_base::scoped_ptr<talk_base::MemoryStream> ms(                         \
1942        CreateRgbSample(cricket::FOURCC_ARGB, kWidth, kHeight));               \
1943    ASSERT_TRUE(ms.get() != NULL);                                             \
1944    for (int i = 0; i < repeat_; ++i) {                                        \
1945      libyuv::Bayer##BAYER##ToARGB(bayer1, kWidth,                             \
1946                             reinterpret_cast<uint8*>(ms->GetBuffer()),        \
1947                             kWidth * 4,                                       \
1948                             kWidth, kHeight);                                 \
1949    }                                                                          \
1950    talk_base::scoped_array<uint8> bayer2buf(new uint8[                        \
1951        bayer_size + kAlignment]);                                             \
1952    uint8 *bayer2 = ALIGNP(bayer2buf.get(), kAlignment);                       \
1953    libyuv::ARGBToBayer##BAYER(reinterpret_cast<uint8*>(ms->GetBuffer()),      \
1954                           kWidth * 4,                                         \
1955                           bayer2, kWidth,                                     \
1956                           kWidth, kHeight);                                   \
1957    EXPECT_TRUE(IsPlaneEqual("bayer",                                          \
1958        bayer1, kWidth,                                                        \
1959        bayer2, kWidth,                                                        \
1960        kWidth, kHeight, 0));                                                  \
1961  }
1962
1963  // Tests Bayer formats to ARGB.
1964  TEST_BYRTORGB(ConvertBayerGRBGToARGB, GRBG)
1965  TEST_BYRTORGB(ConvertBayerGBRGToARGB, GBRG)
1966  TEST_BYRTORGB(ConvertBayerBGGRToARGB, BGGR)
1967  TEST_BYRTORGB(ConvertBayerRGGBToARGB, RGGB)
1968
1969  ///////////////////
1970  // General tests //
1971  ///////////////////
1972
1973  void Copy() {
1974    talk_base::scoped_ptr<T> source(new T);
1975    talk_base::scoped_ptr<cricket::VideoFrame> target;
1976    ASSERT_TRUE(LoadFrameNoRepeat(source.get()));
1977    target.reset(source->Copy());
1978    EXPECT_TRUE(IsEqual(*source, *target, 0));
1979    source.reset();
1980    EXPECT_TRUE(target->GetYPlane() != NULL);
1981  }
1982
1983  void CopyIsRef() {
1984    talk_base::scoped_ptr<T> source(new T);
1985    talk_base::scoped_ptr<cricket::VideoFrame> target;
1986    ASSERT_TRUE(LoadFrameNoRepeat(source.get()));
1987    target.reset(source->Copy());
1988    EXPECT_TRUE(IsEqual(*source, *target, 0));
1989    EXPECT_EQ(source->GetYPlane(), target->GetYPlane());
1990    EXPECT_EQ(source->GetUPlane(), target->GetUPlane());
1991    EXPECT_EQ(source->GetVPlane(), target->GetVPlane());
1992  }
1993
1994  void MakeExclusive() {
1995    talk_base::scoped_ptr<T> source(new T);
1996    talk_base::scoped_ptr<cricket::VideoFrame> target;
1997    ASSERT_TRUE(LoadFrameNoRepeat(source.get()));
1998    target.reset(source->Copy());
1999    EXPECT_TRUE(target->MakeExclusive());
2000    EXPECT_TRUE(IsEqual(*source, *target, 0));
2001    EXPECT_NE(target->GetYPlane(), source->GetYPlane());
2002    EXPECT_NE(target->GetUPlane(), source->GetUPlane());
2003    EXPECT_NE(target->GetVPlane(), source->GetVPlane());
2004  }
2005
2006  void CopyToBuffer() {
2007    T frame;
2008    talk_base::scoped_ptr<talk_base::MemoryStream> ms(
2009        LoadSample(kImageFilename));
2010    ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight,
2011                          &frame));
2012    size_t out_size = kWidth * kHeight * 3 / 2;
2013    talk_base::scoped_array<uint8> out(new uint8[out_size]);
2014    for (int i = 0; i < repeat_; ++i) {
2015      EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size));
2016    }
2017    EXPECT_EQ(0, memcmp(out.get(), ms->GetBuffer(), out_size));
2018  }
2019
2020  void CopyToFrame() {
2021    T source;
2022    talk_base::scoped_ptr<talk_base::MemoryStream> ms(
2023        LoadSample(kImageFilename));
2024    ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight,
2025                          &source));
2026
2027    // Create the target frame by loading from a file.
2028    T target;
2029    ASSERT_TRUE(LoadFrameNoRepeat(&target));
2030    EXPECT_FALSE(IsBlack(target));
2031
2032    // Stretch and check if the stretched target is black.
2033    source.CopyToFrame(&target);
2034
2035    EXPECT_TRUE(IsEqual(source, target, 0));
2036  }
2037
2038  void Write() {
2039    T frame;
2040    talk_base::scoped_ptr<talk_base::MemoryStream> ms(
2041        LoadSample(kImageFilename));
2042    talk_base::MemoryStream ms2;
2043    size_t size;
2044    ASSERT_TRUE(ms->GetSize(&size));
2045    ASSERT_TRUE(ms2.ReserveSize(size));
2046    ASSERT_TRUE(LoadFrame(ms.get(), cricket::FOURCC_I420, kWidth, kHeight,
2047                          &frame));
2048    for (int i = 0; i < repeat_; ++i) {
2049      ms2.SetPosition(0u);  // Useful when repeat_ > 1.
2050      int error;
2051      EXPECT_EQ(talk_base::SR_SUCCESS, frame.Write(&ms2, &error));
2052    }
2053    size_t out_size = cricket::VideoFrame::SizeOf(kWidth, kHeight);
2054    EXPECT_EQ(0, memcmp(ms2.GetBuffer(), ms->GetBuffer(), out_size));
2055  }
2056
2057  void CopyToBuffer1Pixel() {
2058    size_t out_size = 3;
2059    talk_base::scoped_array<uint8> out(new uint8[out_size + 1]);
2060    memset(out.get(), 0xfb, out_size + 1);  // Fill buffer
2061    uint8 pixel[3] = { 1, 2, 3 };
2062    T frame;
2063    EXPECT_TRUE(frame.Init(cricket::FOURCC_I420, 1, 1, 1, 1,
2064                           pixel, sizeof(pixel),
2065                           1, 1, 0, 0, 0));
2066    for (int i = 0; i < repeat_; ++i) {
2067      EXPECT_EQ(out_size, frame.CopyToBuffer(out.get(), out_size));
2068    }
2069    EXPECT_EQ(1, out.get()[0]);  // Check Y.  Should be 1.
2070    EXPECT_EQ(2, out.get()[1]);  // Check U.  Should be 2.
2071    EXPECT_EQ(3, out.get()[2]);  // Check V.  Should be 3.
2072    EXPECT_EQ(0xfb, out.get()[3]);  // Check sentinel is still intact.
2073  }
2074
2075  void StretchToFrame() {
2076    // Create the source frame as a black frame.
2077    T source;
2078    EXPECT_TRUE(source.InitToBlack(kWidth * 2, kHeight * 2, 1, 1, 0, 0));
2079    EXPECT_TRUE(IsSize(source, kWidth * 2, kHeight * 2));
2080
2081    // Create the target frame by loading from a file.
2082    T target1;
2083    ASSERT_TRUE(LoadFrameNoRepeat(&target1));
2084    EXPECT_FALSE(IsBlack(target1));
2085
2086    // Stretch and check if the stretched target is black.
2087    source.StretchToFrame(&target1, true, false);
2088    EXPECT_TRUE(IsBlack(target1));
2089
2090    // Crop and stretch and check if the stretched target is black.
2091    T target2;
2092    ASSERT_TRUE(LoadFrameNoRepeat(&target2));
2093    source.StretchToFrame(&target2, true, true);
2094    EXPECT_TRUE(IsBlack(target2));
2095    EXPECT_EQ(source.GetElapsedTime(), target2.GetElapsedTime());
2096    EXPECT_EQ(source.GetTimeStamp(), target2.GetTimeStamp());
2097  }
2098
2099  int repeat_;
2100};
2101
2102#endif  // TALK_MEDIA_BASE_VIDEOFRAME_UNITTEST_H_
2103