xref: /aosp_15_r20/external/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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