Extend I420 frame buffer pool to also create NV12 buffers
Bug: webrtc:11956 Change-Id: I758a28f2755cfa72ad486fbe1f9209f356eb5fa1 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/184510 Reviewed-by: Stefan Holmer <stefan@webrtc.org> Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32147}
This commit is contained in:

committed by
Commit Bot

parent
c5a74ffba4
commit
4c87d83d03
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
]
|
||||
|
||||
|
@ -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 <limits>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
I420BufferPool::I420BufferPool() : I420BufferPool(false) {}
|
||||
I420BufferPool::I420BufferPool(bool zero_initialize)
|
||||
: I420BufferPool(zero_initialize, std::numeric_limits<size_t>::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<PooledI420Buffer>& 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<I420Buffer> 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<I420Buffer> 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<PooledI420Buffer>& 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<PooledI420Buffer> buffer =
|
||||
new PooledI420Buffer(width, height, stride_y, stride_u, stride_v);
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<I420Buffer> 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
|
@ -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<I420Buffer> CreateBuffer(int width, int height);
|
||||
|
||||
// Returns a buffer from the pool with the explicitly specified stride.
|
||||
rtc::scoped_refptr<I420Buffer> 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<I420Buffer>;
|
||||
|
||||
rtc::RaceChecker race_checker_;
|
||||
std::list<rtc::scoped_refptr<PooledI420Buffer>> 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<I420Buffer> CreateBuffer(int width, int height) {
|
||||
return VideoFrameBufferPool::CreateI420Buffer(width, height);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
75
common_video/include/video_frame_buffer_pool.h
Normal file
75
common_video/include/video_frame_buffer_pool.h
Normal file
@ -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 <stddef.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#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<I420Buffer> CreateI420Buffer(int width, int height);
|
||||
rtc::scoped_refptr<NV12Buffer> 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<VideoFrameBuffer>
|
||||
GetExistingBuffer(int width, int height, VideoFrameBuffer::Type type);
|
||||
|
||||
rtc::RaceChecker race_checker_;
|
||||
std::list<rtc::scoped_refptr<VideoFrameBuffer>> 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_
|
178
common_video/video_frame_buffer_pool.cc
Normal file
178
common_video/video_frame_buffer_pool.cc
Normal file
@ -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 <limits>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
bool HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer>& buffer) {
|
||||
// Cast to rtc::RefCountedObject is safe because this function is only called
|
||||
// on locally created VideoFrameBuffers, which are either
|
||||
// |rtc::RefCountedObject<I420Buffer>| or |rtc::RefCountedObject<NV12Buffer>|.
|
||||
switch (buffer->type()) {
|
||||
case VideoFrameBuffer::Type::kI420: {
|
||||
return static_cast<rtc::RefCountedObject<I420Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
case VideoFrameBuffer::Type::kNV12: {
|
||||
return static_cast<rtc::RefCountedObject<NV12Buffer>*>(buffer.get())
|
||||
->HasOneRef();
|
||||
}
|
||||
default:
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {}
|
||||
|
||||
VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize)
|
||||
: VideoFrameBufferPool(zero_initialize,
|
||||
std::numeric_limits<size_t>::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<VideoFrameBuffer>& 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<I420Buffer> VideoFrameBufferPool::CreateI420Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> 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<I420Buffer>| is
|
||||
// created.
|
||||
rtc::RefCountedObject<I420Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<I420Buffer>*>(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<I420Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<I420Buffer> buffer =
|
||||
new rtc::RefCountedObject<I420Buffer>(width, height);
|
||||
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer(
|
||||
int width,
|
||||
int height) {
|
||||
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> 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<I420Buffer>| is
|
||||
// created.
|
||||
rtc::RefCountedObject<NV12Buffer>* raw_buffer =
|
||||
static_cast<rtc::RefCountedObject<NV12Buffer>*>(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<NV12Buffer>(raw_buffer);
|
||||
}
|
||||
|
||||
if (buffers_.size() >= max_number_of_buffers_)
|
||||
return nullptr;
|
||||
// Allocate new buffer.
|
||||
rtc::scoped_refptr<NV12Buffer> buffer =
|
||||
new rtc::RefCountedObject<NV12Buffer>(width, height);
|
||||
|
||||
if (zero_initialize_)
|
||||
buffer->InitializeData();
|
||||
|
||||
buffers_.push_back(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> 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<VideoFrameBuffer>& 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
|
91
common_video/video_frame_buffer_pool_unittest.cc
Normal file
91
common_video/video_frame_buffer_pool_unittest.cc
Normal file
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<I420Buffer> 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
|
@ -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<I420Buffer> 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"
|
||||
|
@ -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<I420Buffer> 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();
|
||||
|
@ -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<AVCodecContext, AVCodecContextDeleter> av_context_;
|
||||
std::unique_ptr<AVFrame, AVFrameDeleter> av_frame_;
|
||||
|
||||
|
@ -329,7 +329,8 @@ int LibvpxVp8Decoder::ReturnFrame(
|
||||
last_frame_height_ = img->d_h;
|
||||
// Allocate memory for decoded image.
|
||||
rtc::scoped_refptr<I420Buffer> 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",
|
||||
|
@ -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_;
|
||||
|
@ -235,7 +235,7 @@ rtc::scoped_refptr<I420BufferInterface> VideoDenoiser::DenoiseFrame(
|
||||
const uint8_t* y_src = frame->DataY();
|
||||
int stride_y_src = frame->StrideY();
|
||||
rtc::scoped_refptr<I420Buffer> dst =
|
||||
buffer_pool_.CreateBuffer(width_, height_);
|
||||
buffer_pool_.CreateI420Buffer(width_, height_);
|
||||
|
||||
uint8_t* y_dst = dst->MutableDataY();
|
||||
int stride_y_dst = dst->StrideY();
|
||||
|
@ -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<uint8_t[]> y_density_;
|
||||
// Save the return values by MbDenoise for each block.
|
||||
std::unique_ptr<DenoiserDecision[]> mb_filter_decision_;
|
||||
I420BufferPool buffer_pool_;
|
||||
VideoFrameBufferPool buffer_pool_;
|
||||
rtc::scoped_refptr<I420BufferInterface> prev_buffer_;
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#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"
|
||||
|
Reference in New Issue
Block a user