1 // Copyright (c) 2013 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 // Unit test for VideoCaptureBufferPool. 6 7 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" 8 9 #include "base/bind.h" 10 #include "base/memory/ref_counted.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "content/browser/renderer_host/media/video_capture_controller.h" 13 #include "media/base/video_frame.h" 14 #include "media/base/video_util.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 namespace content { 18 19 class VideoCaptureBufferPoolTest : public testing::Test { 20 protected: 21 class Buffer { 22 public: 23 Buffer(const scoped_refptr<VideoCaptureBufferPool> pool, 24 int id, 25 void* data, 26 size_t size) 27 : pool_(pool), id_(id), data_(data), size_(size) {} 28 ~Buffer() { pool_->RelinquishProducerReservation(id()); } 29 int id() const { return id_; } 30 void* data() const { return data_; } 31 size_t size() const { return size_; } 32 33 private: 34 const scoped_refptr<VideoCaptureBufferPool> pool_; 35 const int id_; 36 void* const data_; 37 const size_t size_; 38 }; 39 VideoCaptureBufferPoolTest() 40 : expected_dropped_id_(0), 41 pool_(new VideoCaptureBufferPool(3)) {} 42 43 void ExpectDroppedId(int expected_dropped_id) { 44 expected_dropped_id_ = expected_dropped_id; 45 } 46 47 scoped_ptr<Buffer> ReserveI420Buffer(const gfx::Size& dimensions) { 48 const size_t frame_bytes = 49 media::VideoFrame::AllocationSize(media::VideoFrame::I420, dimensions); 50 // To verify that ReserveI420Buffer always sets |buffer_id_to_drop|, 51 // initialize it to something different than the expected value. 52 int buffer_id_to_drop = ~expected_dropped_id_; 53 int buffer_id = pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop); 54 if (buffer_id == VideoCaptureBufferPool::kInvalidId) 55 return scoped_ptr<Buffer>(); 56 57 void* memory; 58 size_t size; 59 pool_->GetBufferInfo(buffer_id, &memory, &size); 60 EXPECT_EQ(expected_dropped_id_, buffer_id_to_drop); 61 return scoped_ptr<Buffer>(new Buffer(pool_, buffer_id, memory, size)); 62 } 63 64 int expected_dropped_id_; 65 scoped_refptr<VideoCaptureBufferPool> pool_; 66 67 private: 68 DISALLOW_COPY_AND_ASSIGN(VideoCaptureBufferPoolTest); 69 }; 70 71 TEST_F(VideoCaptureBufferPoolTest, BufferPool) { 72 const gfx::Size size_lo = gfx::Size(640, 480); 73 const gfx::Size size_hi = gfx::Size(1024, 768); 74 scoped_refptr<media::VideoFrame> non_pool_frame = 75 media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size_lo, 76 gfx::Rect(size_lo), size_lo, 77 base::TimeDelta()); 78 79 // Reallocation won't happen for the first part of the test. 80 ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); 81 82 scoped_ptr<Buffer> buffer1 = ReserveI420Buffer(size_lo); 83 ASSERT_TRUE(NULL != buffer1.get()); 84 ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), 85 buffer1->size()); 86 scoped_ptr<Buffer> buffer2 = ReserveI420Buffer(size_lo); 87 ASSERT_TRUE(NULL != buffer2.get()); 88 ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), 89 buffer2->size()); 90 scoped_ptr<Buffer> buffer3 = ReserveI420Buffer(size_lo); 91 ASSERT_TRUE(NULL != buffer3.get()); 92 ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), 93 buffer3->size()); 94 95 // Touch the memory. 96 memset(buffer1->data(), 0x11, buffer1->size()); 97 memset(buffer2->data(), 0x44, buffer2->size()); 98 memset(buffer3->data(), 0x77, buffer3->size()); 99 100 // Fourth buffer should fail. 101 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 102 103 // Release 1st buffer and retry; this should succeed. 104 buffer1.reset(); 105 scoped_ptr<Buffer> buffer4 = ReserveI420Buffer(size_lo); 106 ASSERT_TRUE(NULL != buffer4.get()); 107 108 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 109 ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty"; 110 111 // Validate the IDs 112 int buffer_id2 = buffer2->id(); 113 ASSERT_EQ(1, buffer_id2); 114 int buffer_id3 = buffer3->id(); 115 ASSERT_EQ(2, buffer_id3); 116 void* const memory_pointer3 = buffer3->data(); 117 int buffer_id4 = buffer4->id(); 118 ASSERT_EQ(0, buffer_id4); 119 120 // Deliver a buffer. 121 pool_->HoldForConsumers(buffer_id3, 2); 122 123 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 124 125 buffer3.reset(); // Old producer releases buffer. Should be a noop. 126 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 127 ASSERT_FALSE(ReserveI420Buffer(size_hi)) << "Pool should be empty"; 128 129 buffer2.reset(); // Active producer releases buffer. Should free a buffer. 130 131 buffer1 = ReserveI420Buffer(size_lo); 132 ASSERT_TRUE(NULL != buffer1.get()); 133 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 134 135 // First consumer finishes. 136 pool_->RelinquishConsumerHold(buffer_id3, 1); 137 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 138 139 // Second consumer finishes. This should free that buffer. 140 pool_->RelinquishConsumerHold(buffer_id3, 1); 141 buffer3 = ReserveI420Buffer(size_lo); 142 ASSERT_TRUE(NULL != buffer3.get()); 143 ASSERT_EQ(buffer_id3, buffer3->id()) << "Buffer ID should be reused."; 144 ASSERT_EQ(memory_pointer3, buffer3->data()); 145 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 146 147 // Now deliver & consume buffer1, but don't release the buffer. 148 int buffer_id1 = buffer1->id(); 149 ASSERT_EQ(1, buffer_id1); 150 pool_->HoldForConsumers(buffer_id1, 5); 151 pool_->RelinquishConsumerHold(buffer_id1, 5); 152 153 // Even though the consumer is done with the buffer at |buffer_id1|, it cannot 154 // be re-allocated to the producer, because |buffer1| still references it. But 155 // when |buffer1| goes away, we should be able to re-reserve the buffer (and 156 // the ID ought to be the same). 157 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 158 buffer1.reset(); // Should free the buffer. 159 buffer2 = ReserveI420Buffer(size_lo); 160 ASSERT_TRUE(NULL != buffer2.get()); 161 ASSERT_EQ(buffer_id1, buffer2->id()); 162 buffer_id2 = buffer_id1; 163 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 164 165 // Now try reallocation with different resolutions. We expect reallocation 166 // to occur only when the old buffer is too small. 167 buffer2.reset(); 168 ExpectDroppedId(buffer_id2); 169 buffer2 = ReserveI420Buffer(size_hi); 170 ASSERT_TRUE(NULL != buffer2.get()); 171 ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_hi), 172 buffer2->size()); 173 ASSERT_EQ(3, buffer2->id()); 174 void* const memory_pointer_hi = buffer2->data(); 175 buffer2.reset(); // Frees it. 176 ExpectDroppedId(VideoCaptureBufferPool::kInvalidId); 177 buffer2 = ReserveI420Buffer(size_lo); 178 void* const memory_pointer_lo = buffer2->data(); 179 ASSERT_EQ(memory_pointer_hi, memory_pointer_lo) 180 << "Decrease in resolution should not reallocate buffer"; 181 ASSERT_TRUE(NULL != buffer2.get()); 182 ASSERT_EQ(3, buffer2->id()); 183 ASSERT_LE(media::VideoFrame::AllocationSize(media::VideoFrame::I420, size_lo), 184 buffer2->size()); 185 ASSERT_FALSE(ReserveI420Buffer(size_lo)) << "Pool should be empty"; 186 187 // Tear down the pool_, writing into the buffers. The buffer should preserve 188 // the lifetime of the underlying memory. 189 buffer3.reset(); 190 pool_ = NULL; 191 192 // Touch the memory. 193 memset(buffer2->data(), 0x22, buffer2->size()); 194 memset(buffer4->data(), 0x55, buffer4->size()); 195 196 buffer2.reset(); 197 198 memset(buffer4->data(), 0x77, buffer4->size()); 199 buffer4.reset(); 200 } 201 202 } // namespace content 203