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/scoped_ptr.h" 11 #include "base/strings/stringprintf.h" 12 #include "media/base/buffers.h" 13 #include "media/base/yuv_convert.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 16 namespace media { 17 18 using base::MD5DigestToBase16; 19 20 // Helper function that initializes a YV12 frame with white and black scan 21 // lines based on the |white_to_black| parameter. If 0, then the entire 22 // frame will be black, if 1 then the entire frame will be white. 23 void InitializeYV12Frame(VideoFrame* frame, double white_to_black) { 24 EXPECT_EQ(VideoFrame::YV12, frame->format()); 25 int first_black_row = static_cast<int>(frame->coded_size().height() * 26 white_to_black); 27 uint8* y_plane = frame->data(VideoFrame::kYPlane); 28 for (int row = 0; row < frame->coded_size().height(); ++row) { 29 int color = (row < first_black_row) ? 0xFF : 0x00; 30 memset(y_plane, color, frame->stride(VideoFrame::kYPlane)); 31 y_plane += frame->stride(VideoFrame::kYPlane); 32 } 33 uint8* u_plane = frame->data(VideoFrame::kUPlane); 34 uint8* v_plane = frame->data(VideoFrame::kVPlane); 35 for (int row = 0; row < frame->coded_size().height(); row += 2) { 36 memset(u_plane, 0x80, frame->stride(VideoFrame::kUPlane)); 37 memset(v_plane, 0x80, frame->stride(VideoFrame::kVPlane)); 38 u_plane += frame->stride(VideoFrame::kUPlane); 39 v_plane += frame->stride(VideoFrame::kVPlane); 40 } 41 } 42 43 // Given a |yv12_frame| this method converts the YV12 frame to RGBA and 44 // makes sure that all the pixels of the RBG frame equal |expect_rgb_color|. 45 void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) { 46 ASSERT_EQ(VideoFrame::YV12, yv12_frame->format()); 47 ASSERT_EQ(yv12_frame->stride(VideoFrame::kUPlane), 48 yv12_frame->stride(VideoFrame::kVPlane)); 49 50 scoped_refptr<media::VideoFrame> rgb_frame; 51 rgb_frame = media::VideoFrame::CreateFrame(VideoFrame::RGB32, 52 yv12_frame->coded_size(), 53 yv12_frame->visible_rect(), 54 yv12_frame->natural_size(), 55 yv12_frame->GetTimestamp()); 56 57 ASSERT_EQ(yv12_frame->coded_size().width(), 58 rgb_frame->coded_size().width()); 59 ASSERT_EQ(yv12_frame->coded_size().height(), 60 rgb_frame->coded_size().height()); 61 62 media::ConvertYUVToRGB32(yv12_frame->data(VideoFrame::kYPlane), 63 yv12_frame->data(VideoFrame::kUPlane), 64 yv12_frame->data(VideoFrame::kVPlane), 65 rgb_frame->data(VideoFrame::kRGBPlane), 66 rgb_frame->coded_size().width(), 67 rgb_frame->coded_size().height(), 68 yv12_frame->stride(VideoFrame::kYPlane), 69 yv12_frame->stride(VideoFrame::kUPlane), 70 rgb_frame->stride(VideoFrame::kRGBPlane), 71 media::YV12); 72 73 for (int row = 0; row < rgb_frame->coded_size().height(); ++row) { 74 uint32* rgb_row_data = reinterpret_cast<uint32*>( 75 rgb_frame->data(VideoFrame::kRGBPlane) + 76 (rgb_frame->stride(VideoFrame::kRGBPlane) * row)); 77 for (int col = 0; col < rgb_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 85 // Fill each plane to its reported extents and verify accessors report non 86 // zero values. Additionally, for the first plane verify the rows and 87 // row_bytes values are correct. 88 void ExpectFrameExtents(VideoFrame::Format format, int planes, 89 int bytes_per_pixel, const char* expected_hash) { 90 const unsigned char kFillByte = 0x80; 91 const int kWidth = 61; 92 const int kHeight = 31; 93 const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337); 94 95 gfx::Size size(kWidth, kHeight); 96 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame( 97 format, size, gfx::Rect(size), size, kTimestamp); 98 ASSERT_TRUE(frame.get()); 99 100 for(int plane = 0; plane < planes; plane++) { 101 SCOPED_TRACE(base::StringPrintf("Checking plane %d", plane)); 102 EXPECT_TRUE(frame->data(plane)); 103 EXPECT_TRUE(frame->stride(plane)); 104 EXPECT_TRUE(frame->rows(plane)); 105 EXPECT_TRUE(frame->row_bytes(plane)); 106 107 if (plane == 0) { 108 EXPECT_EQ(frame->rows(plane), kHeight); 109 EXPECT_EQ(frame->row_bytes(plane), kWidth * bytes_per_pixel); 110 } 111 112 memset(frame->data(plane), kFillByte, 113 frame->stride(plane) * frame->rows(plane)); 114 } 115 116 base::MD5Context context; 117 base::MD5Init(&context); 118 frame->HashFrameForTesting(&context); 119 base::MD5Digest digest; 120 base::MD5Final(&digest, &context); 121 EXPECT_EQ(MD5DigestToBase16(digest), expected_hash); 122 } 123 124 TEST(VideoFrame, CreateFrame) { 125 const int kWidth = 64; 126 const int kHeight = 48; 127 const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337); 128 129 // Create a YV12 Video Frame. 130 gfx::Size size(kWidth, kHeight); 131 scoped_refptr<media::VideoFrame> frame = 132 VideoFrame::CreateFrame(media::VideoFrame::YV12, size, gfx::Rect(size), 133 size, kTimestamp); 134 ASSERT_TRUE(frame.get()); 135 136 // Test VideoFrame implementation. 137 EXPECT_EQ(media::VideoFrame::YV12, frame->format()); 138 { 139 SCOPED_TRACE(""); 140 InitializeYV12Frame(frame.get(), 0.0f); 141 ExpectFrameColor(frame.get(), 0xFF000000); 142 } 143 base::MD5Digest digest; 144 base::MD5Context context; 145 base::MD5Init(&context); 146 frame->HashFrameForTesting(&context); 147 base::MD5Final(&digest, &context); 148 EXPECT_EQ(MD5DigestToBase16(digest), "9065c841d9fca49186ef8b4ef547e79b"); 149 { 150 SCOPED_TRACE(""); 151 InitializeYV12Frame(frame.get(), 1.0f); 152 ExpectFrameColor(frame.get(), 0xFFFFFFFF); 153 } 154 base::MD5Init(&context); 155 frame->HashFrameForTesting(&context); 156 base::MD5Final(&digest, &context); 157 EXPECT_EQ(MD5DigestToBase16(digest), "911991d51438ad2e1a40ed5f6fc7c796"); 158 159 // Test an empty frame. 160 frame = VideoFrame::CreateEmptyFrame(); 161 EXPECT_TRUE(frame->IsEndOfStream()); 162 } 163 164 TEST(VideoFrame, CreateBlackFrame) { 165 const int kWidth = 2; 166 const int kHeight = 2; 167 const uint8 kExpectedYRow[] = { 0, 0 }; 168 const uint8 kExpectedUVRow[] = { 128 }; 169 170 scoped_refptr<media::VideoFrame> frame = 171 VideoFrame::CreateBlackFrame(gfx::Size(kWidth, kHeight)); 172 ASSERT_TRUE(frame.get()); 173 174 // Test basic properties. 175 EXPECT_EQ(0, frame->GetTimestamp().InMicroseconds()); 176 EXPECT_FALSE(frame->IsEndOfStream()); 177 178 // Test |frame| properties. 179 EXPECT_EQ(VideoFrame::YV12, frame->format()); 180 EXPECT_EQ(kWidth, frame->coded_size().width()); 181 EXPECT_EQ(kHeight, frame->coded_size().height()); 182 183 // Test frames themselves. 184 uint8* y_plane = frame->data(VideoFrame::kYPlane); 185 for (int y = 0; y < frame->coded_size().height(); ++y) { 186 EXPECT_EQ(0, memcmp(kExpectedYRow, y_plane, arraysize(kExpectedYRow))); 187 y_plane += frame->stride(VideoFrame::kYPlane); 188 } 189 190 uint8* u_plane = frame->data(VideoFrame::kUPlane); 191 uint8* v_plane = frame->data(VideoFrame::kVPlane); 192 for (int y = 0; y < frame->coded_size().height() / 2; ++y) { 193 EXPECT_EQ(0, memcmp(kExpectedUVRow, u_plane, arraysize(kExpectedUVRow))); 194 EXPECT_EQ(0, memcmp(kExpectedUVRow, v_plane, arraysize(kExpectedUVRow))); 195 u_plane += frame->stride(VideoFrame::kUPlane); 196 v_plane += frame->stride(VideoFrame::kVPlane); 197 } 198 } 199 200 // Ensure each frame is properly sized and allocated. Will trigger OOB reads 201 // and writes as well as incorrect frame hashes otherwise. 202 TEST(VideoFrame, CheckFrameExtents) { 203 // Each call consists of a VideoFrame::Format, # of planes, bytes per pixel, 204 // and the expected hash of all planes if filled with kFillByte (defined in 205 // ExpectFrameExtents). 206 ExpectFrameExtents( 207 VideoFrame::RGB32, 1, 4, "de6d3d567e282f6a38d478f04fc81fb0"); 208 ExpectFrameExtents( 209 VideoFrame::YV12, 3, 1, "71113bdfd4c0de6cf62f48fb74f7a0b1"); 210 ExpectFrameExtents( 211 VideoFrame::YV16, 3, 1, "9bb99ac3ff350644ebff4d28dc01b461"); 212 } 213 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. 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 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 taken by a caller. 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 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 { 268 scoped_refptr<VideoFrame::MailboxHolder> mailbox_holder = 269 frame->texture_mailbox(); 270 271 EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox().name[0]); 272 EXPECT_EQ(sync_point, mailbox_holder->sync_point()); 273 EXPECT_EQ(target, frame->texture_target()); 274 275 // Misuse the callback. 276 sync_point = 12; 277 mailbox_holder->Return(sync_point); 278 EXPECT_EQ(0u, called_sync_point); 279 280 // Finish using the mailbox_holder and drop our reference. 281 sync_point = 10; 282 mailbox_holder->Return(sync_point); 283 } 284 EXPECT_EQ(0u, called_sync_point); 285 } 286 EXPECT_EQ(sync_point, called_sync_point); 287 } 288 289 // If a caller has taken ownership of the texture mailbox, it should 290 // not be released when the VideoFrame is destroyed, but should when 291 // the TextureNoLongerNeededCallback is called. 292 TEST(VideoFrame, 293 TextureNoLongerNeededCallbackAfterTakingMailboxWithDestroyedFrame) { 294 uint32 called_sync_point = 0; 295 296 gpu::Mailbox mailbox; 297 mailbox.name[0] = 50; 298 uint32 sync_point = 7; 299 uint32 target = 9; 300 301 { 302 scoped_refptr<VideoFrame::MailboxHolder> mailbox_holder; 303 304 { 305 scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture( 306 new VideoFrame::MailboxHolder( 307 mailbox, 308 sync_point, 309 base::Bind(&TextureCallback, &called_sync_point)), 310 target, 311 gfx::Size(10, 10), // coded_size 312 gfx::Rect(10, 10), // visible_rect 313 gfx::Size(10, 10), // natural_size 314 base::TimeDelta(), // timestamp 315 base::Callback<void(const SkBitmap&)>(), // read_pixels_cb 316 base::Closure()); // no_longer_needed_cb 317 318 mailbox_holder = frame->texture_mailbox(); 319 320 EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox().name[0]); 321 EXPECT_EQ(sync_point, mailbox_holder->sync_point()); 322 EXPECT_EQ(target, frame->texture_target()); 323 324 // Keep a ref on the mailbox_holder after the VideoFrame is dropped. 325 } 326 EXPECT_EQ(0u, called_sync_point); 327 328 // Misuse the callback. 329 sync_point = 12; 330 mailbox_holder->Return(sync_point); 331 EXPECT_EQ(0u, called_sync_point); 332 333 // Finish using the mailbox_holder and drop our ref. 334 sync_point = 10; 335 mailbox_holder->Return(sync_point); 336 } 337 EXPECT_EQ(sync_point, called_sync_point); 338 } 339 340 // If a caller has taken ownership of the texture mailbox, but does 341 // not call the callback, it should still happen with the original 342 // sync point. 343 TEST(VideoFrame, 344 TextureNoLongerNeededCallbackWhenNotCallingAndFrameDestroyed) { 345 uint32 called_sync_point = 0; 346 347 gpu::Mailbox mailbox; 348 mailbox.name[0] = 50; 349 uint32 sync_point = 7; 350 uint32 target = 9; 351 352 { 353 scoped_refptr<VideoFrame::MailboxHolder> mailbox_holder; 354 355 { 356 scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture( 357 new VideoFrame::MailboxHolder( 358 mailbox, 359 sync_point, 360 base::Bind(&TextureCallback, &called_sync_point)), 361 target, 362 gfx::Size(10, 10), // coded_size 363 gfx::Rect(10, 10), // visible_rect 364 gfx::Size(10, 10), // natural_size 365 base::TimeDelta(), // timestamp 366 base::Callback<void(const SkBitmap&)>(), // read_pixels_cb 367 base::Closure()); // no_longer_needed_cb 368 369 mailbox_holder = frame->texture_mailbox(); 370 371 EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox().name[0]); 372 EXPECT_EQ(sync_point, mailbox_holder->sync_point()); 373 EXPECT_EQ(target, frame->texture_target()); 374 375 // Destroy the video frame. 376 } 377 EXPECT_EQ(0u, called_sync_point); 378 379 // Drop the reference on the mailbox without using it at all. 380 } 381 EXPECT_EQ(sync_point, called_sync_point); 382 } 383 384 // If a caller has taken ownership of the texture mailbox, but does 385 // not call the callback, it should still happen with the original 386 // sync point. 387 TEST(VideoFrame, 388 TextureNoLongerNeededCallbackAfterTakingMailboxAndNotCalling) { 389 uint32 called_sync_point = 0; 390 391 gpu::Mailbox mailbox; 392 mailbox.name[0] = 50; 393 uint32 sync_point = 7; 394 uint32 target = 9; 395 396 { 397 scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture( 398 new VideoFrame::MailboxHolder( 399 mailbox, 400 sync_point, 401 base::Bind(&TextureCallback, &called_sync_point)), 402 target, 403 gfx::Size(10, 10), // coded_size 404 gfx::Rect(10, 10), // visible_rect 405 gfx::Size(10, 10), // natural_size 406 base::TimeDelta(), // timestamp 407 base::Callback<void(const SkBitmap&)>(), // read_pixels_cb 408 base::Closure()); // no_longer_needed_cb 409 410 scoped_refptr<VideoFrame::MailboxHolder> mailbox_holder = 411 frame->texture_mailbox(); 412 413 EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox().name[0]); 414 EXPECT_EQ(sync_point, mailbox_holder->sync_point()); 415 EXPECT_EQ(target, frame->texture_target()); 416 417 EXPECT_EQ(0u, called_sync_point); 418 419 // Don't use the mailbox at all and drop our ref on it. 420 } 421 // The VideoFrame is destroyed, it should call the callback. 422 EXPECT_EQ(sync_point, called_sync_point); 423 } 424 425 } // namespace media 426