blob: d6a7a1b71fba1a8feec058fe4bdd03496ee13dc1 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/chromeos/vda_video_frame_pool.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "media/gpu/chromeos/gpu_buffer_layout.h"
#include "media/gpu/macros.h"
#include "media/media_buildflags.h"
namespace media {
VdaVideoFramePool::VdaVideoFramePool(
base::WeakPtr<VdaDelegate> vda,
scoped_refptr<base::SequencedTaskRunner> vda_task_runner)
: vda_(std::move(vda)), vda_task_runner_(std::move(vda_task_runner)) {
DVLOGF(3);
DETACH_FROM_SEQUENCE(parent_sequence_checker_);
weak_this_ = weak_this_factory_.GetWeakPtr();
}
VdaVideoFramePool::~VdaVideoFramePool() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
weak_this_factory_.InvalidateWeakPtrs();
}
CroStatus::Or<GpuBufferLayout> VdaVideoFramePool::Initialize(
const Fourcc& fourcc,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
size_t max_num_frames,
bool use_protected,
bool use_linear_buffers) {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
#if !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
if (use_protected) {
LOG(ERROR) << "Cannot allocated protected buffers for VDA";
return CroStatus::Codes::kProtectedContentUnsupported;
}
#endif // !BUILDFLAG(USE_ARC_PROTECTED_MEDIA)
visible_rect_ = visible_rect;
natural_size_ = natural_size;
if (layout_ && max_num_frames_ == max_num_frames && fourcc_ &&
*fourcc_ == fourcc && coded_size_ == coded_size) {
DVLOGF(3) << "Arguments related to frame layout are not changed, skip.";
return *layout_;
}
// Invalidate weak pointers so the re-import callbacks of the frames we are
// about to stop managing do not run and add them back to us.
weak_this_factory_.InvalidateWeakPtrs();
weak_this_ = weak_this_factory_.GetWeakPtr();
// Clear the pool and reset the layout to prevent previous frames are recycled
// back to the pool.
frame_pool_ = {};
max_num_frames_ = 0;
layout_ = absl::nullopt;
fourcc_ = absl::nullopt;
coded_size_ = gfx::Size();
CroStatus::Or<GpuBufferLayout> status_or_layout =
CroStatus::Codes::kResetRequired;
base::WaitableEvent done;
vda_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VdaDelegate::RequestFrames, vda_, fourcc, coded_size,
visible_rect, max_num_frames,
base::BindOnce(&VdaVideoFramePool::OnRequestFramesDone,
&done, &status_or_layout),
base::BindRepeating(&VdaVideoFramePool::ImportFrameThunk,
parent_task_runner_, weak_this_)));
done.Wait();
if (!status_or_layout.has_value())
return status_or_layout;
GpuBufferLayout layout = std::move(status_or_layout).value();
if (layout.fourcc() != fourcc ||
layout.size().height() < coded_size.height() ||
layout.size().width() < coded_size.width()) {
return CroStatus::Codes::kFailedToGetFrameLayout;
}
max_num_frames_ = max_num_frames;
layout_ = std::move(layout);
fourcc_ = fourcc;
coded_size_ = coded_size;
return *layout_;
}
// static
void VdaVideoFramePool::OnRequestFramesDone(
base::WaitableEvent* done,
CroStatus::Or<GpuBufferLayout>* layout,
CroStatus::Or<GpuBufferLayout> layout_value) {
DVLOGF(3);
*layout = std::move(layout_value);
done->Signal();
}
// static
void VdaVideoFramePool::ImportFrameThunk(
scoped_refptr<base::SequencedTaskRunner> task_runner,
absl::optional<base::WeakPtr<VdaVideoFramePool>> weak_this,
scoped_refptr<VideoFrame> frame) {
DVLOGF(3);
DCHECK(weak_this);
task_runner->PostTask(
FROM_HERE, base::BindOnce(&VdaVideoFramePool::ImportFrame, *weak_this,
std::move(frame)));
}
void VdaVideoFramePool::ImportFrame(scoped_refptr<VideoFrame> frame) {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
if (!layout_ || layout_->fourcc().ToVideoPixelFormat() != frame->format() ||
layout_->size() != frame->coded_size() ||
layout_->modifier() != frame->layout().modifier()) {
return;
}
frame_pool_.push(std::move(frame));
CallFrameAvailableCbIfNeeded();
}
scoped_refptr<VideoFrame> VdaVideoFramePool::GetFrame() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
if (IsExhausted())
return nullptr;
scoped_refptr<VideoFrame> origin_frame = std::move(frame_pool_.front());
frame_pool_.pop();
// Update visible_rect and natural_size.
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
origin_frame, origin_frame->format(), visible_rect_, natural_size_);
if (!wrapped_frame) {
DLOG(WARNING) << __func__ << "Failed to wrap a VideoFrame";
return nullptr;
}
wrapped_frame->AddDestructionObserver(
base::BindOnce(&VdaVideoFramePool::ImportFrameThunk, parent_task_runner_,
weak_this_, std::move(origin_frame)));
return wrapped_frame;
}
bool VdaVideoFramePool::IsExhausted() {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
return frame_pool_.empty();
}
void VdaVideoFramePool::NotifyWhenFrameAvailable(base::OnceClosure cb) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
frame_available_cb_ = std::move(cb);
CallFrameAvailableCbIfNeeded();
}
void VdaVideoFramePool::ReleaseAllFrames() {
// TODO(jkardatzke): Implement this when we do protected content on Android
// for Intel platforms. I will do this in a follow up CL, removing the
// NOREACHED() for now in order to prevent a DCHECK when this occurs.
}
absl::optional<GpuBufferLayout> VdaVideoFramePool::GetGpuBufferLayout() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
return layout_;
}
void VdaVideoFramePool::CallFrameAvailableCbIfNeeded() {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
if (frame_available_cb_ && !IsExhausted())
parent_task_runner_->PostTask(FROM_HERE, std::move(frame_available_cb_));
}
} // namespace media