| /* | |
| * 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 <cstddef> | |
| #include <limits> | |
| #include "api/make_ref_counted.h" | |
| #include "api/scoped_refptr.h" | |
| #include "api/video/i010_buffer.h" | |
| #include "api/video/i210_buffer.h" | |
| #include "api/video/i410_buffer.h" | |
| #include "api/video/i420_buffer.h" | |
| #include "api/video/i422_buffer.h" | |
| #include "api/video/i444_buffer.h" | |
| #include "api/video/nv12_buffer.h" | |
| #include "api/video/video_frame_buffer.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/race_checker.h" | |
| #include "rtc_base/ref_counted_object.h" | |
| namespace webrtc { | |
| namespace { | |
| bool HasOneRef(const scoped_refptr<VideoFrameBuffer>& buffer) { | |
| // Cast to RefCountedObject is safe because this function is only called | |
| // on locally created VideoFrameBuffers, which are either | |
| // `RefCountedObject<I420Buffer>`, `RefCountedObject<I444Buffer>` or | |
| // `RefCountedObject<NV12Buffer>`. | |
| switch (buffer->type()) { | |
| case VideoFrameBuffer::Type::kI420: { | |
| return static_cast<RefCountedObject<I420Buffer>*>(buffer.get()) | |
| ->HasOneRef(); | |
| } | |
| case VideoFrameBuffer::Type::kI444: { | |
| return static_cast<RefCountedObject<I444Buffer>*>(buffer.get()) | |
| ->HasOneRef(); | |
| } | |
| case VideoFrameBuffer::Type::kI422: { | |
| return static_cast<RefCountedObject<I422Buffer>*>(buffer.get()) | |
| ->HasOneRef(); | |
| } | |
| case VideoFrameBuffer::Type::kI010: { | |
| return static_cast<RefCountedObject<I010Buffer>*>(buffer.get()) | |
| ->HasOneRef(); | |
| } | |
| case VideoFrameBuffer::Type::kI210: { | |
| return static_cast<RefCountedObject<I210Buffer>*>(buffer.get()) | |
| ->HasOneRef(); | |
| } | |
| case VideoFrameBuffer::Type::kI410: { | |
| return static_cast<RefCountedObject<I410Buffer>*>(buffer.get()) | |
| ->HasOneRef(); | |
| } | |
| case VideoFrameBuffer::Type::kNV12: { | |
| return static_cast<RefCountedObject<NV12Buffer>*>(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<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 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; | |
| } | |
| scoped_refptr<I420Buffer> VideoFrameBufferPool::CreateI420Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| 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. | |
| RefCountedObject<I420Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<I420Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<I420Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<I420Buffer> buffer = | |
| make_ref_counted<I420Buffer>(width, height); | |
| if (zero_initialize_) | |
| buffer->InitializeData(); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| scoped_refptr<I444Buffer> VideoFrameBufferPool::CreateI444Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| scoped_refptr<VideoFrameBuffer> 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<I444Buffer>| | |
| // is created. | |
| RefCountedObject<I444Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<I444Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<I444Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<I444Buffer> buffer = | |
| make_ref_counted<I444Buffer>(width, height); | |
| if (zero_initialize_) | |
| buffer->InitializeData(); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| scoped_refptr<I422Buffer> VideoFrameBufferPool::CreateI422Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| scoped_refptr<VideoFrameBuffer> 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<I422Buffer>| | |
| // is created. | |
| RefCountedObject<I422Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<I422Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<I422Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<I422Buffer> buffer = | |
| make_ref_counted<I422Buffer>(width, height); | |
| if (zero_initialize_) | |
| buffer->InitializeData(); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| 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. | |
| RefCountedObject<NV12Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<NV12Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<NV12Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<NV12Buffer> buffer = | |
| make_ref_counted<NV12Buffer>(width, height); | |
| if (zero_initialize_) | |
| buffer->InitializeData(); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| scoped_refptr<I010Buffer> VideoFrameBufferPool::CreateI010Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| scoped_refptr<VideoFrameBuffer> 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<I010Buffer>| | |
| // is created. | |
| RefCountedObject<I010Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<I010Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<I010Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<I010Buffer> buffer = I010Buffer::Create(width, height); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| scoped_refptr<I210Buffer> VideoFrameBufferPool::CreateI210Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| scoped_refptr<VideoFrameBuffer> 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<I210Buffer>| | |
| // is created. | |
| RefCountedObject<I210Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<I210Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<I210Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<I210Buffer> buffer = I210Buffer::Create(width, height); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| scoped_refptr<I410Buffer> VideoFrameBufferPool::CreateI410Buffer(int width, | |
| int height) { | |
| RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); | |
| scoped_refptr<VideoFrameBuffer> 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<I410Buffer>| | |
| // is created. | |
| RefCountedObject<I410Buffer>* raw_buffer = | |
| static_cast<RefCountedObject<I410Buffer>*>(existing_buffer.get()); | |
| // Creates a new scoped_refptr, which is also pointing to the same | |
| // RefCountedObject as buffer, increasing ref count. | |
| return scoped_refptr<I410Buffer>(raw_buffer); | |
| } | |
| if (buffers_.size() >= max_number_of_buffers_) | |
| return nullptr; | |
| // Allocate new buffer. | |
| scoped_refptr<I410Buffer> buffer = I410Buffer::Create(width, height); | |
| buffers_.push_back(buffer); | |
| return buffer; | |
| } | |
| 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 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 |