/* * 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 "api/make_ref_counted.h" #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`, `rtc::RefCountedObject` or // `rtc::RefCountedObject`. switch (buffer->type()) { case VideoFrameBuffer::Type::kI420: { return static_cast*>(buffer.get()) ->HasOneRef(); } case VideoFrameBuffer::Type::kI444: { return static_cast*>(buffer.get()) ->HasOneRef(); } case VideoFrameBuffer::Type::kI422: { return static_cast*>(buffer.get()) ->HasOneRef(); } case VideoFrameBuffer::Type::kI010: { return static_cast*>(buffer.get()) ->HasOneRef(); } case VideoFrameBuffer::Type::kI210: { return static_cast*>(buffer.get()) ->HasOneRef(); } case VideoFrameBuffer::Type::kI410: { return static_cast*>(buffer.get()) ->HasOneRef(); } case VideoFrameBuffer::Type::kNV12: { return static_cast*>(buffer.get()) ->HasOneRef(); } default: RTC_DCHECK_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 = rtc::make_ref_counted(width, height); if (zero_initialize_) buffer->InitializeData(); buffers_.push_back(buffer); return buffer; } rtc::scoped_refptr VideoFrameBufferPool::CreateI444Buffer( int width, int height) { RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); rtc::scoped_refptr existing_buffer = GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI444); if (existing_buffer) { // Cast is safe because the only way kI444 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 = rtc::make_ref_counted(width, height); if (zero_initialize_) buffer->InitializeData(); buffers_.push_back(buffer); return buffer; } rtc::scoped_refptr VideoFrameBufferPool::CreateI422Buffer( int width, int height) { RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); rtc::scoped_refptr existing_buffer = GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI422); if (existing_buffer) { // Cast is safe because the only way kI422 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 = rtc::make_ref_counted(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 = rtc::make_ref_counted(width, height); if (zero_initialize_) buffer->InitializeData(); buffers_.push_back(buffer); return buffer; } rtc::scoped_refptr VideoFrameBufferPool::CreateI010Buffer( int width, int height) { RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); rtc::scoped_refptr existing_buffer = GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI010); if (existing_buffer) { // Cast is safe because the only way kI010 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 = I010Buffer::Create(width, height); buffers_.push_back(buffer); return buffer; } rtc::scoped_refptr VideoFrameBufferPool::CreateI210Buffer( int width, int height) { RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); rtc::scoped_refptr existing_buffer = GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI210); if (existing_buffer) { // Cast is safe because the only way kI210 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 = I210Buffer::Create(width, height); buffers_.push_back(buffer); return buffer; } rtc::scoped_refptr VideoFrameBufferPool::CreateI410Buffer( int width, int height) { RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); rtc::scoped_refptr existing_buffer = GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI410); if (existing_buffer) { // Cast is safe because the only way kI410 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 = I410Buffer::Create(width, height); 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