Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "common_video/include/video_frame_buffer_pool.h" |
| 12 | |
| 13 | #include <limits> |
| 14 | |
Niels Möller | 105711e | 2022-06-14 13:48:26 | [diff] [blame] | 15 | #include "api/make_ref_counted.h" |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 16 | #include "rtc_base/checks.h" |
| 17 | |
| 18 | namespace webrtc { |
| 19 | |
| 20 | namespace { |
| 21 | bool HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer>& buffer) { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 22 | // Cast to RefCountedObject is safe because this function is only called |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 23 | // on locally created VideoFrameBuffers, which are either |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 24 | // `RefCountedObject<I420Buffer>`, `RefCountedObject<I444Buffer>` or |
| 25 | // `RefCountedObject<NV12Buffer>`. |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 26 | switch (buffer->type()) { |
| 27 | case VideoFrameBuffer::Type::kI420: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 28 | return static_cast<RefCountedObject<I420Buffer>*>(buffer.get()) |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 29 | ->HasOneRef(); |
| 30 | } |
Stefan Mitic | ffdc680 | 2022-02-08 15:00:16 | [diff] [blame] | 31 | case VideoFrameBuffer::Type::kI444: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 32 | return static_cast<RefCountedObject<I444Buffer>*>(buffer.get()) |
Stefan Mitic | ffdc680 | 2022-02-08 15:00:16 | [diff] [blame] | 33 | ->HasOneRef(); |
| 34 | } |
Sergio Garcia Murillo | b63536f | 2022-03-25 08:04:09 | [diff] [blame] | 35 | case VideoFrameBuffer::Type::kI422: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 36 | return static_cast<RefCountedObject<I422Buffer>*>(buffer.get()) |
Sergio Garcia Murillo | b63536f | 2022-03-25 08:04:09 | [diff] [blame] | 37 | ->HasOneRef(); |
| 38 | } |
Sergio Garcia Murillo | 8545eba | 2022-06-17 09:48:14 | [diff] [blame] | 39 | case VideoFrameBuffer::Type::kI010: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 40 | return static_cast<RefCountedObject<I010Buffer>*>(buffer.get()) |
Sergio Garcia Murillo | 8545eba | 2022-06-17 09:48:14 | [diff] [blame] | 41 | ->HasOneRef(); |
| 42 | } |
| 43 | case VideoFrameBuffer::Type::kI210: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 44 | return static_cast<RefCountedObject<I210Buffer>*>(buffer.get()) |
Sergio Garcia Murillo | 8545eba | 2022-06-17 09:48:14 | [diff] [blame] | 45 | ->HasOneRef(); |
| 46 | } |
Sergio Garcia Murillo | 1389c4b | 2023-01-09 17:29:34 | [diff] [blame] | 47 | case VideoFrameBuffer::Type::kI410: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 48 | return static_cast<RefCountedObject<I410Buffer>*>(buffer.get()) |
Sergio Garcia Murillo | 1389c4b | 2023-01-09 17:29:34 | [diff] [blame] | 49 | ->HasOneRef(); |
| 50 | } |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 51 | case VideoFrameBuffer::Type::kNV12: { |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 52 | return static_cast<RefCountedObject<NV12Buffer>*>(buffer.get()) |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 53 | ->HasOneRef(); |
| 54 | } |
| 55 | default: |
Artem Titov | d325196 | 2021-11-15 15:57:07 | [diff] [blame] | 56 | RTC_DCHECK_NOTREACHED(); |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 57 | } |
| 58 | return false; |
| 59 | } |
| 60 | |
| 61 | } // namespace |
| 62 | |
| 63 | VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {} |
| 64 | |
| 65 | VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize) |
| 66 | : VideoFrameBufferPool(zero_initialize, |
| 67 | std::numeric_limits<size_t>::max()) {} |
| 68 | |
| 69 | VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize, |
| 70 | size_t max_number_of_buffers) |
| 71 | : zero_initialize_(zero_initialize), |
| 72 | max_number_of_buffers_(max_number_of_buffers) {} |
| 73 | |
| 74 | VideoFrameBufferPool::~VideoFrameBufferPool() = default; |
| 75 | |
| 76 | void VideoFrameBufferPool::Release() { |
| 77 | buffers_.clear(); |
| 78 | } |
| 79 | |
| 80 | bool VideoFrameBufferPool::Resize(size_t max_number_of_buffers) { |
| 81 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 82 | size_t used_buffers_count = 0; |
| 83 | for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) { |
| 84 | // If the buffer is in use, the ref count will be >= 2, one from the list we |
| 85 | // are looping over and one from the application. If the ref count is 1, |
| 86 | // then the list we are looping over holds the only reference and it's safe |
| 87 | // to reuse. |
| 88 | if (!HasOneRef(buffer)) { |
| 89 | used_buffers_count++; |
| 90 | } |
| 91 | } |
| 92 | if (used_buffers_count > max_number_of_buffers) { |
| 93 | return false; |
| 94 | } |
| 95 | max_number_of_buffers_ = max_number_of_buffers; |
| 96 | |
| 97 | size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_; |
| 98 | auto iter = buffers_.begin(); |
| 99 | while (iter != buffers_.end() && buffers_to_purge > 0) { |
| 100 | if (HasOneRef(*iter)) { |
| 101 | iter = buffers_.erase(iter); |
| 102 | buffers_to_purge--; |
| 103 | } else { |
| 104 | ++iter; |
| 105 | } |
| 106 | } |
| 107 | return true; |
| 108 | } |
| 109 | |
| 110 | rtc::scoped_refptr<I420Buffer> VideoFrameBufferPool::CreateI420Buffer( |
| 111 | int width, |
| 112 | int height) { |
| 113 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 114 | |
| 115 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 116 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI420); |
| 117 | if (existing_buffer) { |
| 118 | // Cast is safe because the only way kI420 buffer is created is |
Artem Titov | cfea218 | 2021-08-09 23:22:31 | [diff] [blame] | 119 | // in the same function below, where `RefCountedObject<I420Buffer>` is |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 120 | // created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 121 | RefCountedObject<I420Buffer>* raw_buffer = |
| 122 | static_cast<RefCountedObject<I420Buffer>*>(existing_buffer.get()); |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 123 | // Creates a new scoped_refptr, which is also pointing to the same |
| 124 | // RefCountedObject as buffer, increasing ref count. |
| 125 | return rtc::scoped_refptr<I420Buffer>(raw_buffer); |
| 126 | } |
| 127 | |
| 128 | if (buffers_.size() >= max_number_of_buffers_) |
| 129 | return nullptr; |
| 130 | // Allocate new buffer. |
| 131 | rtc::scoped_refptr<I420Buffer> buffer = |
Tomas Gunnarsson | c1d5891 | 2021-04-22 17:21:43 | [diff] [blame] | 132 | rtc::make_ref_counted<I420Buffer>(width, height); |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 133 | |
| 134 | if (zero_initialize_) |
| 135 | buffer->InitializeData(); |
| 136 | |
| 137 | buffers_.push_back(buffer); |
| 138 | return buffer; |
| 139 | } |
| 140 | |
Stefan Mitic | ffdc680 | 2022-02-08 15:00:16 | [diff] [blame] | 141 | rtc::scoped_refptr<I444Buffer> VideoFrameBufferPool::CreateI444Buffer( |
| 142 | int width, |
| 143 | int height) { |
| 144 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 145 | |
| 146 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 147 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI444); |
| 148 | if (existing_buffer) { |
| 149 | // Cast is safe because the only way kI444 buffer is created is |
| 150 | // in the same function below, where |RefCountedObject<I444Buffer>| |
| 151 | // is created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 152 | RefCountedObject<I444Buffer>* raw_buffer = |
| 153 | static_cast<RefCountedObject<I444Buffer>*>(existing_buffer.get()); |
Stefan Mitic | ffdc680 | 2022-02-08 15:00:16 | [diff] [blame] | 154 | // Creates a new scoped_refptr, which is also pointing to the same |
| 155 | // RefCountedObject as buffer, increasing ref count. |
| 156 | return rtc::scoped_refptr<I444Buffer>(raw_buffer); |
| 157 | } |
| 158 | |
| 159 | if (buffers_.size() >= max_number_of_buffers_) |
| 160 | return nullptr; |
| 161 | // Allocate new buffer. |
| 162 | rtc::scoped_refptr<I444Buffer> buffer = |
| 163 | rtc::make_ref_counted<I444Buffer>(width, height); |
| 164 | |
| 165 | if (zero_initialize_) |
| 166 | buffer->InitializeData(); |
| 167 | |
| 168 | buffers_.push_back(buffer); |
| 169 | return buffer; |
| 170 | } |
| 171 | |
Sergio Garcia Murillo | b63536f | 2022-03-25 08:04:09 | [diff] [blame] | 172 | rtc::scoped_refptr<I422Buffer> VideoFrameBufferPool::CreateI422Buffer( |
| 173 | int width, |
| 174 | int height) { |
| 175 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 176 | |
| 177 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 178 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI422); |
| 179 | if (existing_buffer) { |
| 180 | // Cast is safe because the only way kI422 buffer is created is |
| 181 | // in the same function below, where |RefCountedObject<I422Buffer>| |
| 182 | // is created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 183 | RefCountedObject<I422Buffer>* raw_buffer = |
| 184 | static_cast<RefCountedObject<I422Buffer>*>(existing_buffer.get()); |
Sergio Garcia Murillo | b63536f | 2022-03-25 08:04:09 | [diff] [blame] | 185 | // Creates a new scoped_refptr, which is also pointing to the same |
| 186 | // RefCountedObject as buffer, increasing ref count. |
| 187 | return rtc::scoped_refptr<I422Buffer>(raw_buffer); |
| 188 | } |
| 189 | |
| 190 | if (buffers_.size() >= max_number_of_buffers_) |
| 191 | return nullptr; |
| 192 | // Allocate new buffer. |
| 193 | rtc::scoped_refptr<I422Buffer> buffer = |
| 194 | rtc::make_ref_counted<I422Buffer>(width, height); |
| 195 | |
| 196 | if (zero_initialize_) |
| 197 | buffer->InitializeData(); |
| 198 | |
| 199 | buffers_.push_back(buffer); |
| 200 | return buffer; |
| 201 | } |
| 202 | |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 203 | rtc::scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer( |
| 204 | int width, |
| 205 | int height) { |
| 206 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 207 | |
| 208 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 209 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kNV12); |
| 210 | if (existing_buffer) { |
| 211 | // Cast is safe because the only way kI420 buffer is created is |
Artem Titov | cfea218 | 2021-08-09 23:22:31 | [diff] [blame] | 212 | // in the same function below, where `RefCountedObject<I420Buffer>` is |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 213 | // created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 214 | RefCountedObject<NV12Buffer>* raw_buffer = |
| 215 | static_cast<RefCountedObject<NV12Buffer>*>(existing_buffer.get()); |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 216 | // Creates a new scoped_refptr, which is also pointing to the same |
| 217 | // RefCountedObject as buffer, increasing ref count. |
| 218 | return rtc::scoped_refptr<NV12Buffer>(raw_buffer); |
| 219 | } |
| 220 | |
| 221 | if (buffers_.size() >= max_number_of_buffers_) |
| 222 | return nullptr; |
| 223 | // Allocate new buffer. |
| 224 | rtc::scoped_refptr<NV12Buffer> buffer = |
Tomas Gunnarsson | c1d5891 | 2021-04-22 17:21:43 | [diff] [blame] | 225 | rtc::make_ref_counted<NV12Buffer>(width, height); |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 226 | |
| 227 | if (zero_initialize_) |
| 228 | buffer->InitializeData(); |
| 229 | |
| 230 | buffers_.push_back(buffer); |
| 231 | return buffer; |
| 232 | } |
| 233 | |
Sergio Garcia Murillo | 8545eba | 2022-06-17 09:48:14 | [diff] [blame] | 234 | rtc::scoped_refptr<I010Buffer> VideoFrameBufferPool::CreateI010Buffer( |
| 235 | int width, |
| 236 | int height) { |
| 237 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 238 | |
| 239 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 240 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI010); |
| 241 | if (existing_buffer) { |
| 242 | // Cast is safe because the only way kI010 buffer is created is |
| 243 | // in the same function below, where |RefCountedObject<I010Buffer>| |
| 244 | // is created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 245 | RefCountedObject<I010Buffer>* raw_buffer = |
| 246 | static_cast<RefCountedObject<I010Buffer>*>(existing_buffer.get()); |
Sergio Garcia Murillo | 8545eba | 2022-06-17 09:48:14 | [diff] [blame] | 247 | // Creates a new scoped_refptr, which is also pointing to the same |
| 248 | // RefCountedObject as buffer, increasing ref count. |
| 249 | return rtc::scoped_refptr<I010Buffer>(raw_buffer); |
| 250 | } |
| 251 | |
| 252 | if (buffers_.size() >= max_number_of_buffers_) |
| 253 | return nullptr; |
| 254 | // Allocate new buffer. |
| 255 | rtc::scoped_refptr<I010Buffer> buffer = I010Buffer::Create(width, height); |
| 256 | |
| 257 | buffers_.push_back(buffer); |
| 258 | return buffer; |
| 259 | } |
| 260 | |
| 261 | rtc::scoped_refptr<I210Buffer> VideoFrameBufferPool::CreateI210Buffer( |
| 262 | int width, |
| 263 | int height) { |
| 264 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 265 | |
| 266 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 267 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI210); |
| 268 | if (existing_buffer) { |
| 269 | // Cast is safe because the only way kI210 buffer is created is |
| 270 | // in the same function below, where |RefCountedObject<I210Buffer>| |
| 271 | // is created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 272 | RefCountedObject<I210Buffer>* raw_buffer = |
| 273 | static_cast<RefCountedObject<I210Buffer>*>(existing_buffer.get()); |
Sergio Garcia Murillo | 8545eba | 2022-06-17 09:48:14 | [diff] [blame] | 274 | // Creates a new scoped_refptr, which is also pointing to the same |
| 275 | // RefCountedObject as buffer, increasing ref count. |
| 276 | return rtc::scoped_refptr<I210Buffer>(raw_buffer); |
| 277 | } |
| 278 | |
| 279 | if (buffers_.size() >= max_number_of_buffers_) |
| 280 | return nullptr; |
| 281 | // Allocate new buffer. |
| 282 | rtc::scoped_refptr<I210Buffer> buffer = I210Buffer::Create(width, height); |
| 283 | |
| 284 | buffers_.push_back(buffer); |
| 285 | return buffer; |
| 286 | } |
| 287 | |
Sergio Garcia Murillo | 1389c4b | 2023-01-09 17:29:34 | [diff] [blame] | 288 | rtc::scoped_refptr<I410Buffer> VideoFrameBufferPool::CreateI410Buffer( |
| 289 | int width, |
| 290 | int height) { |
| 291 | RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); |
| 292 | |
| 293 | rtc::scoped_refptr<VideoFrameBuffer> existing_buffer = |
| 294 | GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI410); |
| 295 | if (existing_buffer) { |
| 296 | // Cast is safe because the only way kI410 buffer is created is |
| 297 | // in the same function below, where |RefCountedObject<I410Buffer>| |
| 298 | // is created. |
Harald Alvestrand | c74412b | 2024-06-06 11:01:02 | [diff] [blame] | 299 | RefCountedObject<I410Buffer>* raw_buffer = |
| 300 | static_cast<RefCountedObject<I410Buffer>*>(existing_buffer.get()); |
Sergio Garcia Murillo | 1389c4b | 2023-01-09 17:29:34 | [diff] [blame] | 301 | // Creates a new scoped_refptr, which is also pointing to the same |
| 302 | // RefCountedObject as buffer, increasing ref count. |
| 303 | return rtc::scoped_refptr<I410Buffer>(raw_buffer); |
| 304 | } |
| 305 | |
| 306 | if (buffers_.size() >= max_number_of_buffers_) |
| 307 | return nullptr; |
| 308 | // Allocate new buffer. |
| 309 | rtc::scoped_refptr<I410Buffer> buffer = I410Buffer::Create(width, height); |
| 310 | |
| 311 | buffers_.push_back(buffer); |
| 312 | return buffer; |
| 313 | } |
| 314 | |
Ilya Nikolaevskiy | 4c87d83 | 2020-09-18 13:18:54 | [diff] [blame] | 315 | rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBufferPool::GetExistingBuffer( |
| 316 | int width, |
| 317 | int height, |
| 318 | VideoFrameBuffer::Type type) { |
| 319 | // Release buffers with wrong resolution or different type. |
| 320 | for (auto it = buffers_.begin(); it != buffers_.end();) { |
| 321 | const auto& buffer = *it; |
| 322 | if (buffer->width() != width || buffer->height() != height || |
| 323 | buffer->type() != type) { |
| 324 | it = buffers_.erase(it); |
| 325 | } else { |
| 326 | ++it; |
| 327 | } |
| 328 | } |
| 329 | // Look for a free buffer. |
| 330 | for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) { |
| 331 | // If the buffer is in use, the ref count will be >= 2, one from the list we |
| 332 | // are looping over and one from the application. If the ref count is 1, |
| 333 | // then the list we are looping over holds the only reference and it's safe |
| 334 | // to reuse. |
| 335 | if (HasOneRef(buffer)) { |
| 336 | RTC_CHECK(buffer->type() == type); |
| 337 | return buffer; |
| 338 | } |
| 339 | } |
| 340 | return nullptr; |
| 341 | } |
| 342 | |
| 343 | } // namespace webrtc |