Add I420 buffer pool to avoid unnecessary allocations
Now when we don't use SwapFrame consistently anymore, we need to recycle allocations with a buffer pool instead. This CL adds a buffer pool class, and updates the vp8 decoder to use it. If this CL lands successfully I will update the other video producers as well.
BUG=1128
R=stefan@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/41189004
Cr-Commit-Position: refs/heads/master@{#8748}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8748 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/common_video/BUILD.gn b/webrtc/common_video/BUILD.gn
index afe2be9..24423f5 100644
--- a/webrtc/common_video/BUILD.gn
+++ b/webrtc/common_video/BUILD.gn
@@ -17,8 +17,10 @@
source_set("common_video") {
sources = [
+ "i420_buffer_pool.cc",
"i420_video_frame.cc",
"interface/i420_video_frame.h",
+ "interface/i420_buffer_pool.h",
"interface/native_handle.h",
"interface/video_frame_buffer.h",
"libyuv/include/scaler.h",
diff --git a/webrtc/common_video/common_video.gyp b/webrtc/common_video/common_video.gyp
index 84cbc74..5a41201 100644
--- a/webrtc/common_video/common_video.gyp
+++ b/webrtc/common_video/common_video.gyp
@@ -39,9 +39,11 @@
}],
],
'sources': [
+ 'interface/i420_buffer_pool.h',
'interface/i420_video_frame.h',
'interface/native_handle.h',
'interface/video_frame_buffer.h',
+ 'i420_buffer_pool.cc',
'i420_video_frame.cc',
'libyuv/include/webrtc_libyuv.h',
'libyuv/include/scaler.h',
diff --git a/webrtc/common_video/common_video_unittests.gyp b/webrtc/common_video/common_video_unittests.gyp
index e79b063..beeab5d 100644
--- a/webrtc/common_video/common_video_unittests.gyp
+++ b/webrtc/common_video/common_video_unittests.gyp
@@ -19,6 +19,7 @@
'<(webrtc_root)/test/test.gyp:test_support_main',
],
'sources': [
+ 'i420_buffer_pool_unittest.cc',
'i420_video_frame_unittest.cc',
'libyuv/libyuv_unittest.cc',
'libyuv/scaler_unittest.cc',
diff --git a/webrtc/common_video/i420_buffer_pool.cc b/webrtc/common_video/i420_buffer_pool.cc
new file mode 100644
index 0000000..b6ad2ba
--- /dev/null
+++ b/webrtc/common_video/i420_buffer_pool.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/common_video/interface/i420_buffer_pool.h"
+
+#include "webrtc/base/checks.h"
+
+namespace {
+
+// One extra indirection is needed to make |HasOneRef| work.
+class PooledI420Buffer : public webrtc::VideoFrameBuffer {
+ public:
+ explicit PooledI420Buffer(
+ const rtc::scoped_refptr<webrtc::I420Buffer>& buffer)
+ : buffer_(buffer) {}
+
+ private:
+ ~PooledI420Buffer() override {}
+
+ int width() const override { return buffer_->width(); }
+ int height() const override { return buffer_->height(); }
+ const uint8_t* data(webrtc::PlaneType type) const override {
+ const webrtc::I420Buffer* cbuffer = buffer_.get();
+ return cbuffer->data(type);
+ }
+ uint8_t* data(webrtc::PlaneType type) {
+ DCHECK(HasOneRef());
+ const webrtc::I420Buffer* cbuffer = buffer_.get();
+ return const_cast<uint8_t*>(cbuffer->data(type));
+ }
+ int stride(webrtc::PlaneType type) const override {
+ return buffer_->stride(type);
+ }
+ rtc::scoped_refptr<webrtc::NativeHandle> native_handle() const override {
+ return nullptr;
+ }
+
+ friend class rtc::RefCountedObject<PooledI420Buffer>;
+ rtc::scoped_refptr<webrtc::I420Buffer> buffer_;
+};
+
+} // namespace
+
+namespace webrtc {
+
+I420BufferPool::I420BufferPool() {
+ thread_checker_.DetachFromThread();
+}
+
+rtc::scoped_refptr<VideoFrameBuffer> I420BufferPool::CreateBuffer(int width,
+ int height) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // Release buffers with wrong resolution.
+ for (auto it = buffers_.begin(); it != buffers_.end();) {
+ if ((*it)->width() != width || (*it)->height() != height)
+ it = buffers_.erase(it);
+ else
+ ++it;
+ }
+ // Look for a free buffer.
+ for (const rtc::scoped_refptr<I420Buffer>& buffer : buffers_) {
+ // If the buffer is in use, the ref count will be 2, one from the list we
+ // are looping over and one from a PooledI420Buffer returned from
+ // CreateBuffer that has not been released yet. If the ref count is 1
+ // (HasOneRef), then the list we are looping over holds the only reference
+ // and it's safe to reuse.
+ if (buffer->HasOneRef())
+ return new rtc::RefCountedObject<PooledI420Buffer>(buffer);
+ }
+ // Allocate new buffer.
+ buffers_.push_back(new rtc::RefCountedObject<I420Buffer>(width, height));
+ return new rtc::RefCountedObject<PooledI420Buffer>(buffers_.back());
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_video/i420_buffer_pool_unittest.cc b/webrtc/common_video/i420_buffer_pool_unittest.cc
new file mode 100644
index 0000000..625160b
--- /dev/null
+++ b/webrtc/common_video/i420_buffer_pool_unittest.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/common_video/interface/i420_buffer_pool.h"
+
+namespace webrtc {
+
+TEST(TestI420BufferPool, SimpleFrameReuse) {
+ I420BufferPool pool;
+ rtc::scoped_refptr<VideoFrameBuffer> buffer = pool.CreateBuffer(16, 16);
+ EXPECT_EQ(16, buffer->width());
+ EXPECT_EQ(16, buffer->height());
+ // Extract non-refcounted pointers for testing.
+ const uint8_t* y_ptr = buffer->data(kYPlane);
+ const uint8_t* u_ptr = buffer->data(kUPlane);
+ const uint8_t* v_ptr = buffer->data(kVPlane);
+ // Release buffer so that it is returned to the pool.
+ buffer = nullptr;
+ // Check that the memory is resued.
+ buffer = pool.CreateBuffer(16, 16);
+ EXPECT_EQ(y_ptr, buffer->data(kYPlane));
+ EXPECT_EQ(u_ptr, buffer->data(kUPlane));
+ EXPECT_EQ(v_ptr, buffer->data(kVPlane));
+ EXPECT_EQ(16, buffer->width());
+ EXPECT_EQ(16, buffer->height());
+}
+
+TEST(TestI420BufferPool, FailToReuse) {
+ I420BufferPool pool;
+ rtc::scoped_refptr<VideoFrameBuffer> buffer = pool.CreateBuffer(16, 16);
+ // Extract non-refcounted pointers for testing.
+ const uint8_t* u_ptr = buffer->data(kUPlane);
+ const uint8_t* v_ptr = buffer->data(kVPlane);
+ // Release buffer so that it is returned to the pool.
+ buffer = nullptr;
+ // Check that the pool doesn't try to reuse buffers of incorrect size.
+ buffer = pool.CreateBuffer(32, 16);
+ EXPECT_EQ(32, buffer->width());
+ EXPECT_EQ(16, buffer->height());
+ EXPECT_NE(u_ptr, buffer->data(kUPlane));
+ EXPECT_NE(v_ptr, buffer->data(kVPlane));
+}
+
+TEST(TestI420BufferPool, ExclusiveOwner) {
+ // Check that created buffers are exclusive so that they can be written to.
+ I420BufferPool pool;
+ rtc::scoped_refptr<VideoFrameBuffer> buffer = pool.CreateBuffer(16, 16);
+ EXPECT_TRUE(buffer->HasOneRef());
+}
+
+TEST(TestI420BufferPool, FrameValidAfterPoolDestruction) {
+ rtc::scoped_refptr<VideoFrameBuffer> buffer;
+ {
+ I420BufferPool pool;
+ buffer = pool.CreateBuffer(16, 16);
+ }
+ EXPECT_TRUE(buffer->HasOneRef());
+ EXPECT_EQ(16, buffer->width());
+ EXPECT_EQ(16, buffer->height());
+ // Try to trigger use-after-free errors by writing to y-plane.
+ memset(buffer->data(kYPlane), 0xA5, 16 * buffer->stride(kYPlane));
+}
+
+} // namespace webrtc
diff --git a/webrtc/common_video/interface/i420_buffer_pool.h b/webrtc/common_video/interface/i420_buffer_pool.h
new file mode 100644
index 0000000..e9dff8c
--- /dev/null
+++ b/webrtc/common_video/interface/i420_buffer_pool.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_COMMON_VIDEO_INTERFACE_I420_BUFFER_POOL_H_
+#define WEBRTC_COMMON_VIDEO_INTERFACE_I420_BUFFER_POOL_H_
+
+#include <list>
+
+#include "webrtc/base/thread_checker.h"
+#include "webrtc/common_video/interface/video_frame_buffer.h"
+
+namespace webrtc {
+
+// Simple buffer pool to avoid unnecessary allocations of I420Buffer objects.
+// The pool manages the memory of the I420Buffer returned from CreateBuffer.
+// When the I420Buffer is destructed, the memory is returned to the pool for use
+// by subsequent calls to CreateBuffer. If the resolution passed to CreateBuffer
+// changes, old buffers will be purged from the pool.
+class I420BufferPool {
+ public:
+ I420BufferPool();
+ // Returns a buffer from the pool, or creates a new buffer if no suitable
+ // buffer exists in the pool.
+ rtc::scoped_refptr<VideoFrameBuffer> CreateBuffer(int width, int height);
+
+ private:
+ rtc::ThreadChecker thread_checker_;
+ std::list<rtc::scoped_refptr<I420Buffer>> buffers_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_COMMON_VIDEO_INTERFACE_I420_BUFFER_POOL_H_