[M120 Migration][hbbtv] Audio tracks count notification
[platform/framework/web/chromium-efl.git] / media / filters / frame_buffer_pool.cc
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/filters/frame_buffer_pool.h"
6
7 #include "base/logging.h"
8
9 #include "base/check_op.h"
10 #include "base/containers/cxx20_erase.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/process/memory.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/task/sequenced_task_runner.h"
18 #include "base/time/time.h"
19 #include "base/trace_event/memory_allocator_dump.h"
20 #include "base/trace_event/memory_dump_manager.h"
21 #include "base/trace_event/process_memory_dump.h"
22
23 namespace media {
24
25 struct FrameBufferPool::FrameBuffer {
26   // Not using std::vector<uint8_t> as resize() calls take a really long time
27   // for large buffers.
28   std::unique_ptr<uint8_t, base::UncheckedFreeDeleter> data;
29   size_t data_size = 0u;
30   std::unique_ptr<uint8_t, base::UncheckedFreeDeleter> alpha_data;
31   size_t alpha_data_size = 0u;
32   bool held_by_library = false;
33   // Needs to be a counter since a frame buffer might be used multiple times.
34   int held_by_frame = 0;
35   base::TimeTicks last_use_time;
36 };
37
38 FrameBufferPool::FrameBufferPool(bool zero_initialize_memory)
39     : zero_initialize_memory_(zero_initialize_memory),
40       tick_clock_(base::DefaultTickClock::GetInstance()),
41       task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
42
43 FrameBufferPool::~FrameBufferPool() {
44   base::AutoLock lock(lock_);
45   DCHECK(in_shutdown_);
46
47   // May be destructed on any thread.
48 }
49
50 uint8_t* FrameBufferPool::GetFrameBuffer(size_t min_size, void** fb_priv) {
51   base::AutoLock lock(lock_);
52   DCHECK(!in_shutdown_);
53
54   if (!registered_dump_provider_) {
55     // We tell the dump provider to call us on `task_runner_`, which will also
56     // be the thread that will call `Shutdown()` to unregister us.  This lets
57     // the call to `Shutdown()` unregister us synchronously with respect to
58     // calls into `OnMemoryDump()`, so that there won't be calls into
59     // `OnMemoryDump()` after `Shutdown()` completes.  The alternative is to
60     // give the `MemoryDumpManager` ownership of `this`, so that it can delete
61     // it after any outstanding dump requests are complete.  Since we're
62     // refcounted, that would require a wrapper object to own the ref.  This is
63     // much simpler.
64     base::trace_event::MemoryDumpManager::GetInstance()
65         ->RegisterDumpProviderWithSequencedTaskRunner(
66             this, "FrameBufferPool", task_runner_,
67             MemoryDumpProvider::Options());
68     registered_dump_provider_ = true;
69   }
70
71   // Check if a free frame buffer exists.
72   auto it = base::ranges::find_if_not(frame_buffers_, &IsUsedLocked,
73                                       &std::unique_ptr<FrameBuffer>::get);
74
75   // If not, create one.
76   if (it == frame_buffers_.end())
77     it = frame_buffers_.insert(it, std::make_unique<FrameBuffer>());
78
79   auto& frame_buffer = *it;
80
81   // Resize the frame buffer if necessary.
82   frame_buffer->held_by_library = true;
83   if (frame_buffer->data_size < min_size) {
84     // Free the existing |data| first so that the memory can be reused,
85     // if possible. Note that the new array is purposely not initialized.
86     frame_buffer->data.reset();
87
88     uint8_t* data = nullptr;
89     if (!force_allocation_error_) {
90       bool result = false;
91       if (zero_initialize_memory_) {
92         result = base::UncheckedCalloc(1u, min_size,
93                                        reinterpret_cast<void**>(&data));
94       } else {
95         result =
96             base::UncheckedMalloc(min_size, reinterpret_cast<void**>(&data));
97       }
98
99       // Unclear why, but the docs indicate both that `data` will be null on
100       // failure, and also that the return value must not be discarded.
101       if (!result) {
102         data = nullptr;
103       }
104     }
105
106     if (!data) {
107       frame_buffers_.erase(it);
108       return nullptr;
109     }
110
111     frame_buffer->data.reset(data);
112     frame_buffer->data_size = min_size;
113   }
114
115   // Provide the client with a private identifier.
116   *fb_priv = frame_buffer.get();
117   return frame_buffer->data.get();
118 }
119
120 void FrameBufferPool::ReleaseFrameBuffer(void* fb_priv) {
121   base::AutoLock lock(lock_);
122   DCHECK(fb_priv);
123
124   // Note: The library may invoke this method multiple times for the same frame,
125   // so we can't DCHECK that |held_by_library| is true.
126   auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
127   frame_buffer->held_by_library = false;
128
129   if (!IsUsedLocked(frame_buffer)) {
130     frame_buffer->last_use_time = tick_clock_->NowTicks();
131   }
132 }
133
134 uint8_t* FrameBufferPool::AllocateAlphaPlaneForFrameBuffer(size_t min_size,
135                                                            void* fb_priv) {
136   base::AutoLock lock(lock_);
137   DCHECK(fb_priv);
138
139   auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
140   DCHECK(IsUsedLocked(frame_buffer));
141   if (frame_buffer->alpha_data_size < min_size) {
142     // Free the existing |alpha_data| first so that the memory can be reused,
143     // if possible. Note that the new array is purposely not initialized.
144     frame_buffer->alpha_data.reset();
145     uint8_t* data = nullptr;
146     if (force_allocation_error_ ||
147         !base::UncheckedMalloc(min_size, reinterpret_cast<void**>(&data)) ||
148         !data) {
149       return nullptr;
150     }
151     frame_buffer->alpha_data.reset(data);
152     frame_buffer->alpha_data_size = min_size;
153   }
154   return frame_buffer->alpha_data.get();
155 }
156
157 base::OnceClosure FrameBufferPool::CreateFrameCallback(void* fb_priv) {
158   base::AutoLock lock(lock_);
159
160   auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
161   ++frame_buffer->held_by_frame;
162
163   return base::BindOnce(&FrameBufferPool::OnVideoFrameDestroyed, this,
164                         frame_buffer);
165 }
166
167 bool FrameBufferPool::OnMemoryDump(
168     const base::trace_event::MemoryDumpArgs& args,
169     base::trace_event::ProcessMemoryDump* pmd) {
170   // While there's nothing thread-unsafe about this method, if we're ever not
171   // called on `task_runner_`, it likely means that `MemoryDumpManager` is using
172   // the wrong ownership semantics.  See `GetFrameBuffer()` for details.
173   DCHECK(task_runner_->RunsTasksInCurrentSequence());
174   base::AutoLock lock(lock_);
175
176   base::trace_event::MemoryAllocatorDump* memory_dump =
177       pmd->CreateAllocatorDump(
178           base::StringPrintf("media/frame_buffers/memory_pool/0x%" PRIXPTR,
179                              reinterpret_cast<uintptr_t>(this)));
180   base::trace_event::MemoryAllocatorDump* used_memory_dump =
181       pmd->CreateAllocatorDump(
182           base::StringPrintf("media/frame_buffers/memory_pool/used/0x%" PRIXPTR,
183                              reinterpret_cast<uintptr_t>(this)));
184
185   auto* pool_name = base::trace_event::MemoryDumpManager::GetInstance()
186                         ->system_allocator_pool_name();
187   if (pool_name) {
188     pmd->AddSuballocation(memory_dump->guid(), pool_name);
189   }
190   size_t bytes_used = 0;
191   size_t bytes_reserved = 0;
192   for (const auto& frame_buffer : frame_buffers_) {
193     if (IsUsedLocked(frame_buffer.get())) {
194       bytes_used += frame_buffer->data_size + frame_buffer->alpha_data_size;
195     }
196     bytes_reserved += frame_buffer->data_size + frame_buffer->alpha_data_size;
197   }
198
199   memory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
200                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
201                          bytes_reserved);
202   used_memory_dump->AddScalar(
203       base::trace_event::MemoryAllocatorDump::kNameSize,
204       base::trace_event::MemoryAllocatorDump::kUnitsBytes, bytes_used);
205
206   return true;
207 }
208
209 void FrameBufferPool::Shutdown() {
210   // The memory dump manager requires that we unregister on the same thread that
211   // we're expecting memory dump requests on.
212   DCHECK(task_runner_->RunsTasksInCurrentSequence());
213
214   base::AutoLock lock(lock_);
215   in_shutdown_ = true;
216
217   if (registered_dump_provider_) {
218     base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
219         this);
220   }
221
222   // Clear any refs held by the library which isn't good about cleaning up after
223   // itself. This is safe since the library has already been shutdown by this
224   // point.
225   for (const auto& frame_buffer : frame_buffers_)
226     frame_buffer->held_by_library = false;
227
228   EraseUnusedResourcesLocked();
229 }
230
231 // static
232 bool FrameBufferPool::IsUsedLocked(const FrameBuffer* buf) {
233   // Static, so can't check that `lock_` is acquired.
234   return buf->held_by_library || buf->held_by_frame > 0;
235 }
236
237 void FrameBufferPool::EraseUnusedResourcesLocked() {
238   lock_.AssertAcquired();
239   base::EraseIf(frame_buffers_, [](const std::unique_ptr<FrameBuffer>& buf) {
240     return !IsUsedLocked(buf.get());
241   });
242 }
243
244 void FrameBufferPool::OnVideoFrameDestroyed(FrameBuffer* frame_buffer) {
245   base::AutoLock lock(lock_);
246   DCHECK_GT(frame_buffer->held_by_frame, 0);
247   --frame_buffer->held_by_frame;
248
249   if (in_shutdown_) {
250     // If we're in shutdown we can be sure that the library has been destroyed.
251     EraseUnusedResourcesLocked();
252     return;
253   }
254
255   const base::TimeTicks now = tick_clock_->NowTicks();
256   if (!IsUsedLocked(frame_buffer)) {
257     frame_buffer->last_use_time = now;
258   }
259
260   base::EraseIf(frame_buffers_, [now](const std::unique_ptr<FrameBuffer>& buf) {
261     return !IsUsedLocked(buf.get()) &&
262            now - buf->last_use_time > base::Seconds(kStaleFrameLimitSecs);
263   });
264 }
265
266 }  // namespace media