Commit 96daa858 authored by Miguel Casas's avatar Miguel Casas Committed by Commit Bot

media/gpu/chromeos/VideoDecoderPipeline: refactor initialize logic

VideoDecoderPipeline holds together a (Vaapi|V4L2)VideoDecoder, a frame
pool and a converter. It gets a series of VideoDecoder create functions
from ChromeosVideoDecoderFactory, and needs to go through each decoder,
trying to create and then Initialize() each -- the first one that works
is good to go.

VideoDecoderPipeline retrieves such create functions and holds on to the
last |used_create_vd_func_| along such Create+Initialize cycles (to
avoid repeating a potentially broken one) and relies on the factory's
GetCreateDecoderFunctions() to return all such functions /except/ the
one passed as argument. This logic, aside from convoluted, is broken if
there were more than 2 such create functions. This CL simplifies all
that by keeping a list of |remaining_create_decoder_functions_| that are
tried and, if failed, discarded.

During the moves along the different initialization methods (see [1]),
a given Status is constructed and filled to indicate a potential
failure. Status [2] works in a non-intuitive way because it must be
constructed with a given StatusCode, which becomes its code(), but can
AddCause() to it -more StatusCodes- that are sadly, opaque. ToT uses a
kChromeOSVideoDecoderNoDecoders as start StatusCode, so any subsequent
errors e.g. kDecoderFailedCreation or kDecoderFailedInitialization are
thrown into the opaque causes. This CL changes this to start with a kOk,
and then collect the first meaningful error StatusCode.

Unittests added to hopefully clarify and identify the use cases.

[1] https://i.imgur.com/qH17CTJ.jpg (or https://imgur.com/a/anCJ2Ig)
[2] https://source.chromium.org/chromium/chromium/src/+/master:media/base/status.h;l=41

