blob: b70a1f3a96244b95660c17c00854ab90fd3a6d9d [file] [log] [blame]
// Copyright 2013 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/base/video_frame_pool.h"
#include "base/bind.h"
#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/time/default_tick_clock.h"
namespace media {
class VideoFramePool::PoolImpl
: public base::RefCountedThreadSafe<VideoFramePool::PoolImpl> {
// See VideoFramePool::CreateFrame() for usage. Attempts to keep |frames_| in
// LRU order by always pulling from the back of |frames_|.
scoped_refptr<VideoFrame> CreateFrame(VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp);
// Shuts down the frame pool and releases all frames in |frames_|.
// Once this is called frames will no longer be inserted back into
// |frames_|.
void Shutdown();
size_t get_pool_size_for_testing() {
base::AutoLock auto_lock(lock_);
return frames_.size();
void set_tick_clock_for_testing(const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
friend class base::RefCountedThreadSafe<VideoFramePool::PoolImpl>;
// Called when the frame wrapper gets destroyed. |frame| is the actual frame
// that was wrapped and is placed in |frames_| by this function so it can be
// reused. This will attempt to expire frames that haven't been used in some
// time. It relies on |frames_| being in LRU order with the front being the
// least recently used entry.
void FrameReleased(scoped_refptr<VideoFrame> frame);
base::Lock lock_;
bool is_shutdown_ GUARDED_BY(lock_) = false;
struct FrameEntry {
base::TimeTicks last_use_time;
scoped_refptr<VideoFrame> frame;
base::circular_deque<FrameEntry> frames_ GUARDED_BY(lock_);
// |tick_clock_| is always a DefaultTickClock outside of testing.
const base::TickClock* tick_clock_;
: tick_clock_(base::DefaultTickClock::GetInstance()) {}
VideoFramePool::PoolImpl::~PoolImpl() {
scoped_refptr<VideoFrame> VideoFramePool::PoolImpl::CreateFrame(
VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) {
base::AutoLock auto_lock(lock_);
scoped_refptr<VideoFrame> frame;
while (!frame && !frames_.empty()) {
scoped_refptr<VideoFrame> pool_frame = std::move(frames_.back().frame);
if (pool_frame->format() == format &&
pool_frame->coded_size() == coded_size &&
pool_frame->visible_rect() == visible_rect &&
pool_frame->natural_size() == natural_size) {
frame = pool_frame;
if (!frame) {
frame = VideoFrame::CreateZeroInitializedFrame(
format, coded_size, visible_rect, natural_size, timestamp);
// This can happen if the arguments are not valid.
if (!frame) {
LOG(ERROR) << "Failed to create a video frame";
return nullptr;
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
frame, frame->format(), frame->visible_rect(), frame->natural_size());
&VideoFramePool::PoolImpl::FrameReleased, this, std::move(frame)));
return wrapped_frame;
void VideoFramePool::PoolImpl::Shutdown() {
base::AutoLock auto_lock(lock_);
is_shutdown_ = true;
void VideoFramePool::PoolImpl::FrameReleased(scoped_refptr<VideoFrame> frame) {
base::AutoLock auto_lock(lock_);
if (is_shutdown_)
const base::TimeTicks now = tick_clock_->NowTicks();
frames_.push_back({now, std::move(frame)});
// After this loop, |stale_index| is the index of the oldest non-stale frame.
// Such an index must exist because |frame| is never stale.
int stale_index = -1;
constexpr base::TimeDelta kStaleFrameLimit = base::TimeDelta::FromSeconds(10);
while (now - frames_[++stale_index].last_use_time > kStaleFrameLimit) {
// Last frame should never be included since we just added it.
DCHECK_LE(static_cast<size_t>(stale_index), frames_.size());
if (stale_index)
frames_.erase(frames_.begin(), frames_.begin() + stale_index);
VideoFramePool::VideoFramePool() : pool_(new PoolImpl()) {}
VideoFramePool::~VideoFramePool() {
scoped_refptr<VideoFrame> VideoFramePool::CreateFrame(
VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) {
return pool_->CreateFrame(format, coded_size, visible_rect, natural_size,
size_t VideoFramePool::GetPoolSizeForTesting() const {
return pool_->get_pool_size_for_testing();
void VideoFramePool::SetTickClockForTesting(const base::TickClock* tick_clock) {
} // namespace media