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