1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// 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
|