root/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. size_
  2. id
  3. data
  4. pool_
  5. ExpectDroppedId
  6. ReserveI420Buffer
  7. TEST_F

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Unit test for VideoCaptureBufferPool.

#include "content/browser/renderer_host/media/video_capture_buffer_pool.h"

#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/browser/renderer_host/media/video_capture_controller.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

class VideoCaptureBufferPoolTest : public testing::Test {
 protected:
  class Buffer {
   public:
    Buffer(const scoped_refptr<VideoCaptureBufferPool> pool,
           int id,
           void* data,
           size_t size)
        : pool_(pool), id_(id), data_(data), size_(size) {}
    ~Buffer() { pool_->RelinquishProducerReservation(id()); }
    int id() const { return id_; }
    void* data() const { return data_; }
    size_t size() const { return size_; }

   private:
    const scoped_refptr<VideoCaptureBufferPool> pool_;
    const int id_;
    void* const data_;
    const size_t size_;
  };
  VideoCaptureBufferPoolTest()
      : expected_dropped_id_(0),
        pool_(new VideoCaptureBufferPool(3)) {}

  void ExpectDroppedId(int expected_dropped_id) {
    expected_dropped_id_ = expected_dropped_id;
  }

  scoped_ptr<Buffer> ReserveI420Buffer(const gfx::Size& dimensions) {
    const size_t frame_bytes =
        media::VideoFrame::AllocationSize(media::VideoFrame::I420, dimensions);
    // To verify that ReserveI420Buffer always sets |buffer_id_to_drop|,
    // initialize it to something different than the expected value.
    int buffer_id_to_drop = ~expected_dropped_id_;
    int buffer_id = pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
    if (buffer_id == VideoCaptureBufferPool::kInvalidId)
      return scoped_ptr<Buffer>();

    void* memory;
    size_t size;
    pool_->GetBufferInfo(buffer_id, &memory, &size);
    EXPECT_EQ(expected_dropped_id_, buffer_id_to_drop);
    return scoped_ptr<Buffer>(new Buffer(pool_, buffer_id, memory, size));
  }

  int expected_dropped_id_;
  scoped_refptr<VideoCaptureBufferPool> pool_;

 private:
  DISALLOW_COPY_AND_ASSIGN(VideoCaptureBufferPoolTest);
};

TEST_F(VideoCaptureBufferPoolTest, BufferPool) {
  const gfx::Size size_lo = gfx::Size(640, 480);
  const gfx::Size size_hi = gfx::Size(1024, 768);
  scoped_refptr<media::VideoFrame> non_pool_frame =
      media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size_lo,
                                     gfx::Rect(size_lo), size_lo,
                                     base::TimeDelta());

  // Reallocation won't happen for the first part of the test.
  ExpectDroppedId(VideoCaptureBufferPool::kInvalidId);

  scoped_ptr<Buffer> buffer1 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer1.get());
  ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo),
            buffer1->size());
  scoped_ptr<Buffer> buffer2 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer2.get());
  ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo),
            buffer2->size());
  scoped_ptr<Buffer> buffer3 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer3.get());
  ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo),
            buffer3->size());

  // Touch the memory.
  memset(buffer1->data(), 0x11, buffer1->size());
  memset(buffer2->data(), 0x44, buffer2->size());
  memset(buffer3->data(), 0x77, buffer3->size());

  // Fourth buffer should fail.
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  // Release 1st buffer and retry; this should succeed.
  buffer1.reset();
  scoped_ptr<Buffer> buffer4 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer4.get());

  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";
  ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty";

  // Validate the IDs
  int buffer_id2 = buffer2->id();
  ASSERT_EQ(1, buffer_id2);
  int buffer_id3 = buffer3->id();
  ASSERT_EQ(2, buffer_id3);
  void* const memory_pointer3 = buffer3->data();
  int buffer_id4 = buffer4->id();
  ASSERT_EQ(0, buffer_id4);

  // Deliver a buffer.
  pool_->HoldForConsumers(buffer_id3, 2);

  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  buffer3.reset();  // Old producer releases buffer. Should be a noop.
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";
  ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty";

  buffer2.reset();  // Active producer releases buffer. Should free a buffer.

  buffer1 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer1.get());
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  // First consumer finishes.
  pool_->RelinquishConsumerHold(buffer_id3, 1);
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  // Second consumer finishes. This should free that buffer.
  pool_->RelinquishConsumerHold(buffer_id3, 1);
  buffer3 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer3.get());
  ASSERT_EQ(buffer_id3, buffer3->id()) << "Buffer ID should be reused.";
  ASSERT_EQ(memory_pointer3, buffer3->data());
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  // Now deliver & consume buffer1, but don't release the buffer.
  int buffer_id1 = buffer1->id();
  ASSERT_EQ(1, buffer_id1);
  pool_->HoldForConsumers(buffer_id1, 5);
  pool_->RelinquishConsumerHold(buffer_id1, 5);

  // Even though the consumer is done with the buffer at |buffer_id1|, it cannot
  // be re-allocated to the producer, because |buffer1| still references it. But
  // when |buffer1| goes away, we should be able to re-reserve the buffer (and
  // the ID ought to be the same).
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";
  buffer1.reset();  // Should free the buffer.
  buffer2 = ReserveI420Buffer(size_lo);
  ASSERT_TRUE(NULL != buffer2.get());
  ASSERT_EQ(buffer_id1, buffer2->id());
  buffer_id2 = buffer_id1;
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  // Now try reallocation with different resolutions. We expect reallocation
  // to occur only when the old buffer is too small.
  buffer2.reset();
  ExpectDroppedId(buffer_id2);
  buffer2 = ReserveI420Buffer(size_hi);
  ASSERT_TRUE(NULL != buffer2.get());
  ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_hi),
            buffer2->size());
  ASSERT_EQ(3, buffer2->id());
  void* const memory_pointer_hi = buffer2->data();
  buffer2.reset();  // Frees it.
  ExpectDroppedId(VideoCaptureBufferPool::kInvalidId);
  buffer2 = ReserveI420Buffer(size_lo);
  void* const memory_pointer_lo = buffer2->data();
  ASSERT_EQ(memory_pointer_hi, memory_pointer_lo)
      << "Decrease in resolution should not reallocate buffer";
  ASSERT_TRUE(NULL != buffer2.get());
  ASSERT_EQ(3, buffer2->id());
  ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo),
            buffer2->size());
  ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty";

  // Tear down the pool_, writing into the buffers. The buffer should preserve
  // the lifetime of the underlying memory.
  buffer3.reset();
  pool_ = NULL;

  // Touch the memory.
  memset(buffer2->data(), 0x22, buffer2->size());
  memset(buffer4->data(), 0x55, buffer4->size());

  buffer2.reset();

  memset(buffer4->data(), 0x77, buffer4->size());
  buffer4.reset();
}

} // namespace content

/* [<][>][^][v][top][bottom][index][help] */