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