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/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(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.
    250 TEST(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 
    269 namespace {
    270 
    271 class 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).
    287 TEST(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 
    318 TEST(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