blob: 8a7391f850fbb9ed6562b5c7c9aa587f8061a3c1 [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/linux/video_decoder_pipeline.h"
#include "base/bind.h"
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "media/gpu/macros.h"
#include "media/gpu/video_frame_converter.h"
namespace media {
VideoDecoderPipeline::VideoDecoderPipeline(
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<VideoDecoder> decoder,
std::unique_ptr<VideoFrameConverter> frame_converter)
: client_task_runner_(std::move(client_task_runner)),
decoder_(std::move(decoder)),
frame_converter_(std::move(frame_converter)),
weak_this_factory_(this) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(decoder_);
DCHECK(frame_converter_);
DCHECK(client_task_runner_);
frame_converter_->set_parent_task_runner(client_task_runner_);
}
VideoDecoderPipeline::~VideoDecoderPipeline() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void VideoDecoderPipeline::Destroy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
delete this;
}
std::string VideoDecoderPipeline::GetDisplayName() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return decoder_->GetDisplayName();
}
bool VideoDecoderPipeline::IsPlatformDecoder() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return decoder_->IsPlatformDecoder();
}
int VideoDecoderPipeline::GetMaxDecodeRequests() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return decoder_->GetMaxDecodeRequests();
}
bool VideoDecoderPipeline::NeedsBitstreamConversion() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return decoder_->NeedsBitstreamConversion();
}
bool VideoDecoderPipeline::CanReadWithoutStalling() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return decoder_->CanReadWithoutStalling();
}
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(sequence_checker_);
client_output_cb_ = std::move(output_cb);
decoder_->Initialize(
config, low_delay, cdm_context, std::move(init_cb),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecodedThunk,
client_task_runner_, weak_this_factory_.GetWeakPtr()),
std::move(waiting_cb));
}
void VideoDecoderPipeline::Reset(base::OnceClosure closure) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(acourbot) make the decoder jump into our own closure and only call
// the client's when all parts of the pipeline are properly reset.
decoder_->Reset(std::move(closure));
}
void VideoDecoderPipeline::Decode(scoped_refptr<DecoderBuffer> buffer,
DecodeCB decode_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
decoder_->Decode(std::move(buffer), std::move(decode_cb));
}
// static
void VideoDecoderPipeline::OnFrameDecodedThunk(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::Optional<base::WeakPtr<VideoDecoderPipeline>> pipeline,
scoped_refptr<VideoFrame> frame) {
DCHECK(task_runner);
DCHECK(pipeline);
// Workaround for some decoder's non-conformant behavior:
// Decoders are supposed to call the output callback "as soon as possible",
// i.e. directly in their own thread. If they do so the OnFrameDecoded task is
// scheduled on the client task queue, and we have no race condition if we
// are destroyed after that.
//
// But some decoders will run the output callback on their client thread, i.e.
// our own task runner. If we get destroyed before that task is processed,
// then OnFrameDecoded would be scheduled after our destruction and thus
// would never be run, making the client miss a frame.
//
// So we first check whether we already are running on our task runner, and
// execute OnFrameDecoded without delay in that case. Hopefully this can be
// removed in the future.
//
// TODO fix the Mojo service so we don't need to do this dance anymore.
if (task_runner->RunsTasksInCurrentSequence()) {
if (*pipeline)
(*pipeline)->OnFrameDecoded(std::move(frame));
return;
}
task_runner->PostTask(FROM_HERE,
base::BindOnce(&VideoDecoderPipeline::OnFrameDecoded,
*pipeline, std::move(frame)));
}
void VideoDecoderPipeline::OnFrameDecoded(scoped_refptr<VideoFrame> frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(frame_converter_);
scoped_refptr<VideoFrame> converted_frame =
frame_converter_->ConvertFrame(frame);
if (!converted_frame) {
// TODO(acourbot) we need to call the decode_cb with DECODE_ERROR here!
VLOGF(1) << "Error converting frame!";
return;
}
client_output_cb_.Run(converted_frame);
}
} // namespace media