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 #include "webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
13 
14 #include "vpx/vpx_codec.h"
15 #include "vpx/vpx_decoder.h"
16 #include "vpx/vpx_frame_buffer.h"
17 
18 #include "webrtc/base/checks.h"
19 #include "webrtc/base/logging.h"
20 
21 namespace webrtc {
22 
GetData()23 uint8_t* Vp9FrameBufferPool::Vp9FrameBuffer::GetData() {
24   return data_.data<uint8_t>();
25 }
26 
GetDataSize() const27 size_t Vp9FrameBufferPool::Vp9FrameBuffer::GetDataSize() const {
28   return data_.size();
29 }
30 
SetSize(size_t size)31 void Vp9FrameBufferPool::Vp9FrameBuffer::SetSize(size_t size) {
32   data_.SetSize(size);
33 }
34 
InitializeVpxUsePool(vpx_codec_ctx * vpx_codec_context)35 bool Vp9FrameBufferPool::InitializeVpxUsePool(
36     vpx_codec_ctx* vpx_codec_context) {
37   RTC_DCHECK(vpx_codec_context);
38   // Tell libvpx to use this pool.
39   if (vpx_codec_set_frame_buffer_functions(
40           // In which context to use these callback functions.
41           vpx_codec_context,
42           // Called by libvpx when it needs another frame buffer.
43           &Vp9FrameBufferPool::VpxGetFrameBuffer,
44           // Called by libvpx when it no longer uses a frame buffer.
45           &Vp9FrameBufferPool::VpxReleaseFrameBuffer,
46           // |this| will be passed as |user_priv| to VpxGetFrameBuffer.
47           this)) {
48     // Failed to configure libvpx to use Vp9FrameBufferPool.
49     return false;
50   }
51   return true;
52 }
53 
54 rtc::scoped_refptr<Vp9FrameBufferPool::Vp9FrameBuffer>
GetFrameBuffer(size_t min_size)55 Vp9FrameBufferPool::GetFrameBuffer(size_t min_size) {
56   RTC_DCHECK_GT(min_size, 0u);
57   rtc::scoped_refptr<Vp9FrameBuffer> available_buffer = nullptr;
58   {
59     rtc::CritScope cs(&buffers_lock_);
60     // Do we have a buffer we can recycle?
61     for (const auto& buffer : allocated_buffers_) {
62       if (buffer->HasOneRef()) {
63         available_buffer = buffer;
64         break;
65       }
66     }
67     // Otherwise create one.
68     if (available_buffer == nullptr) {
69       available_buffer = new rtc::RefCountedObject<Vp9FrameBuffer>();
70       allocated_buffers_.push_back(available_buffer);
71       if (allocated_buffers_.size() > max_num_buffers_) {
72         LOG(LS_WARNING)
73             << allocated_buffers_.size() << " Vp9FrameBuffers have been "
74             << "allocated by a Vp9FrameBufferPool (exceeding what is "
75             << "considered reasonable, " << max_num_buffers_ << ").";
76         RTC_NOTREACHED();
77       }
78     }
79   }
80 
81   available_buffer->SetSize(min_size);
82   return available_buffer;
83 }
84 
GetNumBuffersInUse() const85 int Vp9FrameBufferPool::GetNumBuffersInUse() const {
86   int num_buffers_in_use = 0;
87   rtc::CritScope cs(&buffers_lock_);
88   for (const auto& buffer : allocated_buffers_) {
89     if (!buffer->HasOneRef())
90       ++num_buffers_in_use;
91   }
92   return num_buffers_in_use;
93 }
94 
ClearPool()95 void Vp9FrameBufferPool::ClearPool() {
96   rtc::CritScope cs(&buffers_lock_);
97   allocated_buffers_.clear();
98 }
99 
100 // static
VpxGetFrameBuffer(void * user_priv,size_t min_size,vpx_codec_frame_buffer * fb)101 int32_t Vp9FrameBufferPool::VpxGetFrameBuffer(void* user_priv,
102                                               size_t min_size,
103                                               vpx_codec_frame_buffer* fb) {
104   RTC_DCHECK(user_priv);
105   RTC_DCHECK(fb);
106   Vp9FrameBufferPool* pool = static_cast<Vp9FrameBufferPool*>(user_priv);
107 
108   rtc::scoped_refptr<Vp9FrameBuffer> buffer = pool->GetFrameBuffer(min_size);
109   fb->data = buffer->GetData();
110   fb->size = buffer->GetDataSize();
111   // Store Vp9FrameBuffer* in |priv| for use in VpxReleaseFrameBuffer.
112   // This also makes vpx_codec_get_frame return images with their |fb_priv| set
113   // to |buffer| which is important for external reference counting.
114   // Release from refptr so that the buffer's |ref_count_| remains 1 when
115   // |buffer| goes out of scope.
116   fb->priv = static_cast<void*>(buffer.release());
117   return 0;
118 }
119 
120 // static
VpxReleaseFrameBuffer(void * user_priv,vpx_codec_frame_buffer * fb)121 int32_t Vp9FrameBufferPool::VpxReleaseFrameBuffer(void* user_priv,
122                                                   vpx_codec_frame_buffer* fb) {
123   RTC_DCHECK(user_priv);
124   RTC_DCHECK(fb);
125   Vp9FrameBuffer* buffer = static_cast<Vp9FrameBuffer*>(fb->priv);
126   if (buffer != nullptr) {
127     buffer->Release();
128     // When libvpx fails to decode and you continue to try to decode (and fail)
129     // libvpx can for some reason try to release the same buffer multiple times.
130     // Setting |priv| to null protects against trying to Release multiple times.
131     fb->priv = nullptr;
132   }
133   return 0;
134 }
135 
136 }  // namespace webrtc
137