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