• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "media/base/buffers.h"
14 #include "media/base/yuv_convert.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace media {
18 
19 using base::MD5DigestToBase16;
20 
21 // Helper function that initializes a YV12 frame with white and black scan
22 // lines based on the |white_to_black| parameter.  If 0, then the entire
23 // frame will be black, if 1 then the entire frame will be white.
InitializeYV12Frame(VideoFrame * frame,double white_to_black)24 void InitializeYV12Frame(VideoFrame* frame, double white_to_black) {
25   EXPECT_EQ(VideoFrame::YV12, frame->format());
26   int first_black_row = static_cast<int>(frame->coded_size().height() *
27                                          white_to_black);
28   uint8* y_plane = frame->data(VideoFrame::kYPlane);
29   for (int row = 0; row < frame->coded_size().height(); ++row) {
30     int color = (row < first_black_row) ? 0xFF : 0x00;
31     memset(y_plane, color, frame->stride(VideoFrame::kYPlane));
32     y_plane += frame->stride(VideoFrame::kYPlane);
33   }
34   uint8* u_plane = frame->data(VideoFrame::kUPlane);
35   uint8* v_plane = frame->data(VideoFrame::kVPlane);
36   for (int row = 0; row < frame->coded_size().height(); row += 2) {
37     memset(u_plane, 0x80, frame->stride(VideoFrame::kUPlane));
38     memset(v_plane, 0x80, frame->stride(VideoFrame::kVPlane));
39     u_plane += frame->stride(VideoFrame::kUPlane);
40     v_plane += frame->stride(VideoFrame::kVPlane);
41   }
42 }
43 
44 // Given a |yv12_frame| this method converts the YV12 frame to RGBA and
45 // makes sure that all the pixels of the RBG frame equal |expect_rgb_color|.
ExpectFrameColor(media::VideoFrame * yv12_frame,uint32 expect_rgb_color)46 void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) {
47   ASSERT_EQ(VideoFrame::YV12, yv12_frame->format());
48   ASSERT_EQ(yv12_frame->stride(VideoFrame::kUPlane),
49             yv12_frame->stride(VideoFrame::kVPlane));
50   ASSERT_EQ(
51       yv12_frame->coded_size().width() & (VideoFrame::kFrameSizeAlignment - 1),
52       0);
53   ASSERT_EQ(
54       yv12_frame->coded_size().height() & (VideoFrame::kFrameSizeAlignment - 1),
55       0);
56 
57   size_t bytes_per_row = yv12_frame->coded_size().width() * 4u;
58   uint8* rgb_data = reinterpret_cast<uint8*>(
59       base::AlignedAlloc(bytes_per_row * yv12_frame->coded_size().height() +
60                              VideoFrame::kFrameSizePadding,
61                          VideoFrame::kFrameAddressAlignment));
62 
63   media::ConvertYUVToRGB32(yv12_frame->data(VideoFrame::kYPlane),
64                            yv12_frame->data(VideoFrame::kUPlane),
65                            yv12_frame->data(VideoFrame::kVPlane),
66                            rgb_data,
67                            yv12_frame->coded_size().width(),
68                            yv12_frame->coded_size().height(),
69                            yv12_frame->stride(VideoFrame::kYPlane),
70                            yv12_frame->stride(VideoFrame::kUPlane),
71                            bytes_per_row,
72                            media::YV12);
73 
74   for (int row = 0; row < yv12_frame->coded_size().height(); ++row) {
75     uint32* rgb_row_data = reinterpret_cast<uint32*>(
76         rgb_data + (bytes_per_row * row));
77     for (int col = 0; col < yv12_frame->coded_size().width(); ++col) {
78       SCOPED_TRACE(
79           base::StringPrintf("Checking (%d, %d)", row, col));
80       EXPECT_EQ(expect_rgb_color, rgb_row_data[col]);
81     }
82   }
83 
84   base::AlignedFree(rgb_data);
85 }
86 
87 // Fill each plane to its reported extents and verify accessors report non
88 // zero values.  Additionally, for the first plane verify the rows and
89 // row_bytes values are correct.
ExpectFrameExtents(VideoFrame::Format format,int planes,int bytes_per_pixel,const char * expected_hash)90 void ExpectFrameExtents(VideoFrame::Format format, int planes,
91                         int bytes_per_pixel, 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   for(int plane = 0; plane < planes; plane++) {
103     SCOPED_TRACE(base::StringPrintf("Checking plane %d", plane));
104     EXPECT_TRUE(frame->data(plane));
105     EXPECT_TRUE(frame->stride(plane));
106     EXPECT_TRUE(frame->rows(plane));
107     EXPECT_TRUE(frame->row_bytes(plane));
108 
109     if (plane == 0) {
110       EXPECT_EQ(frame->rows(plane), kHeight);
111       EXPECT_EQ(frame->row_bytes(plane), kWidth * bytes_per_pixel);
112     }
113 
114     memset(frame->data(plane), kFillByte,
115            frame->stride(plane) * frame->rows(plane));
116   }
117 
118   base::MD5Context context;
119   base::MD5Init(&context);
120   frame->HashFrameForTesting(&context);
121   base::MD5Digest digest;
122   base::MD5Final(&digest, &context);
123   EXPECT_EQ(MD5DigestToBase16(digest), expected_hash);
124 }
125 
TEST(VideoFrame,CreateFrame)126 TEST(VideoFrame, CreateFrame) {
127   const int kWidth = 64;
128   const int kHeight = 48;
129   const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337);
130 
131   // Create a YV12 Video Frame.
132   gfx::Size size(kWidth, kHeight);
133   scoped_refptr<media::VideoFrame> frame =
134       VideoFrame::CreateFrame(media::VideoFrame::YV12, size, gfx::Rect(size),
135                               size, kTimestamp);
136   ASSERT_TRUE(frame.get());
137 
138   // Test VideoFrame implementation.
139   EXPECT_EQ(media::VideoFrame::YV12, frame->format());
140   {
141     SCOPED_TRACE("");
142     InitializeYV12Frame(frame.get(), 0.0f);
143     ExpectFrameColor(frame.get(), 0xFF000000);
144   }
145   base::MD5Digest digest;
146   base::MD5Context context;
147   base::MD5Init(&context);
148   frame->HashFrameForTesting(&context);
149   base::MD5Final(&digest, &context);
150   EXPECT_EQ(MD5DigestToBase16(digest), "9065c841d9fca49186ef8b4ef547e79b");
151   {
152     SCOPED_TRACE("");
153     InitializeYV12Frame(frame.get(), 1.0f);
154     ExpectFrameColor(frame.get(), 0xFFFFFFFF);
155   }
156   base::MD5Init(&context);
157   frame->HashFrameForTesting(&context);
158   base::MD5Final(&digest, &context);
159   EXPECT_EQ(MD5DigestToBase16(digest), "911991d51438ad2e1a40ed5f6fc7c796");
160 
161   // Test an empty frame.
162   frame = VideoFrame::CreateEOSFrame();
163   EXPECT_TRUE(frame->end_of_stream());
164 }
165 
TEST(VideoFrame,CreateBlackFrame)166 TEST(VideoFrame, CreateBlackFrame) {
167   const int kWidth = 2;
168   const int kHeight = 2;
169   const uint8 kExpectedYRow[] = { 0, 0 };
170   const uint8 kExpectedUVRow[] = { 128 };
171 
172   scoped_refptr<media::VideoFrame> frame =
173       VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight));
174   ASSERT_TRUE(frame.get());
175 
176   // Test basic properties.
177   EXPECT_EQ(0, frame->GetTimestamp().InMicroseconds());
178   EXPECT_FALSE(frame->end_of_stream());
179 
180   // Test |frame| properties.
181   EXPECT_EQ(VideoFrame::YV12, frame->format());
182   EXPECT_EQ(kWidth, frame->coded_size().width());
183   EXPECT_EQ(kHeight, frame->coded_size().height());
184 
185   // Test frames themselves.
186   uint8* y_plane = frame->data(VideoFrame::kYPlane);
187   for (int y = 0; y < frame->coded_size().height(); ++y) {
188     EXPECT_EQ(0, memcmp(kExpectedYRow, y_plane, arraysize(kExpectedYRow)));
189     y_plane += frame->stride(VideoFrame::kYPlane);
190   }
191 
192   uint8* u_plane = frame->data(VideoFrame::kUPlane);
193   uint8* v_plane = frame->data(VideoFrame::kVPlane);
194   for (int y = 0; y < frame->coded_size().height() / 2; ++y) {
195     EXPECT_EQ(0, memcmp(kExpectedUVRow, u_plane, arraysize(kExpectedUVRow)));
196     EXPECT_EQ(0, memcmp(kExpectedUVRow, v_plane, arraysize(kExpectedUVRow)));
197     u_plane += frame->stride(VideoFrame::kUPlane);
198     v_plane += frame->stride(VideoFrame::kVPlane);
199   }
200 }
201 
202 // Ensure each frame is properly sized and allocated.  Will trigger OOB reads
203 // and writes as well as incorrect frame hashes otherwise.
TEST(VideoFrame,CheckFrameExtents)204 TEST(VideoFrame, CheckFrameExtents) {
205   // Each call consists of a VideoFrame::Format, # of planes, bytes per pixel,
206   // and the expected hash of all planes if filled with kFillByte (defined in
207   // ExpectFrameExtents).
208   ExpectFrameExtents(
209       VideoFrame::YV12,   3, 1, "71113bdfd4c0de6cf62f48fb74f7a0b1");
210   ExpectFrameExtents(
211       VideoFrame::YV16,   3, 1, "9bb99ac3ff350644ebff4d28dc01b461");
212 }
213 
TextureCallback(uint32 * called_sync_point,uint32 sync_point)214 static void TextureCallback(uint32* called_sync_point, uint32 sync_point) {
215   *called_sync_point = sync_point;
216 }
217 
218 // Verify the TextureNoLongerNeededCallback is called when VideoFrame is
219 // destroyed with the original sync point.
TEST(VideoFrame,TextureNoLongerNeededCallbackIsCalled)220 TEST(VideoFrame, TextureNoLongerNeededCallbackIsCalled) {
221   uint32 sync_point = 7;
222   uint32 called_sync_point = 0;
223 
224   {
225     scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture(
226         make_scoped_ptr(new VideoFrame::MailboxHolder(
227             gpu::Mailbox(),
228             sync_point,
229             base::Bind(&TextureCallback, &called_sync_point))),
230         5,                                        // texture_target
231         gfx::Size(10, 10),                        // coded_size
232         gfx::Rect(10, 10),                        // visible_rect
233         gfx::Size(10, 10),                        // natural_size
234         base::TimeDelta(),                        // timestamp
235         base::Callback<void(const SkBitmap&)>(),  // read_pixels_cb
236         base::Closure());                         // no_longer_needed_cb
237 
238     EXPECT_EQ(0u, called_sync_point);
239   }
240   EXPECT_EQ(sync_point, called_sync_point);
241 }
242 
243 // Verify the TextureNoLongerNeededCallback is called when VideoFrame is
244 // destroyed with the new sync point, when the mailbox is accessed by a caller.
TEST(VideoFrame,TextureNoLongerNeededCallbackAfterTakingAndReleasingMailbox)245 TEST(VideoFrame, TextureNoLongerNeededCallbackAfterTakingAndReleasingMailbox) {
246   uint32 called_sync_point = 0;
247 
248   gpu::Mailbox mailbox;
249   mailbox.name[0] = 50;
250   uint32 sync_point = 7;
251   uint32 target = 9;
252 
253   {
254     scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture(
255         make_scoped_ptr(new VideoFrame::MailboxHolder(
256             mailbox,
257             sync_point,
258             base::Bind(&TextureCallback, &called_sync_point))),
259         target,
260         gfx::Size(10, 10),                        // coded_size
261         gfx::Rect(10, 10),                        // visible_rect
262         gfx::Size(10, 10),                        // natural_size
263         base::TimeDelta(),                        // timestamp
264         base::Callback<void(const SkBitmap&)>(),  // read_pixels_cb
265         base::Closure());                         // no_longer_needed_cb
266 
267     VideoFrame::MailboxHolder* mailbox_holder = frame->texture_mailbox();
268 
269     EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox().name[0]);
270     EXPECT_EQ(sync_point, mailbox_holder->sync_point());
271     EXPECT_EQ(target, frame->texture_target());
272 
273     // Finish using the mailbox_holder and drop our reference.
274     sync_point = 10;
275     mailbox_holder->Resync(sync_point);
276   }
277   EXPECT_EQ(sync_point, called_sync_point);
278 }
279 
280 }  // namespace media
281