Index: content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc |
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc |
index e8f446fc6cf33f897760d147e737527fb5afc5d6..0de8cfd5187635da3990c4a07e8347f7e1e3f11a 100644 |
--- a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc |
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc |
@@ -9,6 +9,10 @@ |
#include "base/bind.h" |
#include "base/memory/ref_counted.h" |
#include "base/memory/scoped_ptr.h" |
+#include "cc/test/test_context_provider.h" |
+#include "cc/test/test_web_graphics_context_3d.h" |
+#include "content/browser/compositor/buffer_queue.h" |
+#include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" |
#include "content/browser/renderer_host/media/video_capture_controller.h" |
#include "media/base/video_frame.h" |
#include "media/base/video_util.h" |
@@ -16,132 +20,222 @@ |
namespace content { |
-class VideoCaptureBufferPoolTest : public testing::Test { |
+static const media::VideoPixelFormat kCaptureFormats[] = { |
+ media::PIXEL_FORMAT_I420, |
+ media::PIXEL_FORMAT_TEXTURE, |
+ media::PIXEL_FORMAT_GPUMEMORYBUFFER |
+}; |
+ |
+class VideoCaptureBufferPoolTest |
+ : public testing::TestWithParam<media::VideoPixelFormat> { |
protected: |
+ // A GpuMemoryBuffer Mock to provide a trivial RGBA buffer as Map() backing. |
+ // We need to allocate on ctor and deallocate on dtor so that consecutive |
+ // Map()-Unmap() cycles yield the same underlying data pointer. |
+ class MockGpuMemoryBuffer : public gfx::GpuMemoryBuffer { |
+ public: |
+ explicit MockGpuMemoryBuffer(const gfx::Size& size) |
+ : size_(size), data_(new uint8[size_.GetArea() * 4]), mapped_(false) {} |
+ ~MockGpuMemoryBuffer() override { delete[] data_; } |
+ |
+ bool Map(void** data) override { |
+ EXPECT_EQ(mapped_, false); |
+ mapped_ = true; |
+ data[0] = static_cast<void*>(data_); |
+ return true; |
+ } |
+ void Unmap() override { |
+ EXPECT_EQ(mapped_, true); |
+ mapped_ = false; |
+ } |
+ bool IsMapped() const override { return mapped_; } |
+ Format GetFormat() const override { return BGRA_8888; } |
+ void GetStride(uint32* stride) const override { return; } |
+ gfx::GpuMemoryBufferHandle GetHandle() const override { |
+ return gfx::GpuMemoryBufferHandle(); |
+ } |
+ ClientBuffer AsClientBuffer() override { return nullptr; } |
+ |
+ private: |
+ const gfx::Size size_; |
+ uint8* const data_; |
+ bool mapped_; |
+ }; |
+ |
+ // The next two classes are needed to replicate the GpuMemoryBuffer allocation |
+ // on Browser side. |
+ class StubBrowserGpuMemoryBufferManager |
+ : public BrowserGpuMemoryBufferManager { |
+ public: |
+ StubBrowserGpuMemoryBufferManager() |
+ : BrowserGpuMemoryBufferManager(nullptr, 1) {} |
+ |
+ scoped_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer( |
+ const gfx::Size& size, |
+ gfx::GpuMemoryBuffer::Format format, |
+ gfx::GpuMemoryBuffer::Usage usage) override { |
+ return make_scoped_ptr(new MockGpuMemoryBuffer(size)); |
+ } |
+ }; |
+ class MockBufferQueue : public BufferQueue { |
+ public: |
+ MockBufferQueue(scoped_refptr<cc::ContextProvider> context_provider, |
+ BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager, |
+ unsigned int internalformat) |
+ : BufferQueue(context_provider, |
+ internalformat, |
+ nullptr, |
+ gpu_memory_buffer_manager, |
+ 1) {} |
+ }; |
+ |
+ // This is a generic Buffer tracker |
class Buffer { |
public: |
Buffer(const scoped_refptr<VideoCaptureBufferPool> pool, |
- int id, |
- void* data, |
- size_t size) |
- : pool_(pool), id_(id), data_(data), size_(size) {} |
+ scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle, |
+ int id) |
+ : pool_(pool), id_(id), buffer_handle_(buffer_handle.Pass()) {} |
~Buffer() { pool_->RelinquishProducerReservation(id()); } |
int id() const { return id_; } |
- void* data() const { return data_; } |
- size_t size() const { return size_; } |
+ scoped_ptr<media::DataHandle> GetDataHandle() { |
+ return buffer_handle_->GetDataHandle().Pass(); |
+ } |
+ size_t size() { return buffer_handle_->size(); } |
private: |
const scoped_refptr<VideoCaptureBufferPool> pool_; |
const int id_; |
- void* const data_; |
- const size_t size_; |
+ const scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle_; |
}; |
+ |
VideoCaptureBufferPoolTest() |
: expected_dropped_id_(0), |
pool_(new VideoCaptureBufferPool(3)) {} |
+ void SetUp() override { |
+ scoped_refptr<cc::TestContextProvider> context_provider = |
+ cc::TestContextProvider::Create(cc::TestWebGraphicsContext3D::Create()); |
+ context_provider->BindToCurrentThread(); |
+ gpu_memory_buffer_manager_.reset(new StubBrowserGpuMemoryBufferManager); |
+ output_surface_.reset(new MockBufferQueue( |
+ context_provider, gpu_memory_buffer_manager_.get(), GL_RGBA)); |
+ output_surface_->Initialize(); |
+ } |
+ |
void ExpectDroppedId(int expected_dropped_id) { |
expected_dropped_id_ = expected_dropped_id; |
} |
- scoped_ptr<Buffer> ReserveI420Buffer(const gfx::Size& dimensions) { |
- // To verify that ReserveI420Buffer always sets |buffer_id_to_drop|, |
+ scoped_ptr<Buffer> ReserveBuffer(const gfx::Size& dimensions, |
+ media::VideoPixelFormat pixel_format) { |
+ // To verify that ReserveBuffer 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(media::PIXEL_FORMAT_I420, |
- dimensions, &buffer_id_to_drop); |
+ DVLOG(1) << media::VideoCaptureFormat::PixelFormatToString(pixel_format) |
+ << " " << dimensions.ToString(); |
+ int buffer_id = |
+ pool_->ReserveForProducer(pixel_format, dimensions, &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)); |
+ |
+ scoped_ptr<VideoCaptureBufferPool::BufferHandle> buffer_handle = |
+ pool_->GetBufferHandle(buffer_id); |
+ return scoped_ptr<Buffer>( |
+ new Buffer(pool_, buffer_handle.Pass(), buffer_id)); |
} |
int expected_dropped_id_; |
scoped_refptr<VideoCaptureBufferPool> pool_; |
private: |
+ scoped_ptr<StubBrowserGpuMemoryBufferManager> gpu_memory_buffer_manager_; |
+ scoped_ptr<MockBufferQueue> output_surface_; |
+ |
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()); |
+TEST_P(VideoCaptureBufferPoolTest, BufferPool) { |
+ const gfx::Size size_lo = gfx::Size(10, 10); |
+ const gfx::Size size_hi = gfx::Size(21, 33); |
+ const media::VideoCaptureFormat format_lo(size_lo, 0.0, GetParam()); |
+ const media::VideoCaptureFormat format_hi(size_hi, 0.0, GetParam()); |
// 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()); |
+ scoped_ptr<Buffer> buffer1 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, buffer1.get()); |
+ ASSERT_LE(format_lo.ImageAllocationSize(), buffer1->size()); |
+ scoped_ptr<Buffer> buffer2 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, buffer2.get()); |
+ ASSERT_LE(format_lo.ImageAllocationSize(), buffer2->size()); |
+ scoped_ptr<Buffer> buffer3 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, buffer3.get()); |
+ ASSERT_LE(format_lo.ImageAllocationSize(), buffer3->size()); |
+ |
+ // Texture backed Frames cannot be manipulated via mapping. |
+ if (GetParam() != media::PIXEL_FORMAT_TEXTURE) { |
+ ASSERT_NE(nullptr, buffer1->GetDataHandle()->data()); |
+ ASSERT_NE(nullptr, buffer2->GetDataHandle()->data()); |
+ ASSERT_NE(nullptr, buffer3->GetDataHandle()->data()); |
+ } |
// Touch the memory. |
- memset(buffer1->data(), 0x11, buffer1->size()); |
- memset(buffer2->data(), 0x44, buffer2->size()); |
- memset(buffer3->data(), 0x77, buffer3->size()); |
+ if (buffer1->GetDataHandle()->data() != nullptr) |
+ memset(buffer1->GetDataHandle()->data(), 0x11, buffer1->size()); |
+ if (buffer2->GetDataHandle()->data() != nullptr) |
+ memset(buffer2->GetDataHandle()->data(), 0x44, buffer2->size()); |
+ if (buffer3->GetDataHandle()->data() != nullptr) |
+ memset(buffer3->GetDataHandle()->data(), 0x77, buffer3->size()); |
// Fourth buffer should fail. |
- ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "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()); |
+ scoped_ptr<Buffer> buffer4 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, buffer4.get()); |
- ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; |
- ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_hi, GetParam())) << "Pool should be empty"; |
// Validate the IDs |
int buffer_id2 = buffer2->id(); |
ASSERT_EQ(1, buffer_id2); |
- int buffer_id3 = buffer3->id(); |
+ const int buffer_id3 = buffer3->id(); |
ASSERT_EQ(2, buffer_id3); |
- void* const memory_pointer3 = buffer3->data(); |
- int buffer_id4 = buffer4->id(); |
+ const int buffer_id4 = buffer4->id(); |
ASSERT_EQ(0, buffer_id4); |
+ void* const memory_pointer3 = buffer3->GetDataHandle()->data(); |
// Deliver a buffer. |
pool_->HoldForConsumers(buffer_id3, 2); |
- ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "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"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_hi, GetParam())) << "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"; |
+ buffer1 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, buffer1.get()); |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; |
// First consumer finishes. |
pool_->RelinquishConsumerHold(buffer_id3, 1); |
- ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "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()); |
+ buffer3 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, 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"; |
+ ASSERT_EQ(memory_pointer3, buffer3->GetDataHandle()->data()); |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; |
// Now deliver & consume buffer1, but don't release the buffer. |
int buffer_id1 = buffer1->id(); |
@@ -153,35 +247,33 @@ TEST_F(VideoCaptureBufferPoolTest, BufferPool) { |
// 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"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; |
buffer1.reset(); // Should free the buffer. |
- buffer2 = ReserveI420Buffer(size_lo); |
- ASSERT_TRUE(NULL != buffer2.get()); |
+ buffer2 = ReserveBuffer(size_lo, GetParam()); |
+ ASSERT_NE(nullptr, buffer2.get()); |
ASSERT_EQ(buffer_id1, buffer2->id()); |
buffer_id2 = buffer_id1; |
- ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "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()); |
+ buffer2 = ReserveBuffer(size_hi, GetParam()); |
+ ASSERT_NE(nullptr, buffer2.get()); |
+ ASSERT_LE(format_hi.ImageAllocationSize(), buffer2->size()); |
ASSERT_EQ(3, buffer2->id()); |
- void* const memory_pointer_hi = buffer2->data(); |
+ void* const memory_pointer_hi = buffer2->GetDataHandle()->data(); |
buffer2.reset(); // Frees it. |
ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); |
- buffer2 = ReserveI420Buffer(size_lo); |
- void* const memory_pointer_lo = buffer2->data(); |
+ buffer2 = ReserveBuffer(size_lo, GetParam()); |
+ void* const memory_pointer_lo = buffer2->GetDataHandle()->data(); |
ASSERT_EQ(memory_pointer_hi, memory_pointer_lo) |
<< "Decrease in resolution should not reallocate buffer"; |
- ASSERT_TRUE(NULL != buffer2.get()); |
+ ASSERT_NE(nullptr, 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"; |
+ ASSERT_LE(format_lo.ImageAllocationSize(), buffer2->size()); |
+ ASSERT_FALSE(ReserveBuffer(size_lo, GetParam())) << "Pool should be empty"; |
// Tear down the pool_, writing into the buffers. The buffer should preserve |
// the lifetime of the underlying memory. |
@@ -189,13 +281,19 @@ TEST_F(VideoCaptureBufferPoolTest, BufferPool) { |
pool_ = NULL; |
// Touch the memory. |
- memset(buffer2->data(), 0x22, buffer2->size()); |
- memset(buffer4->data(), 0x55, buffer4->size()); |
- |
+ if (buffer2->GetDataHandle()->data() != nullptr) |
+ memset(buffer2->GetDataHandle()->data(), 0x22, buffer2->size()); |
+ if (buffer4->GetDataHandle()->data() != nullptr) |
+ memset(buffer4->GetDataHandle()->data(), 0x55, buffer4->size()); |
buffer2.reset(); |
- memset(buffer4->data(), 0x77, buffer4->size()); |
+ if (buffer4->GetDataHandle()->data() != nullptr) |
+ memset(buffer4->GetDataHandle()->data(), 0x77, buffer4->size()); |
buffer4.reset(); |
} |
+INSTANTIATE_TEST_CASE_P(, |
+ VideoCaptureBufferPoolTest, |
+ testing::ValuesIn(kCaptureFormats)); |
+ |
} // namespace content |