Bug: 1040291
Change-Id: I35889605850eb51e0dfe9308688c092d2b755edd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2259472
Commit-Queue: Miguel Casas <mcasas@chromium.org>
Reviewed-by: default avatarJao-ke Chin-Lee <jchinlee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781964}
parent 97a8b5d1
...@@ -149,6 +149,7 @@ source_set("unit_tests") { ...@@ -149,6 +149,7 @@ source_set("unit_tests") {
"mailbox_video_frame_converter_unittest.cc", "mailbox_video_frame_converter_unittest.cc",
"platform_video_frame_pool_unittest.cc", "platform_video_frame_pool_unittest.cc",
"platform_video_frame_utils_unittest.cc", "platform_video_frame_utils_unittest.cc",
"video_decoder_pipeline_unittest.cc",
] ]
} }
......
...@@ -26,10 +26,9 @@ namespace media { ...@@ -26,10 +26,9 @@ namespace media {
namespace { namespace {
// Get a list of the available functions for creating VideoDecoder. // Gets a list of the available functions for creating VideoDecoders.
base::queue<VideoDecoderPipeline::CreateVDFunc> GetCreateVDFunctions( VideoDecoderPipeline::CreateDecoderFunctions GetCreateDecoderFunctions() {
VideoDecoderPipeline::CreateVDFunc cur_create_vd_func) { constexpr VideoDecoderPipeline::CreateDecoderFunction kCreateVDFuncs[] = {
static constexpr VideoDecoderPipeline::CreateVDFunc kCreateVDFuncs[] = {
#if BUILDFLAG(USE_VAAPI) #if BUILDFLAG(USE_VAAPI)
&VaapiVideoDecoder::Create, &VaapiVideoDecoder::Create,
#endif // BUILDFLAG(USE_VAAPI) #endif // BUILDFLAG(USE_VAAPI)
...@@ -39,12 +38,8 @@ base::queue<VideoDecoderPipeline::CreateVDFunc> GetCreateVDFunctions( ...@@ -39,12 +38,8 @@ base::queue<VideoDecoderPipeline::CreateVDFunc> GetCreateVDFunctions(
#endif // BUILDFLAG(USE_V4L2_CODEC) #endif // BUILDFLAG(USE_V4L2_CODEC)
}; };
base::queue<VideoDecoderPipeline::CreateVDFunc> ret; return VideoDecoderPipeline::CreateDecoderFunctions(
for (const auto& func : kCreateVDFuncs) { kCreateVDFuncs, kCreateVDFuncs + base::size(kCreateVDFuncs));
if (func != cur_create_vd_func)
ret.push(func);
}
return ret;
} }
} // namespace } // namespace
...@@ -79,7 +74,7 @@ std::unique_ptr<VideoDecoder> ChromeosVideoDecoderFactory::Create( ...@@ -79,7 +74,7 @@ std::unique_ptr<VideoDecoder> ChromeosVideoDecoderFactory::Create(
return VideoDecoderPipeline::Create( return VideoDecoderPipeline::Create(
std::move(client_task_runner), std::move(frame_pool), std::move(client_task_runner), std::move(frame_pool),
std::move(frame_converter), std::move(media_log), std::move(frame_converter), std::move(media_log),
base::BindRepeating(&GetCreateVDFunctions)); base::BindRepeating(&GetCreateDecoderFunctions));
} }
} // namespace media } // namespace media
...@@ -55,6 +55,14 @@ base::Optional<Fourcc> PickRenderableFourcc( ...@@ -55,6 +55,14 @@ base::Optional<Fourcc> PickRenderableFourcc(
return base::nullopt; return base::nullopt;
} }
// Appends |new_status| to |parent_status| unless |parent_status| is kOk, in
// that case we cannot append, just forward |new_status| then.
Status AppendOrForwardStatus(Status parent_status, Status new_status) {
if (parent_status.is_ok())
return new_status;
return std::move(parent_status).AddCause(std::move(new_status));
}
} // namespace } // namespace
DecoderInterface::DecoderInterface( DecoderInterface::DecoderInterface(
...@@ -70,34 +78,33 @@ std::unique_ptr<VideoDecoder> VideoDecoderPipeline::Create( ...@@ -70,34 +78,33 @@ std::unique_ptr<VideoDecoder> VideoDecoderPipeline::Create(
std::unique_ptr<DmabufVideoFramePool> frame_pool, std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter, std::unique_ptr<VideoFrameConverter> frame_converter,
std::unique_ptr<MediaLog> /*media_log*/, std::unique_ptr<MediaLog> /*media_log*/,
GetCreateVDFunctionsCB get_create_vd_functions_cb) { GetCreateDecoderFunctionsCB get_create_decoder_functions_cb) {
if (!client_task_runner || !frame_pool || !frame_converter) { if (!client_task_runner || !frame_pool || !frame_converter) {
VLOGF(1) << "One of arguments is nullptr."; VLOGF(1) << "One of arguments is nullptr.";
return nullptr; return nullptr;
} }
if (get_create_vd_functions_cb.Run(nullptr).empty()) { if (get_create_decoder_functions_cb.Run().empty()) {
VLOGF(1) << "No available function to create video decoder."; VLOGF(1) << "No available function to create video decoder.";
return nullptr; return nullptr;
} }
return base::WrapUnique<VideoDecoder>(new VideoDecoderPipeline( return base::WrapUnique<VideoDecoder>(new VideoDecoderPipeline(
std::move(client_task_runner), std::move(frame_pool), std::move(client_task_runner), std::move(frame_pool),
std::move(frame_converter), std::move(get_create_vd_functions_cb))); std::move(frame_converter), std::move(get_create_decoder_functions_cb)));
} }
VideoDecoderPipeline::VideoDecoderPipeline( VideoDecoderPipeline::VideoDecoderPipeline(
scoped_refptr<base::SequencedTaskRunner> client_task_runner, scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool, std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter, std::unique_ptr<VideoFrameConverter> frame_converter,
GetCreateVDFunctionsCB get_create_vd_functions_cb) GetCreateDecoderFunctionsCB get_create_decoder_functions_cb)
: client_task_runner_(std::move(client_task_runner)), : client_task_runner_(std::move(client_task_runner)),
decoder_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner( decoder_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner(
{base::WithBaseSyncPrimitives(), base::TaskPriority::USER_VISIBLE}, {base::WithBaseSyncPrimitives(), base::TaskPriority::USER_VISIBLE},
base::SingleThreadTaskRunnerThreadMode::DEDICATED)), base::SingleThreadTaskRunnerThreadMode::DEDICATED)),
main_frame_pool_(std::move(frame_pool)), main_frame_pool_(std::move(frame_pool)),
frame_converter_(std::move(frame_converter)), 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_); DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DETACH_FROM_SEQUENCE(decoder_sequence_checker_); DETACH_FROM_SEQUENCE(decoder_sequence_checker_);
DCHECK(main_frame_pool_); DCHECK(main_frame_pool_);
...@@ -108,6 +115,8 @@ VideoDecoderPipeline::VideoDecoderPipeline( ...@@ -108,6 +115,8 @@ VideoDecoderPipeline::VideoDecoderPipeline(
client_weak_this_ = client_weak_this_factory_.GetWeakPtr(); client_weak_this_ = client_weak_this_factory_.GetWeakPtr();
decoder_weak_this_ = decoder_weak_this_factory_.GetWeakPtr(); decoder_weak_this_ = decoder_weak_this_factory_.GetWeakPtr();
remaining_create_decoder_functions_ = get_create_decoder_functions_cb.Run();
main_frame_pool_->set_parent_task_runner(decoder_task_runner_); main_frame_pool_->set_parent_task_runner(decoder_task_runner_);
frame_converter_->Initialize( frame_converter_->Initialize(
decoder_task_runner_, decoder_task_runner_,
...@@ -144,7 +153,7 @@ void VideoDecoderPipeline::DestroyTask() { ...@@ -144,7 +153,7 @@ void VideoDecoderPipeline::DestroyTask() {
frame_converter_.reset(); frame_converter_.reset();
decoder_.reset(); decoder_.reset();
used_create_vd_func_ = nullptr; remaining_create_decoder_functions_.clear();
delete this; delete this;
} }
...@@ -215,79 +224,76 @@ void VideoDecoderPipeline::Initialize(const VideoDecoderConfig& config, ...@@ -215,79 +224,76 @@ void VideoDecoderPipeline::Initialize(const VideoDecoderConfig& config,
void VideoDecoderPipeline::InitializeTask(const VideoDecoderConfig& config, void VideoDecoderPipeline::InitializeTask(const VideoDecoderConfig& config,
InitCB init_cb, InitCB init_cb,
const OutputCB& output_cb) { const OutputCB& output_cb) {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(!init_cb_); DCHECK(!init_cb_);
client_output_cb_ = std::move(output_cb); client_output_cb_ = std::move(output_cb);
init_cb_ = std::move(init_cb); init_cb_ = std::move(init_cb);
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs =
get_create_vd_functions_cb_.Run(used_create_vd_func_);
// Initialize() and correspondingly InitializeTask(), are called both on first
// initialization and on subsequent stream |config| changes, e.g. change of
// resolution. Subsequent initializations are marked by |decoder_| already
// existing.
if (!decoder_) { if (!decoder_) {
CreateAndInitializeVD(std::move(create_vd_funcs), config, CreateAndInitializeVD(config, Status());
StatusCode::kChromeOSVideoDecoderNoDecoders);
} else { } else {
decoder_->Initialize( decoder_->Initialize(
config, 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, base::BindOnce(&VideoDecoderPipeline::OnInitializeDone,
decoder_weak_this_, std::move(create_vd_funcs), config, decoder_weak_this_, config, Status()),
StatusCode::kChromeOSVideoDecoderNoDecoders),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded, base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded,
decoder_weak_this_)); decoder_weak_this_));
} }
} }
void VideoDecoderPipeline::CreateAndInitializeVD( void VideoDecoderPipeline::CreateAndInitializeVD(VideoDecoderConfig config,
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs, Status parent_error) {
VideoDecoderConfig config,
Status parent_error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(init_cb_); DCHECK(init_cb_);
DCHECK(!decoder_); DCHECK(!decoder_);
DCHECK(!used_create_vd_func_);
DVLOGF(3); DVLOGF(3);
if (create_vd_funcs.empty()) { if (remaining_create_decoder_functions_.empty()) {
DVLOGF(2) << "No available video decoder."; DVLOGF(2) << "No remaining video decoder create functions to try";
client_task_runner_->PostTask( client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(init_cb_), parent_error)); FROM_HERE,
base::BindOnce(
std::move(init_cb_),
AppendOrForwardStatus(
parent_error, StatusCode::kChromeOSVideoDecoderNoDecoders)));
return; return;
} }
used_create_vd_func_ = create_vd_funcs.front(); decoder_ = remaining_create_decoder_functions_.front()(decoder_task_runner_,
create_vd_funcs.pop(); decoder_weak_this_);
decoder_ = used_create_vd_func_(decoder_task_runner_, decoder_weak_this_); remaining_create_decoder_functions_.pop_front();
if (!decoder_) { if (!decoder_) {
DVLOGF(2) << "Failed to create VideoDecoder."; DVLOGF(2) << "|decoder_| creation failed, trying again with the next "
used_create_vd_func_ = nullptr; "available create function.";
return CreateAndInitializeVD( return CreateAndInitializeVD(
std::move(create_vd_funcs), config, config, AppendOrForwardStatus(parent_error,
std::move(parent_error).AddCause(StatusCode::kDecoderFailedCreation)); StatusCode::kDecoderFailedCreation));
} }
decoder_->Initialize( decoder_->Initialize(
config, config,
base::BindOnce(&VideoDecoderPipeline::OnInitializeDone, base::BindOnce(&VideoDecoderPipeline::OnInitializeDone,
decoder_weak_this_, std::move(create_vd_funcs), config, decoder_weak_this_, config, std::move(parent_error)),
std::move(parent_error)),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded, base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded,
decoder_weak_this_)); decoder_weak_this_));
} }
void VideoDecoderPipeline::OnInitializeDone( void VideoDecoderPipeline::OnInitializeDone(VideoDecoderConfig config,
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs, Status parent_error,
VideoDecoderConfig config, Status status) {
Status parent_error,
Status status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(init_cb_); DCHECK(init_cb_);
DVLOGF(4) << "Initialization status = " << status.code(); DVLOGF(4) << "Initialization status = " << status.code();
if (status.is_ok()) { if (status.is_ok()) {
DVLOGF(2) << "Initialize VD successfully."; DVLOGF(2) << "|decoder_| successfully initialized.";
// TODO(tmathmeyer) consider logging the causes of |parent_error| as they // TODO(tmathmeyer) consider logging the causes of |parent_error| as they
// might have infor about why other decoders failed. // might have infor about why other decoders failed.
client_task_runner_->PostTask( client_task_runner_->PostTask(
...@@ -295,11 +301,11 @@ void VideoDecoderPipeline::OnInitializeDone( ...@@ -295,11 +301,11 @@ void VideoDecoderPipeline::OnInitializeDone(
return; return;
} }
DVLOGF(3) << "Reset VD, try the next create function."; DVLOGF(3) << "|decoder_| initialization failed, trying again with the next "
"available create function.";
decoder_ = nullptr; decoder_ = nullptr;
used_create_vd_func_ = nullptr; CreateAndInitializeVD(config,
CreateAndInitializeVD(std::move(create_vd_funcs), config, AppendOrForwardStatus(parent_error, std::move(status)));
std::move(parent_error).AddCause(std::move(status)));
} }
void VideoDecoderPipeline::Reset(base::OnceClosure closure) { void VideoDecoderPipeline::Reset(base::OnceClosure closure) {
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include <memory> #include <memory>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
...@@ -127,19 +126,19 @@ class MEDIA_GPU_EXPORT DecoderInterface { ...@@ -127,19 +126,19 @@ class MEDIA_GPU_EXPORT DecoderInterface {
class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder, class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
public DecoderInterface::Client { public DecoderInterface::Client {
public: public:
// Function signature for creating VideoDecoder. using CreateDecoderFunction = std::unique_ptr<DecoderInterface> (*)(
using CreateVDFunc = std::unique_ptr<DecoderInterface> (*)(
scoped_refptr<base::SequencedTaskRunner>, scoped_refptr<base::SequencedTaskRunner>,
base::WeakPtr<DecoderInterface::Client>); base::WeakPtr<DecoderInterface::Client>);
using GetCreateVDFunctionsCB = using CreateDecoderFunctions = std::list<CreateDecoderFunction>;
base::RepeatingCallback<base::queue<CreateVDFunc>(CreateVDFunc)>; using GetCreateDecoderFunctionsCB =
base::RepeatingCallback<CreateDecoderFunctions()>;
static std::unique_ptr<VideoDecoder> Create( static std::unique_ptr<VideoDecoder> Create(
scoped_refptr<base::SequencedTaskRunner> client_task_runner, scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool, std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter, std::unique_ptr<VideoFrameConverter> frame_converter,
std::unique_ptr<MediaLog> media_log, std::unique_ptr<MediaLog> media_log,
GetCreateVDFunctionsCB get_create_vd_functions_cb); GetCreateDecoderFunctionsCB get_create_decoder_functions_cb);
~VideoDecoderPipeline() override; ~VideoDecoderPipeline() override;
...@@ -169,11 +168,13 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder, ...@@ -169,11 +168,13 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
const gfx::Rect& visible_rect) override; const gfx::Rect& visible_rect) override;
private: private:
friend class VideoDecoderPipelineTest;
VideoDecoderPipeline( VideoDecoderPipeline(
scoped_refptr<base::SequencedTaskRunner> client_task_runner, scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool, std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter, std::unique_ptr<VideoFrameConverter> frame_converter,
GetCreateVDFunctionsCB get_create_vd_functions_cb); GetCreateDecoderFunctionsCB get_create_decoder_functions_cb);
void Destroy() override; void Destroy() override;
void DestroyTask(); void DestroyTask();
...@@ -183,13 +184,10 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder, ...@@ -183,13 +184,10 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
void ResetTask(base::OnceClosure closure); void ResetTask(base::OnceClosure closure);
void DecodeTask(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb); void DecodeTask(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb);
void CreateAndInitializeVD(base::queue<CreateVDFunc> create_vd_funcs, void CreateAndInitializeVD(VideoDecoderConfig config, Status parent_error);
VideoDecoderConfig config, void OnInitializeDone(VideoDecoderConfig config,
Status parent_error);
void OnInitializeDone(base::queue<CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config,
Status parent_error, Status parent_error,
Status success); Status status);
void OnDecodeDone(bool eos_buffer, DecodeCB decode_cb, DecodeStatus status); void OnDecodeDone(bool eos_buffer, DecodeCB decode_cb, DecodeStatus status);
void OnResetDone(); void OnResetDone();
...@@ -240,14 +238,14 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder, ...@@ -240,14 +238,14 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
// |client_task_runner_|. // |client_task_runner_|.
std::unique_ptr<VideoFrameConverter> frame_converter_; std::unique_ptr<VideoFrameConverter> frame_converter_;
// The callback to get a list of function for creating DecoderInterface.
GetCreateVDFunctionsCB get_create_vd_functions_cb_;
// The current video decoder implementation. Valid after initialization is // The current video decoder implementation. Valid after initialization is
// successfully done. // successfully done.
std::unique_ptr<DecoderInterface> decoder_; std::unique_ptr<DecoderInterface> decoder_;
// The create function of |decoder_|. nullptr iff |decoder_| is nullptr.
CreateVDFunc used_create_vd_func_ = nullptr; // |remaining_create_decoder_functions_| holds all the potential video decoder
// creation functions. We try them all in the given order until one succeeds.
// Only used after initialization on |decoder_sequence_checker_|.
CreateDecoderFunctions remaining_create_decoder_functions_;
// Callback from the client. These callback are called on // Callback from the client. These callback are called on
// |client_task_runner_|. // |client_task_runner_|.
......
// Copyright 2020 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 "base/bind.h"
#include "base/bind_helpers.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/base/media_util.h"
#include "media/base/status.h"
#include "media/base/video_decoder_config.h"
#include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
#include "media/gpu/chromeos/mailbox_video_frame_converter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::test::RunClosure;
using ::testing::_;
using ::testing::TestWithParam;
namespace media {
MATCHER_P(MatchesStatusCode, status_code, "") {
// media::Status doesn't provide an operator==(...), we add here a simple one.
return arg.code() == status_code;
}
class MockVideoFramePool : public DmabufVideoFramePool {
public:
MockVideoFramePool() = default;
~MockVideoFramePool() override = default;
// DmabufVideoFramePool implementation.
MOCK_METHOD5(Initialize,
base::Optional<GpuBufferLayout>(const Fourcc&,
const gfx::Size&,
const gfx::Rect&,
const gfx::Size&,
size_t));
MOCK_METHOD0(GetFrame, scoped_refptr<VideoFrame>());
MOCK_METHOD0(IsExhausted, bool());
MOCK_METHOD1(NotifyWhenFrameAvailable, void(base::OnceClosure));
};
constexpr gfx::Size kCodedSize(48, 36);
class MockDecoder : public DecoderInterface {
public:
MockDecoder()
: DecoderInterface(base::ThreadTaskRunnerHandle::Get(),
base::WeakPtr<DecoderInterface::Client>(nullptr)) {}
~MockDecoder() override = default;
MOCK_METHOD3(Initialize,
void(const VideoDecoderConfig&, InitCB, const OutputCB&));
MOCK_METHOD2(Decode, void(scoped_refptr<DecoderBuffer>, DecodeCB));
MOCK_METHOD1(Reset, void(base::OnceClosure));
MOCK_METHOD0(ApplyResolutionChange, void());
};
struct DecoderPipelineTestParams {
VideoDecoderPipeline::CreateDecoderFunctions create_decoder_functions;
StatusCode status_code;
};
class VideoDecoderPipelineTest
: public testing::TestWithParam<DecoderPipelineTestParams> {
public:
VideoDecoderPipelineTest()
: config_(kCodecVP8,
VP8PROFILE_ANY,
VideoDecoderConfig::AlphaMode::kIsOpaque,
VideoColorSpace(),
kNoTransformation,
kCodedSize,
gfx::Rect(kCodedSize),
kCodedSize,
EmptyExtraData(),
EncryptionScheme::kUnencrypted),
pool_(new MockVideoFramePool),
converter_(new VideoFrameConverter),
decoder_(new VideoDecoderPipeline(
base::ThreadTaskRunnerHandle::Get(),
std::move(pool_),
std::move(converter_),
base::BindRepeating([]() {
// This callback needs to be configured in the individual tests.
return VideoDecoderPipeline::CreateDecoderFunctions();
}))) {}
~VideoDecoderPipelineTest() override = default;
void TearDown() override {
decoder_.release()->Destroy();
task_environment_.RunUntilIdle();
}
MOCK_METHOD1(OnInit, void(Status));
MOCK_METHOD1(OnOutput, void(scoped_refptr<VideoFrame>));
void SetCreateDecoderFunctions(
VideoDecoderPipeline::CreateDecoderFunctions functions) {
decoder_->remaining_create_decoder_functions_ = functions;
}
void InitializeDecoder() {
decoder_->Initialize(
config_, false /* low_delay */, nullptr /* cdm_context */,
base::BindOnce(&VideoDecoderPipelineTest::OnInit,
base::Unretained(this)),
base::BindRepeating(&VideoDecoderPipelineTest::OnOutput,
base::Unretained(this)),
base::DoNothing());
}
static std::unique_ptr<DecoderInterface> CreateNullMockDecoder(
scoped_refptr<base::SequencedTaskRunner> /* decoder_task_runner */,
base::WeakPtr<DecoderInterface::Client> /* client */) {
return nullptr;
}
// Creates a MockDecoder with an EXPECT_CALL on Initialize that returns ok.
static std::unique_ptr<DecoderInterface> CreateGoodMockDecoder(
scoped_refptr<base::SequencedTaskRunner> /* decoder_task_runner */,
base::WeakPtr<DecoderInterface::Client> /* client */) {
std::unique_ptr<MockDecoder> decoder(new MockDecoder());
EXPECT_CALL(*decoder, Initialize(_, _, _))
.WillOnce(::testing::WithArgs<1>([](VideoDecoder::InitCB init_cb) {
std::move(init_cb).Run(OkStatus());
}));
return std::move(decoder);
}
// Creates a MockDecoder with an EXPECT_CALL on Initialize that returns error.
static std::unique_ptr<DecoderInterface> CreateBadMockDecoder(
scoped_refptr<base::SequencedTaskRunner> /* decoder_task_runner */,
base::WeakPtr<DecoderInterface::Client> /* client */) {
std::unique_ptr<MockDecoder> decoder(new MockDecoder());
EXPECT_CALL(*decoder, Initialize(_, _, _))
.WillOnce(::testing::WithArgs<1>([](VideoDecoder::InitCB init_cb) {
std::move(init_cb).Run(StatusCode::kDecoderFailedInitialization);
}));
return std::move(decoder);
}
DecoderInterface* GetUnderlyingDecoder() { return decoder_->decoder_.get(); }
base::test::TaskEnvironment task_environment_;
const VideoDecoderConfig config_;
DecoderInterface* underlying_decoder_ptr_ = nullptr;
std::unique_ptr<MockVideoFramePool> pool_;
std::unique_ptr<VideoFrameConverter> converter_;
std::unique_ptr<VideoDecoderPipeline> decoder_;
};
// Verifies the status code for several typical CreateDecoderFunctions cases.
TEST_P(VideoDecoderPipelineTest, Initialize) {
SetCreateDecoderFunctions(GetParam().create_decoder_functions);
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*this, OnInit(MatchesStatusCode(GetParam().status_code)))
.WillOnce(RunClosure(quit_closure));
InitializeDecoder();
run_loop.Run();
EXPECT_EQ(GetParam().status_code == StatusCode::kOk,
!!GetUnderlyingDecoder());
}
const struct DecoderPipelineTestParams kDecoderPipelineTestParams[] = {
// An empty set of CreateDecoderFunctions.
{{}, StatusCode::kChromeOSVideoDecoderNoDecoders},
// Just one CreateDecoderFunctions that fails to Create() (i.e. returns a
// null Decoder)
{{&VideoDecoderPipelineTest::CreateNullMockDecoder},
StatusCode::kDecoderFailedCreation},
// Just one CreateDecoderFunctions that works fine, i.e. Create()s and
// Initialize()s correctly.
{{&VideoDecoderPipelineTest::CreateGoodMockDecoder}, StatusCode::kOk},
// One CreateDecoderFunctions that Create()s ok but fails to Initialize()
// correctly
{{&VideoDecoderPipelineTest::CreateBadMockDecoder},
StatusCode::kDecoderFailedInitialization},
// Two CreateDecoderFunctions, one that fails to Create() (i.e. returns a
// null Decoder), and one that works. The first error StatusCode is lost
// because VideoDecoderPipeline::OnInitializeDone() throws it away.
{{&VideoDecoderPipelineTest::CreateNullMockDecoder,
&VideoDecoderPipelineTest::CreateGoodMockDecoder},
StatusCode::kOk},
// Two CreateDecoderFunctions, one that Create()s ok but fails to
// Initialize(), and one that works. The first error StatusCode is lost
// because VideoDecoderPipeline::OnInitializeDone() throws it away.
{{&VideoDecoderPipelineTest::CreateBadMockDecoder,
&VideoDecoderPipelineTest::CreateGoodMockDecoder},
StatusCode::kOk},
// Two CreateDecoderFunctions, one that fails to Create() (i.e. returns a
// null Decoder), and one that fails to Initialize(). The first error
// StatusCode is the only one we can check here: a Status object is created
// with a "primary" StatusCode, archiving subsequent ones in a private
// member.
{{&VideoDecoderPipelineTest::CreateNullMockDecoder,
&VideoDecoderPipelineTest::CreateBadMockDecoder},
StatusCode::kDecoderFailedCreation},
// Previous one in reverse order.
{{&VideoDecoderPipelineTest::CreateBadMockDecoder,
&VideoDecoderPipelineTest::CreateNullMockDecoder},
StatusCode::kDecoderFailedInitialization},
{{&VideoDecoderPipelineTest::CreateBadMockDecoder,
&VideoDecoderPipelineTest::CreateBadMockDecoder,
&VideoDecoderPipelineTest::CreateGoodMockDecoder},
StatusCode::kOk},
};
INSTANTIATE_TEST_SUITE_P(All,
VideoDecoderPipelineTest,
testing::ValuesIn(kDecoderPipelineTestParams));
} // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment