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