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 18 namespace media { 19 20 using 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. 25 void 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|. 47 void 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. 91 void 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 122 TEST(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 162 TEST(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 198 static void FrameNoLongerNeededCallback( 199 const scoped_refptr<media::VideoFrame>& frame, 200 bool* triggered) { 201 *triggered = true; 202 } 203 204 TEST(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. 236 TEST(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 243 static void TextureCallback(std::vector<uint32>* called_sync_point, 244 const std::vector<uint32>& release_sync_points) { 245 called_sync_point->assign(release_sync_points.begin(), 246 release_sync_points.end()); 247 } 248 249 // Verify the gpu::MailboxHolder::ReleaseCallback is called when VideoFrame is 250 // destroyed with the default release sync points. 251 TEST(VideoFrame, TextureNoLongerNeededCallbackIsCalled) { 252 std::vector<uint32> called_sync_points; 253 called_sync_points.push_back(1); 254 255 { 256 scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture( 257 make_scoped_ptr( 258 new gpu::MailboxHolder(gpu::Mailbox(), 5, 0 /* sync_point */)), 259 base::Bind(&TextureCallback, &called_sync_points), 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 VideoFrame::ReadPixelsCB()); // read_pixels_cb 265 266 EXPECT_EQ(1u, called_sync_points.size()); 267 } 268 EXPECT_TRUE(called_sync_points.empty()); 269 } 270 271 // Verify the gpu::MailboxHolder::ReleaseCallback is called when VideoFrame is 272 // destroyed with the release sync points, which was updated by clients. 273 // (i.e. the compositor, webgl). 274 TEST(VideoFrame, TextureNoLongerNeededCallbackAfterTakingAndReleasingMailbox) { 275 std::vector<uint32> called_sync_points; 276 277 gpu::Mailbox mailbox; 278 mailbox.name[0] = 50; 279 uint32 sync_point = 7; 280 uint32 target = 9; 281 std::vector<uint32> release_sync_points; 282 release_sync_points.push_back(1); 283 release_sync_points.push_back(2); 284 release_sync_points.push_back(3); 285 286 { 287 scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture( 288 make_scoped_ptr(new gpu::MailboxHolder(mailbox, target, sync_point)), 289 base::Bind(&TextureCallback, &called_sync_points), 290 gfx::Size(10, 10), // coded_size 291 gfx::Rect(10, 10), // visible_rect 292 gfx::Size(10, 10), // natural_size 293 base::TimeDelta(), // timestamp 294 VideoFrame::ReadPixelsCB()); // read_pixels_cb 295 EXPECT_TRUE(called_sync_points.empty()); 296 297 const gpu::MailboxHolder* mailbox_holder = frame->mailbox_holder(); 298 299 EXPECT_EQ(mailbox.name[0], mailbox_holder->mailbox.name[0]); 300 EXPECT_EQ(target, mailbox_holder->texture_target); 301 EXPECT_EQ(sync_point, mailbox_holder->sync_point); 302 303 frame->AppendReleaseSyncPoint(release_sync_points[0]); 304 frame->AppendReleaseSyncPoint(release_sync_points[1]); 305 frame->AppendReleaseSyncPoint(release_sync_points[2]); 306 EXPECT_EQ(sync_point, mailbox_holder->sync_point); 307 } 308 EXPECT_EQ(release_sync_points, called_sync_points); 309 } 310 311 } // namespace media 312