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") {
"mailbox_video_frame_converter_unittest.cc",
"platform_video_frame_pool_unittest.cc",
"platform_video_frame_utils_unittest.cc",
"video_decoder_pipeline_unittest.cc",
]
}
......
......@@ -26,10 +26,9 @@ namespace media {
namespace {
// Get a list of the available functions for creating VideoDecoder.
base::queue<VideoDecoderPipeline::CreateVDFunc> GetCreateVDFunctions(
VideoDecoderPipeline::CreateVDFunc cur_create_vd_func) {
static constexpr VideoDecoderPipeline::CreateVDFunc kCreateVDFuncs[] = {
// Gets a list of the available functions for creating VideoDecoders.
VideoDecoderPipeline::CreateDecoderFunctions GetCreateDecoderFunctions() {
constexpr VideoDecoderPipeline::CreateDecoderFunction kCreateVDFuncs[] = {
#if BUILDFLAG(USE_VAAPI)
&VaapiVideoDecoder::Create,
#endif // BUILDFLAG(USE_VAAPI)
......@@ -39,12 +38,8 @@ base::queue<VideoDecoderPipeline::CreateVDFunc> GetCreateVDFunctions(
#endif // BUILDFLAG(USE_V4L2_CODEC)
};
base::queue<VideoDecoderPipeline::CreateVDFunc> ret;
for (const auto& func : kCreateVDFuncs) {
if (func != cur_create_vd_func)
ret.push(func);
}
return ret;
return VideoDecoderPipeline::CreateDecoderFunctions(
kCreateVDFuncs, kCreateVDFuncs + base::size(kCreateVDFuncs));
}
} // namespace
......@@ -79,7 +74,7 @@ std::unique_ptr<VideoDecoder> ChromeosVideoDecoderFactory::Create(
return VideoDecoderPipeline::Create(
std::move(client_task_runner), std::move(frame_pool),
std::move(frame_converter), std::move(media_log),
base::BindRepeating(&GetCreateVDFunctions));
base::BindRepeating(&GetCreateDecoderFunctions));
}
} // namespace media
......@@ -55,6 +55,14 @@ base::Optional<Fourcc> PickRenderableFourcc(
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
DecoderInterface::DecoderInterface(
......@@ -70,34 +78,33 @@ std::unique_ptr<VideoDecoder> VideoDecoderPipeline::Create(
std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter,
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) {
VLOGF(1) << "One of arguments is 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.";
return nullptr;
}
return base::WrapUnique<VideoDecoder>(new VideoDecoderPipeline(
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(
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool,
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)),
decoder_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner(
{base::WithBaseSyncPrimitives(), base::TaskPriority::USER_VISIBLE},
base::SingleThreadTaskRunnerThreadMode::DEDICATED)),
main_frame_pool_(std::move(frame_pool)),
frame_converter_(std::move(frame_converter)),
get_create_vd_functions_cb_(std::move(get_create_vd_functions_cb)) {
frame_converter_(std::move(frame_converter)) {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
DETACH_FROM_SEQUENCE(decoder_sequence_checker_);
DCHECK(main_frame_pool_);
......@@ -108,6 +115,8 @@ VideoDecoderPipeline::VideoDecoderPipeline(
client_weak_this_ = client_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_);
frame_converter_->Initialize(
decoder_task_runner_,
......@@ -144,7 +153,7 @@ void VideoDecoderPipeline::DestroyTask() {
frame_converter_.reset();
decoder_.reset();
used_create_vd_func_ = nullptr;
remaining_create_decoder_functions_.clear();
delete this;
}
......@@ -215,71 +224,68 @@ void VideoDecoderPipeline::Initialize(const VideoDecoderConfig& config,
void VideoDecoderPipeline::InitializeTask(const VideoDecoderConfig& config,
InitCB init_cb,
const OutputCB& output_cb) {
DVLOGF(3);
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_);
// 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_) {
CreateAndInitializeVD(std::move(create_vd_funcs), config,
StatusCode::kChromeOSVideoDecoderNoDecoders);
CreateAndInitializeVD(config, Status());
} 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,
StatusCode::kChromeOSVideoDecoderNoDecoders),
decoder_weak_this_, config, Status()),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded,
decoder_weak_this_));
}
}
void VideoDecoderPipeline::CreateAndInitializeVD(
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config,
void VideoDecoderPipeline::CreateAndInitializeVD(VideoDecoderConfig config,
Status parent_error) {
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.";
if (remaining_create_decoder_functions_.empty()) {
DVLOGF(2) << "No remaining video decoder create functions to try";
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;
}
used_create_vd_func_ = create_vd_funcs.front();
create_vd_funcs.pop();
decoder_ = used_create_vd_func_(decoder_task_runner_, decoder_weak_this_);
decoder_ = remaining_create_decoder_functions_.front()(decoder_task_runner_,
decoder_weak_this_);
remaining_create_decoder_functions_.pop_front();
if (!decoder_) {
DVLOGF(2) << "Failed to create VideoDecoder.";
used_create_vd_func_ = nullptr;
DVLOGF(2) << "|decoder_| creation failed, trying again with the next "
"available create function.";
return CreateAndInitializeVD(
std::move(create_vd_funcs), config,
std::move(parent_error).AddCause(StatusCode::kDecoderFailedCreation));
config, AppendOrForwardStatus(parent_error,
StatusCode::kDecoderFailedCreation));
}
decoder_->Initialize(
config,
base::BindOnce(&VideoDecoderPipeline::OnInitializeDone,
decoder_weak_this_, std::move(create_vd_funcs), config,
std::move(parent_error)),
decoder_weak_this_, config, std::move(parent_error)),
base::BindRepeating(&VideoDecoderPipeline::OnFrameDecoded,
decoder_weak_this_));
}
void VideoDecoderPipeline::OnInitializeDone(
base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config,
void VideoDecoderPipeline::OnInitializeDone(VideoDecoderConfig config,
Status parent_error,
Status status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
......@@ -287,7 +293,7 @@ void VideoDecoderPipeline::OnInitializeDone(
DVLOGF(4) << "Initialization status = " << status.code();
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
// might have infor about why other decoders failed.
client_task_runner_->PostTask(
......@@ -295,11 +301,11 @@ void VideoDecoderPipeline::OnInitializeDone(
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;
used_create_vd_func_ = nullptr;
CreateAndInitializeVD(std::move(create_vd_funcs), config,
std::move(parent_error).AddCause(std::move(status)));
CreateAndInitializeVD(config,
AppendOrForwardStatus(parent_error, std::move(status)));
}
void VideoDecoderPipeline::Reset(base::OnceClosure closure) {
......
......@@ -8,7 +8,6 @@
#include <memory>
#include "base/callback_forward.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
......@@ -127,19 +126,19 @@ class MEDIA_GPU_EXPORT DecoderInterface {
class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
public DecoderInterface::Client {
public:
// Function signature for creating VideoDecoder.
using CreateVDFunc = std::unique_ptr<DecoderInterface> (*)(
using CreateDecoderFunction = std::unique_ptr<DecoderInterface> (*)(
scoped_refptr<base::SequencedTaskRunner>,
base::WeakPtr<DecoderInterface::Client>);
using GetCreateVDFunctionsCB =
base::RepeatingCallback<base::queue<CreateVDFunc>(CreateVDFunc)>;
using CreateDecoderFunctions = std::list<CreateDecoderFunction>;
using GetCreateDecoderFunctionsCB =
base::RepeatingCallback<CreateDecoderFunctions()>;
static std::unique_ptr<VideoDecoder> Create(
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter,
std::unique_ptr<MediaLog> media_log,
GetCreateVDFunctionsCB get_create_vd_functions_cb);
GetCreateDecoderFunctionsCB get_create_decoder_functions_cb);
~VideoDecoderPipeline() override;
......@@ -169,11 +168,13 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
const gfx::Rect& visible_rect) override;
private:
friend class VideoDecoderPipelineTest;
VideoDecoderPipeline(
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
std::unique_ptr<DmabufVideoFramePool> frame_pool,
std::unique_ptr<VideoFrameConverter> frame_converter,
GetCreateVDFunctionsCB get_create_vd_functions_cb);
GetCreateDecoderFunctionsCB get_create_decoder_functions_cb);
void Destroy() override;
void DestroyTask();
......@@ -183,13 +184,10 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
void ResetTask(base::OnceClosure closure);
void DecodeTask(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb);
void CreateAndInitializeVD(base::queue<CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config,
Status parent_error);
void OnInitializeDone(base::queue<CreateVDFunc> create_vd_funcs,
VideoDecoderConfig config,
void CreateAndInitializeVD(VideoDecoderConfig config, Status parent_error);
void OnInitializeDone(VideoDecoderConfig config,
Status parent_error,
Status success);
Status status);
void OnDecodeDone(bool eos_buffer, DecodeCB decode_cb, DecodeStatus status);
void OnResetDone();
......@@ -240,14 +238,14 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
// |client_task_runner_|.
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
// successfully done.
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
// |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