blob: 9c6e5265eaf7d255cb8e51f27b7e1a69c1267f40 [file] [log] [blame]
// Copyright 2019 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/gpu/chromeos/video_decoder_pipeline.h"
#include <memory>
#include "base/bind.h"
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
#include "media/gpu/chromeos/platform_video_frame_pool.h"
#include "media/gpu/macros.h"
namespace media {
DecoderInterface::DecoderInterface(
scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
base::WeakPtr<DecoderInterface::Client> client)
: decoder_task_runner_(std::move(decoder_task_runner)),
client_(std::move(client)) {}
DecoderInterface::~DecoderInterface() = default;
// static
std::unique_ptr<VideoDecoder> VideoDecoderPipeline::Create(
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter,
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory,
GetCreateVDFunctionsCB get_create_vd_functions_cb) {
if (!client_task_runner || !frame_pool || !frame_converter) {
VLOGF(1) << "One of arguments is nullptr.";
return nullptr;
}
if (get_create_vd_functions_cb.Run(nullptr).empty()) {
VLOGF(1) << "No available function to create video decoder.";
return nullptr;
}
return base::WrapUnique<VideoDecoder>(new VideoDecoderPipeline(
std::move(client_task_runner), std::move(frame_pool),
std::move(frame_converter), gpu_memory_buffer_factory,
std::move(get_create_vd_functions_cb)));
}
VideoDecoderPipeline::VideoDecoderPipeline(
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter,
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory,
GetCreateVDFunctionsCB get_create_vd_functions_cb)
: client_task_runner_(std::move(client_task_runner)),
decoder_task_runner_(base::CreateSingleThreadTaskRunner(
{base::ThreadPool(), base::WithBaseSyncPrimitives(),
base::TaskPriority::USER_VISIBLE},
base::SingleThreadTaskRunnerThreadMode::DEDICATED)),
main_frame_pool_(std::move(frame_pool)),
gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
frame_converter_(std::move(frame_converter)),
get_create_vd_functions_cb_(std::move(get_create_vd_functions_cb)) {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DETACH_FROM_SEQUENCE(decoder_sequence_checker_);
DCHECK(main_frame_pool_);
DCHECK(frame_converter_);
DCHECK(client_task_runner_);
DVLOGF(2);
client_weak_this_ = client_weak_this_factory_.GetWeakPtr();
decoder_weak_this_ = decoder_weak_this_factory_.GetWeakPtr();
main_frame_pool_->set_parent_task_runner(decoder_task_runner_);
frame_converter_->Initialize(
decoder_task_runner_,
base::BindRepeating(&VideoDecoderPipeline::OnFrameConverted,
decoder_weak_this_));
}
VideoDecoderPipeline::~VideoDecoderPipeline() {
// We have to destroy |main_frame_pool_| on |decoder_task_runner_|, so the
// destructor is also called on |decoder_task_runner_|.
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
}
void VideoDecoderPipeline::Destroy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DVLOGF(2);
client_weak_this_factory_.InvalidateWeakPtrs();
decoder_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VideoDecoderPipeline::DestroyTask, decoder_weak_this_));
}
void VideoDecoderPipeline::DestroyTask() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
decoder_weak_this_factory_.InvalidateWeakPtrs();
// The frame pool and converter should be destroyed on |decoder_task_runner_|.
main_frame_pool_.reset();
frame_converter_.reset();
decoder_.reset();
used_create_vd_func_ = nullptr;
delete this;
}
std::string VideoDecoderPipeline::GetDisplayName() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
return "VideoDecoderPipeline";
}
bool VideoDecoderPipeline::IsPlatformDecoder() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
return true;
}
int VideoDecoderPipeline::GetMaxDecodeRequests() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
return 4;
}
bool VideoDecoderPipeline::NeedsBitstreamConversion() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
return needs_bitstream_conversion_;
}
bool VideoDecoderPipeline::CanReadWithoutStalling() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
return main_frame_pool_ && !main_frame_pool_->IsExhausted();
}
void VideoDecoderPipeline::Initialize(const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
InitCB init_cb,
const OutputCB& output_cb,
const WaitingCB& waiting_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
VLOGF(2) << "config: " << config.AsHumanReadableString();
if (!config.IsValidConfig()) {
VLOGF(1) << "config is not valid";
std::move(init_cb).Run(false);
return;
}
if (config.is_encrypted()) {
VLOGF(1) << "Encrypted streams are not supported for this VD";
std::move(init_cb).Run(false);
return;
}
if (cdm_context) {
VLOGF(1) << "cdm_context is not supported.";
std::move(init_cb).Run(false);
return;
}
needs_bitstream_conversion_ = (config.codec() == kCodecH264);
decoder_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VideoDecoderPipeline::InitializeTask, decoder_weak_this_,
config, std::move(init_cb), std::move(output_cb)));
}
void VideoDecoderPipeline::InitializeTask(const VideoDecoderConfig& config,
InitCB init_cb,
const OutputCB& output_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(!init_cb_);
client_output_cb_ = std::move(output_cb);
init_cb_ = std::move(init_cb);
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs =
get_create_vd_functions_cb_.Run(used_create_vd_func_);
if (!decoder_) {
CreateAndInitializeVD(std::move(create_vd_funcs), config);
} else {
decoder_->Initialize(
config,
// If it fails to re-initialize current |decoder_|, it will create
// another decoder instance by trying available VD creation functions
// again. See |OnInitializeDone| for detail.
base::BindOnce(&VideoDecoderPipeline::OnInitializeDone,
decoder_weak_this_, std::move(create_vd_funcs), config),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded,
decoder_weak_this_));
}
}
void VideoDecoderPipeline::CreateAndInitializeVD(
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(init_cb_);
DCHECK(!decoder_);
DCHECK(!used_create_vd_func_);
DVLOGF(3);
if (create_vd_funcs.empty()) {
DVLOGF(2) << "No available video decoder.";
client_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(init_cb_), false));
return;
}
used_create_vd_func_ = create_vd_funcs.front();
create_vd_funcs.pop();
decoder_ = used_create_vd_func_(decoder_task_runner_, decoder_weak_this_);
if (!decoder_) {
DVLOGF(2) << "Failed to create VideoDecoder.";
used_create_vd_func_ = nullptr;
return CreateAndInitializeVD(std::move(create_vd_funcs), config);
}
decoder_->Initialize(
config,
base::BindOnce(&VideoDecoderPipeline::OnInitializeDone,
decoder_weak_this_, std::move(create_vd_funcs), config),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded,
decoder_weak_this_));
}
void VideoDecoderPipeline::OnInitializeDone(
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(init_cb_);
DVLOGF(4) << "Initialization " << (success ? "success." : "failure.");
if (success) {
DVLOGF(2) << "Initialize VD successfully.";
client_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(init_cb_), true));
return;
}
DVLOGF(3) << "Reset VD, try the next create function.";
decoder_ = nullptr;
used_create_vd_func_ = nullptr;
CreateAndInitializeVD(std::move(create_vd_funcs), config);
}
void VideoDecoderPipeline::Reset(base::OnceClosure closure) {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DVLOGF(3);
decoder_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&VideoDecoderPipeline::ResetTask,
decoder_weak_this_, std::move(closure)));
}
void VideoDecoderPipeline::ResetTask(base::OnceClosure closure) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(decoder_);
DCHECK(!client_reset_cb_);
DVLOGF(3);
need_notify_decoder_flushed_ = false;
client_reset_cb_ = std::move(closure);
decoder_->Reset(
base::BindOnce(&VideoDecoderPipeline::OnResetDone, decoder_weak_this_));
}
void VideoDecoderPipeline::OnResetDone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(client_reset_cb_);
DVLOGF(3);
frame_converter_->AbortPendingFrames();
CallFlushCbIfNeeded(DecodeStatus::ABORTED);
client_task_runner_->PostTask(FROM_HERE, std::move(client_reset_cb_));
}
void VideoDecoderPipeline::Decode(scoped_refptr<DecoderBuffer> buffer,
DecodeCB decode_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DVLOGF(4);
decoder_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VideoDecoderPipeline::DecodeTask, decoder_weak_this_,
std::move(buffer), std::move(decode_cb)));
}
void VideoDecoderPipeline::DecodeTask(scoped_refptr<DecoderBuffer> buffer,
DecodeCB decode_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(decoder_);
DVLOGF(4);
bool is_flush = buffer->end_of_stream();
decoder_->Decode(
std::move(buffer),
base::BindOnce(&VideoDecoderPipeline::OnDecodeDone, decoder_weak_this_,
is_flush, std::move(decode_cb)));
}
void VideoDecoderPipeline::OnDecodeDone(bool is_flush,
DecodeCB decode_cb,
DecodeStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(4) << "is_flush: " << is_flush << ", status: " << status;
if (has_error_)
status = DecodeStatus::DECODE_ERROR;
if (is_flush && status == DecodeStatus::OK) {
client_flush_cb_ = std::move(decode_cb);
CallFlushCbIfNeeded(DecodeStatus::OK);
return;
}
client_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(decode_cb), status));
}
void VideoDecoderPipeline::OnFrameDecoded(scoped_refptr<VideoFrame> frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(frame_converter_);
DVLOGF(4);
frame_converter_->ConvertFrame(std::move(frame));
}
void VideoDecoderPipeline::OnFrameConverted(scoped_refptr<VideoFrame> frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(4);
if (!frame)
return OnError("Frame converter returns null frame.");
if (has_error_) {
DVLOGF(2) << "Skip returning frames after error occurs.";
return;
}
// Flag that the video frame is capable of being put in an overlay.
frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
// Flag that the video frame was decoded in a power efficient way.
frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
// MojoVideoDecoderService expects the |output_cb_| to be called on the client
// task runner, even though media::VideoDecoder states frames should be output
// without any thread jumping.
// Note that all the decode/flush/output/reset callbacks are executed on
// |client_task_runner_|.
client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(client_output_cb_, std::move(frame)));
// After outputting a frame, flush might be completed.
CallFlushCbIfNeeded(DecodeStatus::OK);
CallOnPipelineFlushedIfNeeded();
}
bool VideoDecoderPipeline::HasPendingFrames() const {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
return frame_converter_->HasPendingFrames();
}
void VideoDecoderPipeline::OnError(const std::string& msg) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
VLOGF(1) << msg;
has_error_ = true;
CallFlushCbIfNeeded(DecodeStatus::DECODE_ERROR);
}
void VideoDecoderPipeline::CallFlushCbIfNeeded(DecodeStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3) << "status: " << status;
if (!client_flush_cb_)
return;
// Flush is not completed yet.
if (status == DecodeStatus::OK && HasPendingFrames())
return;
client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(client_flush_cb_), status));
}
void VideoDecoderPipeline::PrepareChangeResolution() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
DCHECK(!need_notify_decoder_flushed_);
need_notify_decoder_flushed_ = true;
CallOnPipelineFlushedIfNeeded();
}
void VideoDecoderPipeline::CallOnPipelineFlushedIfNeeded() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
if (need_notify_decoder_flushed_ && !HasPendingFrames()) {
need_notify_decoder_flushed_ = false;
decoder_->OnPipelineFlushed();
}
}
DmabufVideoFramePool* VideoDecoderPipeline::GetVideoFramePool() const {
return main_frame_pool_.get();
}
} // namespace media