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
12 #ifdef RTC_ENABLE_VP9
13
14 #include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
15
16 #include "rtc_base/checks.h"
17 #include "rtc_base/logging.h"
18 #include "vpx/vpx_codec.h"
19 #include "vpx/vpx_decoder.h"
20 #include "vpx/vpx_frame_buffer.h"
21
22 namespace webrtc {
23
GetData()24 uint8_t* Vp9FrameBufferPool::Vp9FrameBuffer::GetData() {
25 return data_.data<uint8_t>();
26 }
27
GetDataSize() const28 size_t Vp9FrameBufferPool::Vp9FrameBuffer::GetDataSize() const {
29 return data_.size();
30 }
31
SetSize(size_t size)32 void Vp9FrameBufferPool::Vp9FrameBuffer::SetSize(size_t size) {
33 data_.SetSize(size);
34 }
35
InitializeVpxUsePool(vpx_codec_ctx * vpx_codec_context)36 bool Vp9FrameBufferPool::InitializeVpxUsePool(
37 vpx_codec_ctx* vpx_codec_context) {
38 RTC_DCHECK(vpx_codec_context);
39 // Tell libvpx to use this pool.
40 if (vpx_codec_set_frame_buffer_functions(
41 // In which context to use these callback functions.
42 vpx_codec_context,
43 // Called by libvpx when it needs another frame buffer.
44 &Vp9FrameBufferPool::VpxGetFrameBuffer,
45 // Called by libvpx when it no longer uses a frame buffer.
46 &Vp9FrameBufferPool::VpxReleaseFrameBuffer,
47 // `this` will be passed as `user_priv` to VpxGetFrameBuffer.
48 this)) {
49 // Failed to configure libvpx to use Vp9FrameBufferPool.
50 return false;
51 }
52 return true;
53 }
54
55 rtc::scoped_refptr<Vp9FrameBufferPool::Vp9FrameBuffer>
GetFrameBuffer(size_t min_size)56 Vp9FrameBufferPool::GetFrameBuffer(size_t min_size) {
57 RTC_DCHECK_GT(min_size, 0);
58 rtc::scoped_refptr<Vp9FrameBuffer> available_buffer = nullptr;
59 {
60 MutexLock lock(&buffers_lock_);
61 // Do we have a buffer we can recycle?
62 for (const auto& buffer : allocated_buffers_) {
63 if (buffer->HasOneRef()) {
64 available_buffer = buffer;
65 break;
66 }
67 }
68 // Otherwise create one.
69 if (available_buffer == nullptr) {
70 available_buffer = new Vp9FrameBuffer();
71 allocated_buffers_.push_back(available_buffer);
72 if (allocated_buffers_.size() > max_num_buffers_) {
73 RTC_LOG(LS_WARNING)
74 << allocated_buffers_.size()
75 << " Vp9FrameBuffers have been "
76 "allocated by a Vp9FrameBufferPool (exceeding what is "
77 "considered reasonable, "
78 << max_num_buffers_ << ").";
79
80 // TODO(phoglund): this limit is being hit in tests since Oct 5 2016.
81 // See https://bugs.chromium.org/p/webrtc/issues/detail?id=6484.
82 // RTC_DCHECK_NOTREACHED();
83 }
84 }
85 }
86
87 available_buffer->SetSize(min_size);
88 return available_buffer;
89 }
90
GetNumBuffersInUse() const91 int Vp9FrameBufferPool::GetNumBuffersInUse() const {
92 int num_buffers_in_use = 0;
93 MutexLock lock(&buffers_lock_);
94 for (const auto& buffer : allocated_buffers_) {
95 if (!buffer->HasOneRef())
96 ++num_buffers_in_use;
97 }
98 return num_buffers_in_use;
99 }
100
Resize(size_t max_number_of_buffers)101 bool Vp9FrameBufferPool::Resize(size_t max_number_of_buffers) {
102 MutexLock lock(&buffers_lock_);
103 size_t used_buffers_count = 0;
104 for (const auto& buffer : allocated_buffers_) {
105 // If the buffer is in use, the ref count will be >= 2, one from the list we
106 // are looping over and one from the application. If the ref count is 1,
107 // then the list we are looping over holds the only reference and it's safe
108 // to reuse.
109 if (!buffer->HasOneRef()) {
110 used_buffers_count++;
111 }
112 }
113 if (used_buffers_count > max_number_of_buffers) {
114 return false;
115 }
116 max_num_buffers_ = max_number_of_buffers;
117
118 size_t buffers_to_purge = allocated_buffers_.size() - max_num_buffers_;
119 auto iter = allocated_buffers_.begin();
120 while (iter != allocated_buffers_.end() && buffers_to_purge > 0) {
121 if ((*iter)->HasOneRef()) {
122 iter = allocated_buffers_.erase(iter);
123 buffers_to_purge--;
124 } else {
125 ++iter;
126 }
127 }
128 return true;
129 }
130
ClearPool()131 void Vp9FrameBufferPool::ClearPool() {
132 MutexLock lock(&buffers_lock_);
133 allocated_buffers_.clear();
134 }
135
136 // static
VpxGetFrameBuffer(void * user_priv,size_t min_size,vpx_codec_frame_buffer * fb)137 int32_t Vp9FrameBufferPool::VpxGetFrameBuffer(void* user_priv,
138 size_t min_size,
139 vpx_codec_frame_buffer* fb) {
140 RTC_DCHECK(user_priv);
141 RTC_DCHECK(fb);
142
143 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
144 // Limit size of 8k YUV highdef frame
145 size_t size_limit = 7680 * 4320 * 3 / 2 * 2;
146 if (min_size > size_limit)
147 return -1;
148 #endif
149
150 Vp9FrameBufferPool* pool = static_cast<Vp9FrameBufferPool*>(user_priv);
151
152 rtc::scoped_refptr<Vp9FrameBuffer> buffer = pool->GetFrameBuffer(min_size);
153 fb->data = buffer->GetData();
154 fb->size = buffer->GetDataSize();
155 // Store Vp9FrameBuffer* in `priv` for use in VpxReleaseFrameBuffer.
156 // This also makes vpx_codec_get_frame return images with their `fb_priv` set
157 // to `buffer` which is important for external reference counting.
158 // Release from refptr so that the buffer's `ref_count_` remains 1 when
159 // `buffer` goes out of scope.
160 fb->priv = static_cast<void*>(buffer.release());
161 return 0;
162 }
163
164 // static
VpxReleaseFrameBuffer(void * user_priv,vpx_codec_frame_buffer * fb)165 int32_t Vp9FrameBufferPool::VpxReleaseFrameBuffer(void* user_priv,
166 vpx_codec_frame_buffer* fb) {
167 RTC_DCHECK(user_priv);
168 RTC_DCHECK(fb);
169 Vp9FrameBuffer* buffer = static_cast<Vp9FrameBuffer*>(fb->priv);
170 if (buffer != nullptr) {
171 buffer->Release();
172 // When libvpx fails to decode and you continue to try to decode (and fail)
173 // libvpx can for some reason try to release the same buffer multiple times.
174 // Setting `priv` to null protects against trying to Release multiple times.
175 fb->priv = nullptr;
176 }
177 return 0;
178 }
179
180 } // namespace webrtc
181
182 #endif // RTC_ENABLE_VP9
183