Home | History | Annotate | Download | only in base
      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