blob: a008db80a19a842e496cfc9f8da5a06695741115 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/filters/frame_buffer_pool.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/process_memory_dump.h"
namespace media {
struct FrameBufferPool::FrameBuffer {
// Not using std::vector<uint8_t> as resize() calls take a really long time
// for large buffers.
std::unique_ptr<uint8_t[]> data;
size_t data_size = 0u;
std::unique_ptr<uint8_t[]> alpha_data;
size_t alpha_data_size = 0u;
bool held_by_library = false;
// Needs to be a counter since a frame buffer might be used multiple times.
int held_by_frame = 0;
base::TimeTicks last_use_time;
};
FrameBufferPool::FrameBufferPool()
: tick_clock_(base::DefaultTickClock::GetInstance()) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
FrameBufferPool::~FrameBufferPool() {
DCHECK(in_shutdown_);
// May be destructed on any thread.
}
uint8_t* FrameBufferPool::GetFrameBuffer(size_t min_size, void** fb_priv) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!in_shutdown_);
if (!registered_dump_provider_) {
base::trace_event::MemoryDumpManager::GetInstance()
->RegisterDumpProviderWithSequencedTaskRunner(
this, "FrameBufferPool", base::SequencedTaskRunnerHandle::Get(),
MemoryDumpProvider::Options());
registered_dump_provider_ = true;
}
// Check if a free frame buffer exists.
auto it = std::find_if(
frame_buffers_.begin(), frame_buffers_.end(),
[](const std::unique_ptr<FrameBuffer>& fb) { return !IsUsed(fb.get()); });
// If not, create one.
if (it == frame_buffers_.end())
it = frame_buffers_.insert(it, std::make_unique<FrameBuffer>());
auto& frame_buffer = *it;
// Resize the frame buffer if necessary.
frame_buffer->held_by_library = true;
if (frame_buffer->data_size < min_size) {
// Free the existing |data| first so that the memory can be reused,
// if possible. Note that the new array is purposely not initialized.
frame_buffer->data.reset();
frame_buffer->data.reset(new uint8_t[min_size]);
frame_buffer->data_size = min_size;
}
// Provide the client with a private identifier.
*fb_priv = frame_buffer.get();
return frame_buffer->data.get();
}
void FrameBufferPool::ReleaseFrameBuffer(void* fb_priv) {
DCHECK(fb_priv);
// Note: The library may invoke this method multiple times for the same frame,
// so we can't DCHECK that |held_by_library| is true.
auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
frame_buffer->held_by_library = false;
if (!IsUsed(frame_buffer))
frame_buffer->last_use_time = tick_clock_->NowTicks();
}
uint8_t* FrameBufferPool::AllocateAlphaPlaneForFrameBuffer(size_t min_size,
void* fb_priv) {
DCHECK(fb_priv);
auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
DCHECK(IsUsed(frame_buffer));
if (frame_buffer->alpha_data_size < min_size) {
// Free the existing |alpha_data| first so that the memory can be reused,
// if possible. Note that the new array is purposely not initialized.
frame_buffer->alpha_data.reset();
frame_buffer->alpha_data.reset(new uint8_t[min_size]);
frame_buffer->alpha_data_size = min_size;
}
return frame_buffer->alpha_data.get();
}
base::Closure FrameBufferPool::CreateFrameCallback(void* fb_priv) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
++frame_buffer->held_by_frame;
return base::Bind(&FrameBufferPool::OnVideoFrameDestroyed, this,
base::SequencedTaskRunnerHandle::Get(), frame_buffer);
}
bool FrameBufferPool::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::trace_event::MemoryAllocatorDump* memory_dump =
pmd->CreateAllocatorDump("media/frame_buffers/memory_pool");
base::trace_event::MemoryAllocatorDump* used_memory_dump =
pmd->CreateAllocatorDump("media/frame_buffers/memory_pool/used");
pmd->AddSuballocation(memory_dump->guid(),
base::trace_event::MemoryDumpManager::GetInstance()
->system_allocator_pool_name());
size_t bytes_used = 0;
size_t bytes_reserved = 0;
for (const auto& frame_buffer : frame_buffers_) {
if (IsUsed(frame_buffer.get()))
bytes_used += frame_buffer->data_size + frame_buffer->alpha_data_size;
bytes_reserved += frame_buffer->data_size + frame_buffer->alpha_data_size;
}
memory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
bytes_reserved);
used_memory_dump->AddScalar(
base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes, bytes_used);
return true;
}
void FrameBufferPool::Shutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
in_shutdown_ = true;
if (registered_dump_provider_) {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
}
// Clear any refs held by the library which isn't good about cleaning up after
// itself. This is safe since the library has already been shutdown by this
// point.
for (const auto& frame_buffer : frame_buffers_)
frame_buffer->held_by_library = false;
EraseUnusedResources();
}
// static
bool FrameBufferPool::IsUsed(const FrameBuffer* buf) {
return buf->held_by_library || buf->held_by_frame > 0;
}
void FrameBufferPool::EraseUnusedResources() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::EraseIf(frame_buffers_, [](const std::unique_ptr<FrameBuffer>& buf) {
return !IsUsed(buf.get());
});
}
void FrameBufferPool::OnVideoFrameDestroyed(
scoped_refptr<base::SequencedTaskRunner> task_runner,
FrameBuffer* frame_buffer) {
if (!task_runner->RunsTasksInCurrentSequence()) {
task_runner->PostTask(
FROM_HERE, base::BindOnce(&FrameBufferPool::OnVideoFrameDestroyed, this,
task_runner, frame_buffer));
return;
}
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(frame_buffer->held_by_frame, 0);
--frame_buffer->held_by_frame;
if (in_shutdown_) {
// If we're in shutdown we can be sure that the library has been destroyed.
EraseUnusedResources();
return;
}
const base::TimeTicks now = tick_clock_->NowTicks();
if (!IsUsed(frame_buffer))
frame_buffer->last_use_time = now;
base::EraseIf(frame_buffers_, [now](const std::unique_ptr<FrameBuffer>& buf) {
return !IsUsed(buf.get()) &&
now - buf->last_use_time >
base::TimeDelta::FromSeconds(kStaleFrameLimitSecs);
});
}
} // namespace media