diff --git a/api/video/nv12_buffer.cc b/api/video/nv12_buffer.cc index 54c27a8d78..4604f1dcea 100644 --- a/api/video/nv12_buffer.cc +++ b/api/video/nv12_buffer.cc @@ -105,4 +105,8 @@ size_t NV12Buffer::UVOffset() const { return stride_y_ * height_; } +void NV12Buffer::InitializeData() { + memset(data_.get(), 0, NV12DataSize(height_, stride_y_, stride_uv_)); +} + } // namespace webrtc diff --git a/api/video/nv12_buffer.h b/api/video/nv12_buffer.h index 8eb484c069..7ffef9100b 100644 --- a/api/video/nv12_buffer.h +++ b/api/video/nv12_buffer.h @@ -46,6 +46,14 @@ class RTC_EXPORT NV12Buffer : public NV12BufferInterface { uint8_t* MutableDataY(); uint8_t* MutableDataUV(); + // Sets all three planes to all zeros. Used to work around for + // quirks in memory checkers + // (https://bugs.chromium.org/p/libyuv/issues/detail?id=377) and + // ffmpeg (http://crbug.com/390941). + // TODO(nisse): Deprecated. Should be deleted if/when those issues + // are resolved in a better way. Or in the mean time, use SetBlack. + void InitializeData(); + protected: NV12Buffer(int width, int height); NV12Buffer(int width, int height, int stride_y, int stride_uv); diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn index 28ffcb220e..5452520733 100644 --- a/common_video/BUILD.gn +++ b/common_video/BUILD.gn @@ -26,16 +26,17 @@ rtc_library("common_video") { "h264/sps_parser.h", "h264/sps_vui_rewriter.cc", "h264/sps_vui_rewriter.h", - "i420_buffer_pool.cc", "include/bitrate_adjuster.h", "include/i420_buffer_pool.h", "include/incoming_video_stream.h", "include/quality_limitation_reason.h", "include/video_frame_buffer.h", + "include/video_frame_buffer_pool.h", "incoming_video_stream.cc", "libyuv/include/webrtc_libyuv.h", "libyuv/webrtc_libyuv.cc", "video_frame_buffer.cc", + "video_frame_buffer_pool.cc", "video_render_frames.cc", "video_render_frames.h", ] @@ -50,6 +51,7 @@ rtc_library("common_video") { "../api/video:video_bitrate_allocator", "../api/video:video_frame", "../api/video:video_frame_i420", + "../api/video:video_frame_nv12", "../api/video:video_rtp_headers", "../api/video_codecs:bitstream_parser_api", "../media:rtc_h264_profile_id", @@ -93,8 +95,8 @@ if (rtc_include_tests) { "h264/profile_level_id_unittest.cc", "h264/sps_parser_unittest.cc", "h264/sps_vui_rewriter_unittest.cc", - "i420_buffer_pool_unittest.cc", "libyuv/libyuv_unittest.cc", + "video_frame_buffer_pool_unittest.cc", "video_frame_unittest.cc", ] diff --git a/common_video/i420_buffer_pool.cc b/common_video/i420_buffer_pool.cc deleted file mode 100644 index d13da6a172..0000000000 --- a/common_video/i420_buffer_pool.cc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 "common_video/include/i420_buffer_pool.h" - -#include - -#include "rtc_base/checks.h" - -namespace webrtc { - -I420BufferPool::I420BufferPool() : I420BufferPool(false) {} -I420BufferPool::I420BufferPool(bool zero_initialize) - : I420BufferPool(zero_initialize, std::numeric_limits::max()) {} -I420BufferPool::I420BufferPool(bool zero_initialize, - size_t max_number_of_buffers) - : zero_initialize_(zero_initialize), - max_number_of_buffers_(max_number_of_buffers) {} -I420BufferPool::~I420BufferPool() = default; - -void I420BufferPool::Release() { - buffers_.clear(); -} - -bool I420BufferPool::Resize(size_t max_number_of_buffers) { - RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); - size_t used_buffers_count = 0; - for (const rtc::scoped_refptr& 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 the application. If the ref count is 1, - // then the list we are looping over holds the only reference and it's safe - // to reuse. - if (!buffer->HasOneRef()) { - used_buffers_count++; - } - } - if (used_buffers_count > max_number_of_buffers) { - return false; - } - max_number_of_buffers_ = max_number_of_buffers; - - size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_; - auto iter = buffers_.begin(); - while (iter != buffers_.end() && buffers_to_purge > 0) { - if ((*iter)->HasOneRef()) { - iter = buffers_.erase(iter); - buffers_to_purge--; - } else { - ++iter; - } - } - return true; -} - -rtc::scoped_refptr I420BufferPool::CreateBuffer(int width, - int height) { - // Default stride_y is width, default uv stride is width / 2 (rounding up). - return CreateBuffer(width, height, width, (width + 1) / 2, (width + 1) / 2); -} - -rtc::scoped_refptr I420BufferPool::CreateBuffer(int width, - int height, - int stride_y, - int stride_u, - int stride_v) { - RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); - // Release buffers with wrong resolution. - for (auto it = buffers_.begin(); it != buffers_.end();) { - const auto& buffer = *it; - if (buffer->width() != width || buffer->height() != height || - buffer->StrideY() != stride_y || buffer->StrideU() != stride_u || - buffer->StrideV() != stride_v) { - it = buffers_.erase(it); - } else { - ++it; - } - } - // Look for a free buffer. - for (const rtc::scoped_refptr& 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 the application. If the ref count is 1, - // then the list we are looping over holds the only reference and it's safe - // to reuse. - if (buffer->HasOneRef()) - return buffer; - } - - if (buffers_.size() >= max_number_of_buffers_) - return nullptr; - // Allocate new buffer. - rtc::scoped_refptr buffer = - new PooledI420Buffer(width, height, stride_y, stride_u, stride_v); - if (zero_initialize_) - buffer->InitializeData(); - buffers_.push_back(buffer); - return buffer; -} - -} // namespace webrtc diff --git a/common_video/i420_buffer_pool_unittest.cc b/common_video/i420_buffer_pool_unittest.cc deleted file mode 100644 index 27503e5b8a..0000000000 --- a/common_video/i420_buffer_pool_unittest.cc +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 "common_video/include/i420_buffer_pool.h" - -#include -#include - -#include "api/scoped_refptr.h" -#include "api/video/i420_buffer.h" -#include "api/video/video_frame_buffer.h" -#include "test/gtest.h" - -namespace webrtc { - -TEST(TestI420BufferPool, SimpleFrameReuse) { - I420BufferPool pool; - auto 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->DataY(); - const uint8_t* u_ptr = buffer->DataU(); - const uint8_t* v_ptr = buffer->DataV(); - // 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->DataY()); - EXPECT_EQ(u_ptr, buffer->DataU()); - EXPECT_EQ(v_ptr, buffer->DataV()); -} - -TEST(TestI420BufferPool, FrameReuseWithDefaultThenExplicitStride) { - I420BufferPool pool; - auto buffer = pool.CreateBuffer(15, 16); - EXPECT_EQ(15, buffer->width()); - EXPECT_EQ(16, buffer->height()); - // The default Y stride is width and UV stride is halfwidth (rounded up). - ASSERT_EQ(15, buffer->StrideY()); - ASSERT_EQ(8, buffer->StrideU()); - ASSERT_EQ(8, buffer->StrideV()); - // Extract non-refcounted pointers for testing. - const uint8_t* y_ptr = buffer->DataY(); - const uint8_t* u_ptr = buffer->DataU(); - const uint8_t* v_ptr = buffer->DataV(); - // Release buffer so that it is returned to the pool. - buffer = nullptr; - // Check that the memory is resued with explicit strides if they match the - // assumed default above. - buffer = pool.CreateBuffer(15, 16, 15, 8, 8); - EXPECT_EQ(y_ptr, buffer->DataY()); - EXPECT_EQ(u_ptr, buffer->DataU()); - EXPECT_EQ(v_ptr, buffer->DataV()); - EXPECT_EQ(15, buffer->width()); - EXPECT_EQ(16, buffer->height()); - EXPECT_EQ(15, buffer->StrideY()); - EXPECT_EQ(8, buffer->StrideU()); - EXPECT_EQ(8, buffer->StrideV()); -} - -TEST(TestI420BufferPool, FailToReuseWrongSize) { - // Set max frames to 1, just to make sure the first buffer is being released. - I420BufferPool pool(/*zero_initialize=*/false, 1); - auto buffer = pool.CreateBuffer(16, 16); - EXPECT_EQ(16, buffer->width()); - EXPECT_EQ(16, buffer->height()); - // 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); - ASSERT_TRUE(buffer); - EXPECT_EQ(32, buffer->width()); - EXPECT_EQ(16, buffer->height()); -} - -TEST(TestI420BufferPool, FailToReuseWrongStride) { - // Set max frames to 1, just to make sure the first buffer is being released. - I420BufferPool pool(/*zero_initialize=*/false, 1); - auto buffer = pool.CreateBuffer(32, 32, 32, 16, 16); - // Make sure the stride was read correctly, for the rest of the test. - ASSERT_EQ(16, buffer->StrideU()); - ASSERT_EQ(16, buffer->StrideV()); - buffer = pool.CreateBuffer(32, 32, 32, 20, 20); - ASSERT_TRUE(buffer); - EXPECT_EQ(32, buffer->StrideY()); - EXPECT_EQ(20, buffer->StrideU()); - EXPECT_EQ(20, buffer->StrideV()); -} - -TEST(TestI420BufferPool, FrameValidAfterPoolDestruction) { - rtc::scoped_refptr buffer; - { - I420BufferPool pool; - buffer = pool.CreateBuffer(16, 16); - } - EXPECT_EQ(16, buffer->width()); - EXPECT_EQ(16, buffer->height()); - // Try to trigger use-after-free errors by writing to y-plane. - memset(buffer->MutableDataY(), 0xA5, 16 * buffer->StrideY()); -} - -TEST(TestI420BufferPool, MaxNumberOfBuffers) { - I420BufferPool pool(false, 1); - auto buffer1 = pool.CreateBuffer(16, 16); - EXPECT_NE(nullptr, buffer1.get()); - EXPECT_EQ(nullptr, pool.CreateBuffer(16, 16).get()); -} - -} // namespace webrtc diff --git a/common_video/include/i420_buffer_pool.h b/common_video/include/i420_buffer_pool.h index 44f4821798..689fb3e1b1 100644 --- a/common_video/include/i420_buffer_pool.h +++ b/common_video/include/i420_buffer_pool.h @@ -17,61 +17,25 @@ #include "api/scoped_refptr.h" #include "api/video/i420_buffer.h" +#include "common_video/include/video_frame_buffer_pool.h" #include "rtc_base/race_checker.h" #include "rtc_base/ref_counted_object.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. -// Note that CreateBuffer will crash if more than kMaxNumberOfFramesBeforeCrash -// are created. This is to prevent memory leaks where frames are not returned. -class I420BufferPool { +// Deprecated. Use VideoFrameBufferPool instead. +class I420BufferPool : public VideoFrameBufferPool { public: - I420BufferPool(); - explicit I420BufferPool(bool zero_initialize); - I420BufferPool(bool zero_initialze, size_t max_number_of_buffers); - ~I420BufferPool(); + I420BufferPool() : VideoFrameBufferPool() {} + explicit I420BufferPool(bool zero_initialize) + : VideoFrameBufferPool(zero_initialize) {} + I420BufferPool(bool zero_initialze, size_t max_number_of_buffers) + : VideoFrameBufferPool(zero_initialze, max_number_of_buffers) {} + ~I420BufferPool() = default; - // Returns a buffer from the pool. If no suitable buffer exist in the pool - // and there are less than |max_number_of_buffers| pending, a buffer is - // created. Returns null otherwise. - rtc::scoped_refptr CreateBuffer(int width, int height); - - // Returns a buffer from the pool with the explicitly specified stride. - rtc::scoped_refptr CreateBuffer(int width, - int height, - int stride_y, - int stride_u, - int stride_v); - - // Changes the max amount of buffers in the pool to the new value. - // Returns true if change was successful and false if the amount of already - // allocated buffers is bigger than new value. - bool Resize(size_t max_number_of_buffers); - - // Clears buffers_ and detaches the thread checker so that it can be reused - // later from another thread. - void Release(); - - private: - // Explicitly use a RefCountedObject to get access to HasOneRef, - // needed by the pool to check exclusive access. - using PooledI420Buffer = rtc::RefCountedObject; - - rtc::RaceChecker race_checker_; - std::list> buffers_; - // If true, newly allocated buffers are zero-initialized. Note that recycled - // buffers are not zero'd before reuse. This is required of buffers used by - // FFmpeg according to http://crbug.com/390941, which only requires it for the - // initial allocation (as shown by FFmpeg's own buffer allocation code). It - // has to do with "Use-of-uninitialized-value" on "Linux_msan_chrome". - const bool zero_initialize_; - // Max number of buffers this pool can have pending. - size_t max_number_of_buffers_; + rtc::scoped_refptr CreateBuffer(int width, int height) { + return VideoFrameBufferPool::CreateI420Buffer(width, height); + } }; } // namespace webrtc diff --git a/common_video/include/video_frame_buffer_pool.h b/common_video/include/video_frame_buffer_pool.h new file mode 100644 index 0000000000..6af117577e --- /dev/null +++ b/common_video/include/video_frame_buffer_pool.h @@ -0,0 +1,75 @@ +/* + * 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 COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_POOL_H_ +#define COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_POOL_H_ + +#include + +#include + +#include "api/scoped_refptr.h" +#include "api/video/i420_buffer.h" +#include "api/video/nv12_buffer.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/ref_counted_object.h" + +namespace webrtc { + +// Simple buffer pool to avoid unnecessary allocations of video frame buffers. +// The pool manages the memory of the I420Buffer/NV12Buffer returned from +// Create(I420|NV12)Buffer. When the buffer is destructed, the memory is +// returned to the pool for use by subsequent calls to Create(I420|NV12)Buffer. +// If the resolution passed to Create(I420|NV12)Buffer changes or requested +// pixel format changes, old buffers will be purged from the pool. +// Note that Create(I420|NV12)Buffer will crash if more than +// kMaxNumberOfFramesBeforeCrash are created. This is to prevent memory leaks +// where frames are not returned. +class VideoFrameBufferPool { + public: + VideoFrameBufferPool(); + explicit VideoFrameBufferPool(bool zero_initialize); + VideoFrameBufferPool(bool zero_initialize, size_t max_number_of_buffers); + ~VideoFrameBufferPool(); + + // Returns a buffer from the pool. If no suitable buffer exist in the pool + // and there are less than |max_number_of_buffers| pending, a buffer is + // created. Returns null otherwise. + rtc::scoped_refptr CreateI420Buffer(int width, int height); + rtc::scoped_refptr CreateNV12Buffer(int width, int height); + + // Changes the max amount of buffers in the pool to the new value. + // Returns true if change was successful and false if the amount of already + // allocated buffers is bigger than new value. + bool Resize(size_t max_number_of_buffers); + + // Clears buffers_ and detaches the thread checker so that it can be reused + // later from another thread. + void Release(); + + private: + rtc::scoped_refptr + GetExistingBuffer(int width, int height, VideoFrameBuffer::Type type); + + rtc::RaceChecker race_checker_; + std::list> buffers_; + // If true, newly allocated buffers are zero-initialized. Note that recycled + // buffers are not zero'd before reuse. This is required of buffers used by + // FFmpeg according to http://crbug.com/390941, which only requires it for the + // initial allocation (as shown by FFmpeg's own buffer allocation code). It + // has to do with "Use-of-uninitialized-value" on "Linux_msan_chrome". + const bool zero_initialize_; + // Max number of buffers this pool can have pending. + size_t max_number_of_buffers_; +}; + +} // namespace webrtc + +#endif // COMMON_VIDEO_INCLUDE_VIDEO_FRAME_BUFFER_POOL_H_ diff --git a/common_video/video_frame_buffer_pool.cc b/common_video/video_frame_buffer_pool.cc new file mode 100644 index 0000000000..6df240d9fe --- /dev/null +++ b/common_video/video_frame_buffer_pool.cc @@ -0,0 +1,178 @@ +/* + * 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 "common_video/include/video_frame_buffer_pool.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { +bool HasOneRef(const rtc::scoped_refptr& buffer) { + // Cast to rtc::RefCountedObject is safe because this function is only called + // on locally created VideoFrameBuffers, which are either + // |rtc::RefCountedObject| or |rtc::RefCountedObject|. + switch (buffer->type()) { + case VideoFrameBuffer::Type::kI420: { + return static_cast*>(buffer.get()) + ->HasOneRef(); + } + case VideoFrameBuffer::Type::kNV12: { + return static_cast*>(buffer.get()) + ->HasOneRef(); + } + default: + RTC_NOTREACHED(); + } + return false; +} + +} // namespace + +VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {} + +VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize) + : VideoFrameBufferPool(zero_initialize, + std::numeric_limits::max()) {} + +VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize, + size_t max_number_of_buffers) + : zero_initialize_(zero_initialize), + max_number_of_buffers_(max_number_of_buffers) {} + +VideoFrameBufferPool::~VideoFrameBufferPool() = default; + +void VideoFrameBufferPool::Release() { + buffers_.clear(); +} + +bool VideoFrameBufferPool::Resize(size_t max_number_of_buffers) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); + size_t used_buffers_count = 0; + for (const rtc::scoped_refptr& 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 the application. If the ref count is 1, + // then the list we are looping over holds the only reference and it's safe + // to reuse. + if (!HasOneRef(buffer)) { + used_buffers_count++; + } + } + if (used_buffers_count > max_number_of_buffers) { + return false; + } + max_number_of_buffers_ = max_number_of_buffers; + + size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_; + auto iter = buffers_.begin(); + while (iter != buffers_.end() && buffers_to_purge > 0) { + if (HasOneRef(*iter)) { + iter = buffers_.erase(iter); + buffers_to_purge--; + } else { + ++iter; + } + } + return true; +} + +rtc::scoped_refptr VideoFrameBufferPool::CreateI420Buffer( + int width, + int height) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); + + rtc::scoped_refptr existing_buffer = + GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI420); + if (existing_buffer) { + // Cast is safe because the only way kI420 buffer is created is + // in the same function below, where |RefCountedObject| is + // created. + rtc::RefCountedObject* raw_buffer = + static_cast*>(existing_buffer.get()); + // Creates a new scoped_refptr, which is also pointing to the same + // RefCountedObject as buffer, increasing ref count. + return rtc::scoped_refptr(raw_buffer); + } + + if (buffers_.size() >= max_number_of_buffers_) + return nullptr; + // Allocate new buffer. + rtc::scoped_refptr buffer = + new rtc::RefCountedObject(width, height); + + if (zero_initialize_) + buffer->InitializeData(); + + buffers_.push_back(buffer); + return buffer; +} + +rtc::scoped_refptr VideoFrameBufferPool::CreateNV12Buffer( + int width, + int height) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); + + rtc::scoped_refptr existing_buffer = + GetExistingBuffer(width, height, VideoFrameBuffer::Type::kNV12); + if (existing_buffer) { + // Cast is safe because the only way kI420 buffer is created is + // in the same function below, where |RefCountedObject| is + // created. + rtc::RefCountedObject* raw_buffer = + static_cast*>(existing_buffer.get()); + // Creates a new scoped_refptr, which is also pointing to the same + // RefCountedObject as buffer, increasing ref count. + return rtc::scoped_refptr(raw_buffer); + } + + if (buffers_.size() >= max_number_of_buffers_) + return nullptr; + // Allocate new buffer. + rtc::scoped_refptr buffer = + new rtc::RefCountedObject(width, height); + + if (zero_initialize_) + buffer->InitializeData(); + + buffers_.push_back(buffer); + return buffer; +} + +rtc::scoped_refptr VideoFrameBufferPool::GetExistingBuffer( + int width, + int height, + VideoFrameBuffer::Type type) { + // Release buffers with wrong resolution or different type. + for (auto it = buffers_.begin(); it != buffers_.end();) { + const auto& buffer = *it; + if (buffer->width() != width || buffer->height() != height || + buffer->type() != type) { + it = buffers_.erase(it); + } else { + ++it; + } + } + // Look for a free buffer. + for (const rtc::scoped_refptr& 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 the application. If the ref count is 1, + // then the list we are looping over holds the only reference and it's safe + // to reuse. + if (HasOneRef(buffer)) { + RTC_CHECK(buffer->type() == type); + return buffer; + } + } + return nullptr; +} + +} // namespace webrtc diff --git a/common_video/video_frame_buffer_pool_unittest.cc b/common_video/video_frame_buffer_pool_unittest.cc new file mode 100644 index 0000000000..eb9b73f1a2 --- /dev/null +++ b/common_video/video_frame_buffer_pool_unittest.cc @@ -0,0 +1,91 @@ +/* + * 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 "common_video/include/video_frame_buffer_pool.h" + +#include +#include + +#include "api/scoped_refptr.h" +#include "api/video/i420_buffer.h" +#include "api/video/video_frame_buffer.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(TestVideoFrameBufferPool, SimpleFrameReuse) { + VideoFrameBufferPool pool; + auto buffer = pool.CreateI420Buffer(16, 16); + EXPECT_EQ(16, buffer->width()); + EXPECT_EQ(16, buffer->height()); + // Extract non-refcounted pointers for testing. + const uint8_t* y_ptr = buffer->DataY(); + const uint8_t* u_ptr = buffer->DataU(); + const uint8_t* v_ptr = buffer->DataV(); + // Release buffer so that it is returned to the pool. + buffer = nullptr; + // Check that the memory is resued. + buffer = pool.CreateI420Buffer(16, 16); + EXPECT_EQ(y_ptr, buffer->DataY()); + EXPECT_EQ(u_ptr, buffer->DataU()); + EXPECT_EQ(v_ptr, buffer->DataV()); +} + +TEST(TestVideoFrameBufferPool, FailToReuseWrongSize) { + // Set max frames to 1, just to make sure the first buffer is being released. + VideoFrameBufferPool pool(/*zero_initialize=*/false, 1); + auto buffer = pool.CreateI420Buffer(16, 16); + EXPECT_EQ(16, buffer->width()); + EXPECT_EQ(16, buffer->height()); + // 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.CreateI420Buffer(32, 16); + ASSERT_TRUE(buffer); + EXPECT_EQ(32, buffer->width()); + EXPECT_EQ(16, buffer->height()); +} + +TEST(TestVideoFrameBufferPool, FrameValidAfterPoolDestruction) { + rtc::scoped_refptr buffer; + { + VideoFrameBufferPool pool; + buffer = pool.CreateI420Buffer(16, 16); + } + EXPECT_EQ(16, buffer->width()); + EXPECT_EQ(16, buffer->height()); + // Access buffer, so that ASAN could find any issues if buffer + // doesn't outlive the buffer pool. + memset(buffer->MutableDataY(), 0xA5, 16 * buffer->StrideY()); +} + +TEST(TestVideoFrameBufferPool, MaxNumberOfBuffers) { + VideoFrameBufferPool pool(false, 1); + auto buffer = pool.CreateI420Buffer(16, 16); + EXPECT_NE(nullptr, buffer.get()); + EXPECT_EQ(nullptr, pool.CreateI420Buffer(16, 16).get()); +} + +TEST(TestVideoFrameBufferPool, ProducesNv12) { + VideoFrameBufferPool pool(false, 1); + auto buffer = pool.CreateNV12Buffer(16, 16); + EXPECT_NE(nullptr, buffer.get()); +} + +TEST(TestVideoFrameBufferPool, SwitchingPixelFormat) { + VideoFrameBufferPool pool(false, 1); + auto buffer = pool.CreateNV12Buffer(16, 16); + EXPECT_EQ(nullptr, pool.CreateNV12Buffer(16, 16).get()); + auto buffer2 = pool.CreateI420Buffer(16, 16); + EXPECT_NE(nullptr, buffer2.get()); + EXPECT_EQ(nullptr, pool.CreateI420Buffer(16, 16).get()); +} + +} // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_decoder.cc b/modules/video_coding/codecs/av1/libaom_av1_decoder.cc index 1a8a0c4775..bedb51937a 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_decoder.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_decoder.cc @@ -19,7 +19,7 @@ #include "api/video/i420_buffer.h" #include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_decoder.h" -#include "common_video/include/i420_buffer_pool.h" +#include "common_video/include/video_frame_buffer_pool.h" #include "modules/video_coding/include/video_error_codes.h" #include "rtc_base/logging.h" #include "third_party/libaom/source/libaom/aom/aom_decoder.h" @@ -59,7 +59,7 @@ class LibaomAv1Decoder final : public VideoDecoder { aom_codec_ctx_t context_; bool inited_; // Pool of memory buffers to store decoded image data for application access. - I420BufferPool buffer_pool_; + VideoFrameBufferPool buffer_pool_; DecodedImageCallback* decode_complete_callback_; }; @@ -138,7 +138,7 @@ int32_t LibaomAv1Decoder::Decode(const EncodedImage& encoded_image, // Allocate memory for decoded frame. rtc::scoped_refptr buffer = - buffer_pool_.CreateBuffer(decoded_image->d_w, decoded_image->d_h); + buffer_pool_.CreateI420Buffer(decoded_image->d_w, decoded_image->d_h); if (!buffer.get()) { // Pool has too many pending frames. RTC_LOG(LS_WARNING) << "LibaomAv1Decoder::Decode returned due to lack of" diff --git a/modules/video_coding/codecs/h264/h264_decoder_impl.cc b/modules/video_coding/codecs/h264/h264_decoder_impl.cc index 9e32c68f76..8c09adbccd 100644 --- a/modules/video_coding/codecs/h264/h264_decoder_impl.cc +++ b/modules/video_coding/codecs/h264/h264_decoder_impl.cc @@ -103,7 +103,7 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, // TODO(nisse): Delete that feature from the video pool, instead add // an explicit call to InitializeData here. rtc::scoped_refptr frame_buffer = - decoder->pool_.CreateBuffer(width, height); + decoder->pool_.CreateI420Buffer(width, height); int y_size = width * height; int uv_size = frame_buffer->ChromaWidth() * frame_buffer->ChromaHeight(); diff --git a/modules/video_coding/codecs/h264/h264_decoder_impl.h b/modules/video_coding/codecs/h264/h264_decoder_impl.h index 3c038e6425..f8cef2502d 100644 --- a/modules/video_coding/codecs/h264/h264_decoder_impl.h +++ b/modules/video_coding/codecs/h264/h264_decoder_impl.h @@ -44,7 +44,7 @@ extern "C" { } // extern "C" #include "common_video/h264/h264_bitstream_parser.h" -#include "common_video/include/i420_buffer_pool.h" +#include "common_video/include/video_frame_buffer_pool.h" namespace webrtc { @@ -92,7 +92,7 @@ class H264DecoderImpl : public H264Decoder { void ReportInit(); void ReportError(); - I420BufferPool pool_; + VideoFrameBufferPool pool_; std::unique_ptr av_context_; std::unique_ptr av_frame_; diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc b/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc index d86d8767c5..ddee637751 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc @@ -329,7 +329,8 @@ int LibvpxVp8Decoder::ReturnFrame( last_frame_height_ = img->d_h; // Allocate memory for decoded image. rtc::scoped_refptr buffer = - buffer_pool_.CreateBuffer(img->d_w, img->d_h); + buffer_pool_.CreateI420Buffer(img->d_w, img->d_h); + if (!buffer.get()) { // Pool has too many pending frames. RTC_HISTOGRAM_BOOLEAN("WebRTC.Video.LibvpxVp8Decoder.TooManyPendingFrames", diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.h b/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.h index 2a0c5f2c5b..a59828be1b 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.h +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.h @@ -16,7 +16,7 @@ #include "absl/types/optional.h" #include "api/video/encoded_image.h" #include "api/video_codecs/video_decoder.h" -#include "common_video/include/i420_buffer_pool.h" +#include "common_video/include/video_frame_buffer_pool.h" #include "modules/video_coding/codecs/vp8/include/vp8.h" #include "modules/video_coding/include/video_codec_interface.h" #include "vpx/vp8dx.h" @@ -54,7 +54,7 @@ class LibvpxVp8Decoder : public VideoDecoder { const webrtc::ColorSpace* explicit_color_space); const bool use_postproc_; - I420BufferPool buffer_pool_; + VideoFrameBufferPool buffer_pool_; DecodedImageCallback* decode_complete_callback_; bool inited_; vpx_codec_ctx_t* decoder_; diff --git a/modules/video_processing/video_denoiser.cc b/modules/video_processing/video_denoiser.cc index 40568a5ec6..3a18125146 100644 --- a/modules/video_processing/video_denoiser.cc +++ b/modules/video_processing/video_denoiser.cc @@ -235,7 +235,7 @@ rtc::scoped_refptr VideoDenoiser::DenoiseFrame( const uint8_t* y_src = frame->DataY(); int stride_y_src = frame->StrideY(); rtc::scoped_refptr dst = - buffer_pool_.CreateBuffer(width_, height_); + buffer_pool_.CreateI420Buffer(width_, height_); uint8_t* y_dst = dst->MutableDataY(); int stride_y_dst = dst->StrideY(); diff --git a/modules/video_processing/video_denoiser.h b/modules/video_processing/video_denoiser.h index 37d624bb25..eb98c5bc53 100644 --- a/modules/video_processing/video_denoiser.h +++ b/modules/video_processing/video_denoiser.h @@ -15,7 +15,7 @@ #include "api/scoped_refptr.h" #include "api/video/video_frame_buffer.h" -#include "common_video/include/i420_buffer_pool.h" +#include "common_video/include/video_frame_buffer_pool.h" #include "modules/video_processing/util/denoiser_filter.h" #include "modules/video_processing/util/noise_estimation.h" #include "modules/video_processing/util/skin_detection.h" @@ -77,7 +77,7 @@ class VideoDenoiser { std::unique_ptr y_density_; // Save the return values by MbDenoise for each block. std::unique_ptr mb_filter_decision_; - I420BufferPool buffer_pool_; + VideoFrameBufferPool buffer_pool_; rtc::scoped_refptr prev_buffer_; }; diff --git a/sdk/android/src/jni/android_video_track_source.h b/sdk/android/src/jni/android_video_track_source.h index 378d380a11..d5ec45cd1e 100644 --- a/sdk/android/src/jni/android_video_track_source.h +++ b/sdk/android/src/jni/android_video_track_source.h @@ -13,7 +13,6 @@ #include -#include "common_video/include/i420_buffer_pool.h" #include "common_video/libyuv/include/webrtc_libyuv.h" #include "media/base/adapted_video_track_source.h" #include "rtc_base/async_invoker.h"