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
15 #include "api/make_ref_counted.h"
16 #include "rtc_base/checks.h"
17
18 namespace webrtc {
19
20 namespace {
HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer> & buffer)21 bool HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer>& buffer) {
22 // Cast to rtc::RefCountedObject is safe because this function is only called
23 // on locally created VideoFrameBuffers, which are either
24 // `rtc::RefCountedObject<I420Buffer>`, `rtc::RefCountedObject<I444Buffer>` or
25 // `rtc::RefCountedObject<NV12Buffer>`.
26 switch (buffer->type()) {
27 case VideoFrameBuffer::Type::kI420: {
28 return static_cast<rtc::RefCountedObject<I420Buffer>*>(buffer.get())
29 ->HasOneRef();
30 }
31 case VideoFrameBuffer::Type::kI444: {
32 return static_cast<rtc::RefCountedObject<I444Buffer>*>(buffer.get())
33 ->HasOneRef();
34 }
35 case VideoFrameBuffer::Type::kI422: {
36 return static_cast<rtc::RefCountedObject<I422Buffer>*>(buffer.get())
37 ->HasOneRef();
38 }
39 case VideoFrameBuffer::Type::kI010: {
40 return static_cast<rtc::RefCountedObject<I010Buffer>*>(buffer.get())
41 ->HasOneRef();
42 }
43 case VideoFrameBuffer::Type::kI210: {
44 return static_cast<rtc::RefCountedObject<I210Buffer>*>(buffer.get())
45 ->HasOneRef();
46 }
47 case VideoFrameBuffer::Type::kNV12: {
48 return static_cast<rtc::RefCountedObject<NV12Buffer>*>(buffer.get())
49 ->HasOneRef();
50 }
51 default:
52 RTC_DCHECK_NOTREACHED();
53 }
54 return false;
55 }
56
57 } // namespace
58
VideoFrameBufferPool()59 VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {}
60
VideoFrameBufferPool(bool zero_initialize)61 VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize)
62 : VideoFrameBufferPool(zero_initialize,
63 std::numeric_limits<size_t>::max()) {}
64
VideoFrameBufferPool(bool zero_initialize,size_t max_number_of_buffers)65 VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize,
66 size_t max_number_of_buffers)
67 : zero_initialize_(zero_initialize),
68 max_number_of_buffers_(max_number_of_buffers) {}
69
70 VideoFrameBufferPool::~VideoFrameBufferPool() = default;
71
Release()72 void VideoFrameBufferPool::Release() {
73 buffers_.clear();
74 }
75
Resize(size_t max_number_of_buffers)76 bool VideoFrameBufferPool::Resize(size_t max_number_of_buffers) {
77 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
78 size_t used_buffers_count = 0;
79 for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
80 // If the buffer is in use, the ref count will be >= 2, one from the list we
81 // are looping over and one from the application. If the ref count is 1,
82 // then the list we are looping over holds the only reference and it's safe
83 // to reuse.
84 if (!HasOneRef(buffer)) {
85 used_buffers_count++;
86 }
87 }
88 if (used_buffers_count > max_number_of_buffers) {
89 return false;
90 }
91 max_number_of_buffers_ = max_number_of_buffers;
92
93 size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_;
94 auto iter = buffers_.begin();
95 while (iter != buffers_.end() && buffers_to_purge > 0) {
96 if (HasOneRef(*iter)) {
97 iter = buffers_.erase(iter);
98 buffers_to_purge--;
99 } else {
100 ++iter;
101 }
102 }
103 return true;
104 }
105
CreateI420Buffer(int width,int height)106 rtc::scoped_refptr<I420Buffer> VideoFrameBufferPool::CreateI420Buffer(
107 int width,
108 int height) {
109 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
110
111 rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
112 GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI420);
113 if (existing_buffer) {
114 // Cast is safe because the only way kI420 buffer is created is
115 // in the same function below, where `RefCountedObject<I420Buffer>` is
116 // created.
117 rtc::RefCountedObject<I420Buffer>* raw_buffer =
118 static_cast<rtc::RefCountedObject<I420Buffer>*>(existing_buffer.get());
119 // Creates a new scoped_refptr, which is also pointing to the same
120 // RefCountedObject as buffer, increasing ref count.
121 return rtc::scoped_refptr<I420Buffer>(raw_buffer);
122 }
123
124 if (buffers_.size() >= max_number_of_buffers_)
125 return nullptr;
126 // Allocate new buffer.
127 rtc::scoped_refptr<I420Buffer> buffer =
128 rtc::make_ref_counted<I420Buffer>(width, height);
129
130 if (zero_initialize_)
131 buffer->InitializeData();
132
133 buffers_.push_back(buffer);
134 return buffer;
135 }
136
CreateI444Buffer(int width,int height)137 rtc::scoped_refptr<I444Buffer> VideoFrameBufferPool::CreateI444Buffer(
138 int width,
139 int height) {
140 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
141
142 rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
143 GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI444);
144 if (existing_buffer) {
145 // Cast is safe because the only way kI444 buffer is created is
146 // in the same function below, where |RefCountedObject<I444Buffer>|
147 // is created.
148 rtc::RefCountedObject<I444Buffer>* raw_buffer =
149 static_cast<rtc::RefCountedObject<I444Buffer>*>(existing_buffer.get());
150 // Creates a new scoped_refptr, which is also pointing to the same
151 // RefCountedObject as buffer, increasing ref count.
152 return rtc::scoped_refptr<I444Buffer>(raw_buffer);
153 }
154
155 if (buffers_.size() >= max_number_of_buffers_)
156 return nullptr;
157 // Allocate new buffer.
158 rtc::scoped_refptr<I444Buffer> buffer =
159 rtc::make_ref_counted<I444Buffer>(width, height);
160
161 if (zero_initialize_)
162 buffer->InitializeData();
163
164 buffers_.push_back(buffer);
165 return buffer;
166 }
167
CreateI422Buffer(int width,int height)168 rtc::scoped_refptr<I422Buffer> VideoFrameBufferPool::CreateI422Buffer(
169 int width,
170 int height) {
171 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
172
173 rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
174 GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI422);
175 if (existing_buffer) {
176 // Cast is safe because the only way kI422 buffer is created is
177 // in the same function below, where |RefCountedObject<I422Buffer>|
178 // is created.
179 rtc::RefCountedObject<I422Buffer>* raw_buffer =
180 static_cast<rtc::RefCountedObject<I422Buffer>*>(existing_buffer.get());
181 // Creates a new scoped_refptr, which is also pointing to the same
182 // RefCountedObject as buffer, increasing ref count.
183 return rtc::scoped_refptr<I422Buffer>(raw_buffer);
184 }
185
186 if (buffers_.size() >= max_number_of_buffers_)
187 return nullptr;
188 // Allocate new buffer.
189 rtc::scoped_refptr<I422Buffer> buffer =
190 rtc::make_ref_counted<I422Buffer>(width, height);
191
192 if (zero_initialize_)
193 buffer->InitializeData();
194
195 buffers_.push_back(buffer);
196 return buffer;
197 }
198
CreateNV12Buffer(int width,int height)199 rtc::scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer(
200 int width,
201 int height) {
202 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
203
204 rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
205 GetExistingBuffer(width, height, VideoFrameBuffer::Type::kNV12);
206 if (existing_buffer) {
207 // Cast is safe because the only way kI420 buffer is created is
208 // in the same function below, where `RefCountedObject<I420Buffer>` is
209 // created.
210 rtc::RefCountedObject<NV12Buffer>* raw_buffer =
211 static_cast<rtc::RefCountedObject<NV12Buffer>*>(existing_buffer.get());
212 // Creates a new scoped_refptr, which is also pointing to the same
213 // RefCountedObject as buffer, increasing ref count.
214 return rtc::scoped_refptr<NV12Buffer>(raw_buffer);
215 }
216
217 if (buffers_.size() >= max_number_of_buffers_)
218 return nullptr;
219 // Allocate new buffer.
220 rtc::scoped_refptr<NV12Buffer> buffer =
221 rtc::make_ref_counted<NV12Buffer>(width, height);
222
223 if (zero_initialize_)
224 buffer->InitializeData();
225
226 buffers_.push_back(buffer);
227 return buffer;
228 }
229
CreateI010Buffer(int width,int height)230 rtc::scoped_refptr<I010Buffer> VideoFrameBufferPool::CreateI010Buffer(
231 int width,
232 int height) {
233 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
234
235 rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
236 GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI010);
237 if (existing_buffer) {
238 // Cast is safe because the only way kI010 buffer is created is
239 // in the same function below, where |RefCountedObject<I010Buffer>|
240 // is created.
241 rtc::RefCountedObject<I010Buffer>* raw_buffer =
242 static_cast<rtc::RefCountedObject<I010Buffer>*>(existing_buffer.get());
243 // Creates a new scoped_refptr, which is also pointing to the same
244 // RefCountedObject as buffer, increasing ref count.
245 return rtc::scoped_refptr<I010Buffer>(raw_buffer);
246 }
247
248 if (buffers_.size() >= max_number_of_buffers_)
249 return nullptr;
250 // Allocate new buffer.
251 rtc::scoped_refptr<I010Buffer> buffer = I010Buffer::Create(width, height);
252
253 buffers_.push_back(buffer);
254 return buffer;
255 }
256
CreateI210Buffer(int width,int height)257 rtc::scoped_refptr<I210Buffer> VideoFrameBufferPool::CreateI210Buffer(
258 int width,
259 int height) {
260 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
261
262 rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
263 GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI210);
264 if (existing_buffer) {
265 // Cast is safe because the only way kI210 buffer is created is
266 // in the same function below, where |RefCountedObject<I210Buffer>|
267 // is created.
268 rtc::RefCountedObject<I210Buffer>* raw_buffer =
269 static_cast<rtc::RefCountedObject<I210Buffer>*>(existing_buffer.get());
270 // Creates a new scoped_refptr, which is also pointing to the same
271 // RefCountedObject as buffer, increasing ref count.
272 return rtc::scoped_refptr<I210Buffer>(raw_buffer);
273 }
274
275 if (buffers_.size() >= max_number_of_buffers_)
276 return nullptr;
277 // Allocate new buffer.
278 rtc::scoped_refptr<I210Buffer> buffer = I210Buffer::Create(width, height);
279
280 buffers_.push_back(buffer);
281 return buffer;
282 }
283
GetExistingBuffer(int width,int height,VideoFrameBuffer::Type type)284 rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBufferPool::GetExistingBuffer(
285 int width,
286 int height,
287 VideoFrameBuffer::Type type) {
288 // Release buffers with wrong resolution or different type.
289 for (auto it = buffers_.begin(); it != buffers_.end();) {
290 const auto& buffer = *it;
291 if (buffer->width() != width || buffer->height() != height ||
292 buffer->type() != type) {
293 it = buffers_.erase(it);
294 } else {
295 ++it;
296 }
297 }
298 // Look for a free buffer.
299 for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
300 // If the buffer is in use, the ref count will be >= 2, one from the list we
301 // are looping over and one from the application. If the ref count is 1,
302 // then the list we are looping over holds the only reference and it's safe
303 // to reuse.
304 if (HasOneRef(buffer)) {
305 RTC_CHECK(buffer->type() == type);
306 return buffer;
307 }
308 }
309 return nullptr;
310 }
311
312 } // namespace webrtc
313