1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/base/video_frame.h"
6
7#include "base/bind.h"
8#include "base/callback_helpers.h"
9#include "base/format_macros.h"
10#include "base/memory/aligned_memory.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/strings/stringprintf.h"
13#include "gpu/command_buffer/common/mailbox_holder.h"
14#include "media/base/buffers.h"
15#include "media/base/yuv_convert.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace media {
19
20using base::MD5DigestToBase16;
21
22// Helper function that initializes a YV12 frame with white and black scan
23// lines based on the |white_to_black| parameter.  If 0, then the entire
24// frame will be black, if 1 then the entire frame will be white.
25void InitializeYV12Frame(VideoFrame* frame, double white_to_black) {
26  EXPECT_EQ(VideoFrame::YV12, frame->format());
27  int first_black_row = static_cast<int>(frame->coded_size().height() *
28                                         white_to_black);
29  uint8* y_plane = frame->data(VideoFrame::kYPlane);
30  for (int row = 0; row < frame->coded_size().height(); ++row) {
31    int color = (row < first_black_row) ? 0xFF : 0x00;
32    memset(y_plane, color, frame->stride(VideoFrame::kYPlane));
33    y_plane += frame->stride(VideoFrame::kYPlane);
34  }
35  uint8* u_plane = frame->data(VideoFrame::kUPlane);
36  uint8* v_plane = frame->data(VideoFrame::kVPlane);
37  for (int row = 0; row < frame->coded_size().height(); row += 2) {
38    memset(u_plane, 0x80, frame->stride(VideoFrame::kUPlane));
39    memset(v_plane, 0x80, frame->stride(VideoFrame::kVPlane));
40    u_plane += frame->stride(VideoFrame::kUPlane);
41    v_plane += frame->stride(VideoFrame::kVPlane);
42  }
43}
44
45// Given a |yv12_frame| this method converts the YV12 frame to RGBA and
46// makes sure that all the pixels of the RBG frame equal |expect_rgb_color|.
47void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) {
48  ASSERT_EQ(VideoFrame::YV12, yv12_frame->format());
49  ASSERT_EQ(yv12_frame->stride(VideoFrame::kUPlane),
50            yv12_frame->stride(VideoFrame::kVPlane));
51  ASSERT_EQ(
52      yv12_frame->coded_size().width() & (VideoFrame::kFrameSizeAlignment - 1),
53      0);
54  ASSERT_EQ(
55      yv12_frame->coded_size().height() & (VideoFrame::kFrameSizeAlignment - 1),
56      0);
57
58  size_t bytes_per_row = yv12_frame->coded_size().width() * 4u;
59  uint8* rgb_data = reinterpret_cast<uint8*>(
60      base::AlignedAlloc(bytes_per_row * yv12_frame->coded_size().height() +
61                             VideoFrame::kFrameSizePadding,
62                         VideoFrame::kFrameAddressAlignment));
63
64  media::ConvertYUVToRGB32(yv12_frame->data(VideoFrame::kYPlane),
65                           yv12_frame->data(VideoFrame::kUPlane),
66                           yv12_frame->data(VideoFrame::kVPlane),
67                           rgb_data,
68                           yv12_frame->coded_size().width(),
69                           yv12_frame->coded_size().height(),
70                           yv12_frame->stride(VideoFrame::kYPlane),
71                           yv12_frame->stride(VideoFrame::kUPlane),
72                           bytes_per_row,
73                           media::YV12);
74
75  for (int row = 0; row < yv12_frame->coded_size().height(); ++row) {
76    uint32* rgb_row_data = reinterpret_cast<uint32*>(
77        rgb_data + (bytes_per_row * row));
78    for (int col = 0; col < yv12_frame->coded_size().width(); ++col) {
79      SCOPED_TRACE(
80          base::StringPrintf("Checking (%d, %d)", row, col));
81      EXPECT_EQ(expect_rgb_color, rgb_row_data[col]);
82    }
83  }
84
85  base::AlignedFree(rgb_data);
86}
87
88// Fill each plane to its reported extents and verify accessors report non
89// zero values.  Additionally, for the first plane verify the rows and
90// row_bytes values are correct.
91void ExpectFrameExtents(VideoFrame::Format format, const char* expected_hash) {
92  const unsigned char kFillByte = 0x80;
93  const int kWidth = 61;
94  const int kHeight = 31;
95  const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337);
96
97  gfx::Size size(kWidth, kHeight);
98  scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
99      format, size, gfx::Rect(size), size, kTimestamp);
100  ASSERT_TRUE(frame.get());
101
102  int planes = VideoFrame::NumPlanes(format);
103  for (int plane = 0; plane < planes; plane++) {
104    SCOPED_TRACE(base::StringPrintf("Checking plane %d", plane));
105    EXPECT_TRUE(frame->data(plane));
106    EXPECT_TRUE(frame->stride(plane));
107    EXPECT_TRUE(frame->rows(plane));
108    EXPECT_TRUE(frame->row_bytes(plane));
109
110    memset(frame->data(plane), kFillByte,
111           frame->stride(plane) * frame->rows(plane));
112  }
113
114  base::MD5Context context;
115  base::MD5Init(&context);
116  frame->HashFrameForTesting(&context);
117  base::MD5Digest digest;
118  base::MD5Final(&digest, &context);
119  EXPECT_EQ(MD5DigestToBase16(digest), expected_hash);
120}
121
122TEST(VideoFrame, CreateFrame) {
123  const int kWidth = 64;
124  const int kHeight = 48;
125  const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337);
126
127  // Create a YV12 Video Frame.
128  gfx::Size size(kWidth, kHeight);
129  scoped_refptr<media::VideoFrame> frame =
130      VideoFrame::CreateFrame(media::VideoFrame::YV12, size, gfx::Rect(size),
131                              size, kTimestamp);
132  ASSERT_TRUE(frame.get());
133
134  // Test VideoFrame implementation.
135  EXPECT_EQ(media::VideoFrame::YV12, frame->format());
136  {
137    SCOPED_TRACE("");
138    InitializeYV12Frame(frame.get(), 0.0f);
139    ExpectFrameColor(frame.get(), 0xFF000000);
140  }
141  base::MD5Digest digest;
142  base::MD5Context context;
143  base::MD5Init(&context);
144  frame->HashFrameForTesting(&context);
145  base::MD5Final(&digest, &context);
146  EXPECT_EQ(MD5DigestToBase16(digest), "9065c841d9fca49186ef8b4ef547e79b");
147  {
148    SCOPED_TRACE("");
149    InitializeYV12Frame(frame.get(), 1.0f);
150    ExpectFrameColor(frame.get(), 0xFFFFFFFF);
151  }
152  base::MD5Init(&context);
153  frame->HashFrameForTesting(&context);
154  base::MD5Final(&digest, &context);
155  EXPECT_EQ(MD5DigestToBase16(digest), "911991d51438ad2e1a40ed5f6fc7c796");
156
157  // Test an empty frame.
158  frame = VideoFrame::CreateEOSFrame();
159  EXPECT_TRUE(frame->end_of_stream());
160}
161
162TEST(VideoFrame, CreateBlackFrame) {
163  const int kWidth = 2;
164  const int kHeight = 2;
165  const uint8 kExpectedYRow[] = { 0, 0 };
166  const uint8 kExpectedUVRow[] = { 128 };
167
168  scoped_refptr<media::VideoFrame> frame =
169      VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight));
170  ASSERT_TRUE(frame.get());
171
172  // Test basic properties.
173  EXPECT_EQ(0, frame->timestamp().InMicroseconds());
174  EXPECT_FALSE(frame->end_of_stream());
175
176  // Test |frame| properties.
177  EXPECT_EQ(VideoFrame::YV12, frame->format());
178  EXPECT_EQ(kWidth, frame->coded_size().width());
179  EXPECT_EQ(kHeight, frame->coded_size().height());
180
181  // Test frames themselves.
182  uint8* y_plane = frame->data(VideoFrame::kYPlane);
183  for (int y = 0; y < frame->coded_size().height(); ++y) {
184    EXPECT_EQ(0, memcmp(kExpectedYRow, y_plane, arraysize(kExpectedYRow)));
185    y_plane += frame->stride(VideoFrame::kYPlane);
186  }
187
188  uint8* u_plane = frame->data(VideoFrame::kUPlane);
189  uint8* v_plane = frame->data(VideoFrame::kVPlane);
190  for (int y = 0; y < frame->coded_size().height() / 2; ++y) {
191    EXPECT_EQ(0, memcmp(kExpectedUVRow, u_plane, arraysize(kExpectedUVRow)));
192    EXPECT_EQ(0, memcmp(kExpectedUVRow, v_plane, arraysize(kExpectedUVRow)));
193    u_plane += frame->stride(VideoFrame::kUPlane);
194    v_plane += frame->stride(VideoFrame::kVPlane);
195  }
196}
197
198static void FrameNoLongerNeededCallback(
199    const scoped_refptr<media::VideoFrame>& frame,
200    bool* triggered) {
201  *triggered = true;
202}
203
204TEST(VideoFrame, WrapVideoFrame) {
205  const int kWidth = 4;
206  const int kHeight = 4;
207  scoped_refptr<media::VideoFrame> frame;
208  bool no_longer_needed_triggered = false;
209  {
210    scoped_refptr<media::VideoFrame> wrapped_frame =
211        VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight));
212    ASSERT_TRUE(wrapped_frame.get());
213
214    gfx::Rect visible_rect(1, 1, 1, 1);
215    gfx::Size natural_size = visible_rect.size();
216    frame = media::VideoFrame::WrapVideoFrame(
217        wrapped_frame, visible_rect, natural_size,
218        base::Bind(&FrameNoLongerNeededCallback, wrapped_frame,
219                   &no_longer_needed_triggered));
220    EXPECT_EQ(wrapped_frame->coded_size(), frame->coded_size());
221    EXPECT_EQ(wrapped_frame->data(media::VideoFrame::kYPlane),
222              frame->data(media::VideoFrame::kYPlane));
223    EXPECT_NE(wrapped_frame->visible_rect(), frame->visible_rect());
224    EXPECT_EQ(visible_rect, frame->visible_rect());
225    EXPECT_NE(wrapped_frame->natural_size(), frame->natural_size());
226    EXPECT_EQ(natural_size, frame->natural_size());
227  }
228
229  EXPECT_FALSE(no_longer_needed_triggered);
230  frame = NULL;
231  EXPECT_TRUE(no_longer_needed_triggered);
232}
233
234// Ensure each frame is properly sized and allocated.  Will trigger OOB reads
235// and writes as well as incorrect frame hashes otherwise.
236TEST(VideoFrame, CheckFrameExtents) {
237  // Each call consists of a VideoFrame::Format and the expected hash of all
238  // planes if filled with kFillByte (defined in ExpectFrameExtents).
239  ExpectFrameExtents(VideoFrame::YV12, "8e5d54cb23cd0edca111dd35ffb6ff05");
240  ExpectFrameExtents(VideoFrame::YV16, "cce408a044b212db42a10dfec304b3ef");
241}
242
243static void TextureCallback(uint32* called_sync_point,
244                            uint32 release_sync_point) {
245  *called_sync_point = release_sync_point;
246}
247
248// Verify the gpu::MailboxHolder::ReleaseCallback is called when VideoFrame is
249// destroyed with the default release sync point.
250TEST(VideoFrame, TextureNoLongerNeededCallbackIsCalled) {
251  uint32 called_sync_point = 1;
252
253  {
254    scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture(
255        make_scoped_ptr(
256            new gpu::MailboxHolder(gpu::Mailbox(), 5, 0 /* sync_point */)),
257        base::Bind(&TextureCallback, &called_sync_point),
258        gfx::Size(10, 10),            // coded_size
259        gfx::Rect(10, 10),            // visible_rect
260        gfx::Size(10, 10),            // natural_size
261        base::TimeDelta(),            // timestamp
262        VideoFrame::ReadPixelsCB());  // read_pixels_cb
263  }
264  // Nobody set a sync point to |frame|, so |frame| set |called_sync_point| to 0
265  // as default value.
266  EXPECT_EQ(0u, called_sync_point);
267}
268
269namespace {
270
271class SyncPointClientImpl : public VideoFrame::SyncPointClient {
272 public:
273  explicit SyncPointClientImpl(uint32 sync_point) : sync_point_(sync_point) {}
274  virtual ~SyncPointClientImpl() {}
275  virtual uint32 InsertSyncPoint() OVERRIDE { return sync_point_; }
276  virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {}
277
278 private:
279  uint32 sync_point_;
280};
281
282}  // namespace
283
284// Verify the gpu::MailboxHolder::ReleaseCallback is called when VideoFrame is
285// destroyed with the release sync point, which was updated by clients.
286// (i.e. the compositor, webgl).
287TEST(VideoFrame, TextureNoLongerNeededCallbackAfterTakingAndReleasingMailbox) {
288  gpu::Mailbox mailbox;
289  mailbox.name[0] = 50;
290  uint32 sync_point = 7;
291  uint32 target = 9;
292  uint32 release_sync_point = 111;
293  uint32 called_sync_point = 0;
294
295  {
296    scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture(
297        make_scoped_ptr(new gpu::MailboxHolder(mailbox, target, sync_point)),
298        base::Bind(&TextureCallback, &called_sync_point),
299        gfx::Size(10, 10),            // coded_size
300        gfx::Rect(10, 10),            // visible_rect
301        gfx::Size(10, 10),            // natural_size
302        base::TimeDelta(),            // timestamp
303        VideoFrame::ReadPixelsCB());  // read_pixels_cb
304
305    const gpu::MailboxHolder* mailbox_holder = frame->mailbox_holder();
306
307    EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox.name[0]);
308    EXPECT_EQ(target, mailbox_holder->texture_target);
309    EXPECT_EQ(sync_point, mailbox_holder->sync_point);
310
311    SyncPointClientImpl client(release_sync_point);
312    frame->UpdateReleaseSyncPoint(&client);
313    EXPECT_EQ(sync_point, mailbox_holder->sync_point);
314  }
315  EXPECT_EQ(release_sync_point, called_sync_point);
316}
317
318TEST(VideoFrame, ZeroInitialized) {
319  const int kWidth = 64;
320  const int kHeight = 48;
321  const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337);
322
323  gfx::Size size(kWidth, kHeight);
324  scoped_refptr<media::VideoFrame> frame = VideoFrame::CreateFrame(
325      media::VideoFrame::YV12, size, gfx::Rect(size), size, kTimestamp);
326
327  for (size_t i = 0; i < VideoFrame::NumPlanes(frame->format()); ++i)
328    EXPECT_EQ(0, frame->data(i)[0]);
329}
330
331}  // namespace media
332