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