#include "media/gpu/chromeos/video_decoder_pipeline.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "media/base/cdm_context.h"
#include "media/base/media_util.h"
#include "media/base/mock_filters.h"
#include "media/base/mock_media_log.h"
#include "media/base/status.h"
#include "media/base/video_decoder_config.h"
#include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libdrm/src/include/drm/drm_fourcc.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chromeos/components/cdm_factory_daemon/chromeos_cdm_context.h"
#endif
RunClosure;
_;
ByMove;
InSequence;
Return;
StrictMock;
TestWithParam;
namespace media {
PixelLayoutCandidate;
MATCHER_P(MatchesStatusCode, status_code, "") { … }
MATCHER_P(MatchesDecoderBuffer, buffer, "") { … }
class MockVideoFramePool : public DmabufVideoFramePool { … };
class MockDecoder : public VideoDecoderMixin { … };
#if BUILDFLAG(IS_CHROMEOS_ASH)
constexpr uint8_t kEncryptedData[] = {1, 8, 9};
constexpr uint8_t kTranscryptedData[] = {9, 2, 4};
constexpr uint64_t kFakeSecureHandle = 75;
class MockChromeOsCdmContext : public chromeos::ChromeOsCdmContext {
public:
MockChromeOsCdmContext() : chromeos::ChromeOsCdmContext() {}
~MockChromeOsCdmContext() override = default;
MOCK_METHOD3(GetHwKeyData,
void(const DecryptConfig*,
const std::vector<uint8_t>&,
chromeos::ChromeOsCdmContext::GetHwKeyDataCB));
MOCK_METHOD1(GetHwConfigData,
void(chromeos::ChromeOsCdmContext::GetHwConfigDataCB));
MOCK_METHOD1(GetScreenResolutions,
void(chromeos::ChromeOsCdmContext::GetScreenResolutionsCB));
MOCK_METHOD0(GetCdmContextRef, std::unique_ptr<CdmContextRef>());
MOCK_CONST_METHOD0(UsingArcCdm, bool());
MOCK_CONST_METHOD0(IsRemoteCdm, bool());
MOCK_METHOD2(AllocateSecureBuffer,
void(uint32_t,
chromeos::ChromeOsCdmContext::AllocateSecureBufferCB));
MOCK_METHOD4(ParseEncryptedSliceHeader,
void(uint64_t,
uint32_t,
const std::vector<uint8_t>&,
ParseEncryptedSliceHeaderCB));
};
class FakeCdmContextRef : public CdmContextRef {
public:
FakeCdmContextRef(CdmContext* cdm_context) : cdm_context_(cdm_context) {}
~FakeCdmContextRef() override = default;
CdmContext* GetCdmContext() override { return cdm_context_; }
private:
raw_ptr<CdmContext> cdm_context_;
};
#endif
class MockImageProcessor : public ImageProcessor { … };
struct DecoderPipelineTestParams { … };
constexpr gfx::Size kMinSupportedResolution(64, 64);
constexpr gfx::Size kMaxSupportedResolution(2048, 1088);
constexpr gfx::Size kCodedSize(128, 128);
static_assert …;
class VideoDecoderPipelineTest
: public testing::TestWithParam<DecoderPipelineTestParams> { … };
TEST_P(VideoDecoderPipelineTest, Initialize) { … }
const struct DecoderPipelineTestParams kDecoderPipelineTestParams[] = …;
INSTANTIATE_TEST_SUITE_P(…);
TEST_F(VideoDecoderPipelineTest, InitializeFailsDueToNotSupportedConfig) { … }
TEST_F(VideoDecoderPipelineTest, Reset) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(VideoDecoderPipelineTest, TranscryptThenEos) {
InitializeForTranscrypt();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([this](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kSuccess, transcrypted_buffer_);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(transcrypted_buffer_, _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kOk)));
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&decryptor_);
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(this);
scoped_refptr<DecoderBuffer> eos_buffer = DecoderBuffer::CreateEOSBuffer();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(eos_buffer, _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kOk)));
}
decoder_->Decode(eos_buffer,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, TranscryptReset) {
InitializeForTranscrypt();
scoped_refptr<DecoderBuffer> encrypted_buffer2 =
DecoderBuffer::CopyFrom(base::span(kEncryptedData).subspan(1));
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.Times(1);
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
decoder_->Decode(encrypted_buffer2,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
decoder_->Decode(encrypted_buffer2,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(&decryptor_);
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Reset(_))
.WillOnce([](base::OnceClosure closure) { std::move(closure).Run(); });
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kAborted)))
.Times(3);
EXPECT_CALL(*this, OnResetDone()).Times(1);
}
decoder_->Reset(base::BindOnce(&VideoDecoderPipelineTest::OnResetDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, TranscryptDecodeDuringReset) {
InitializeForTranscrypt();
Decryptor::DecryptCB saved_decrypt_cb;
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([&saved_decrypt_cb](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
saved_decrypt_cb =
base::BindPostTaskToCurrentDefault(std::move(decrypt_cb));
});
}
base::OnceClosure saved_reset_cb;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()), Reset(_))
.WillOnce([&saved_reset_cb](base::OnceClosure closure) {
saved_reset_cb = base::BindPostTaskToCurrentDefault(std::move(closure));
});
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
decoder_->Reset(base::BindOnce(&VideoDecoderPipelineTest::OnResetDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(&decryptor_);
ASSERT_TRUE(saved_decrypt_cb);
ASSERT_TRUE(saved_reset_cb);
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(_, _))
.Times(0);
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kAborted)))
.Times(1);
std::move(saved_decrypt_cb).Run(Decryptor::kSuccess, transcrypted_buffer_);
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*this, OnResetDone()).Times(1);
std::move(saved_reset_cb).Run();
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, TranscryptKeyAddedDuringTranscrypt) {
InitializeForTranscrypt();
Decryptor::DecryptCB saved_decrypt_cb;
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([&saved_decrypt_cb](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
saved_decrypt_cb =
base::BindPostTaskToCurrentDefault(std::move(decrypt_cb));
});
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(&decryptor_);
event_callbacks_.Notify(CdmContext::Event::kHasAdditionalUsableKey);
task_environment_.RunUntilIdle();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([this](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kSuccess, transcrypted_buffer_);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(transcrypted_buffer_, _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kOk)));
}
EXPECT_CALL(*this, OnWaiting(_)).Times(0);
std::move(saved_decrypt_cb).Run(Decryptor::kNoKey, nullptr);
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, RetryDoesntReattachSecureBuffer) {
InitializeForTranscrypt();
Decryptor::DecryptCB saved_decrypt_cb;
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce([](scoped_refptr<DecoderBuffer>& buffer) {
buffer->WritableSideData().secure_handle = kFakeSecureHandle;
return CroStatus::Codes::kOk;
});
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([&saved_decrypt_cb](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
saved_decrypt_cb =
base::BindPostTaskToCurrentDefault(std::move(decrypt_cb));
});
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(&decryptor_);
event_callbacks_.Notify(CdmContext::Event::kHasAdditionalUsableKey);
task_environment_.RunUntilIdle();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(_))
.Times(0);
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([this](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kSuccess, transcrypted_buffer_);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(transcrypted_buffer_, _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
ReleaseSecureBuffer(kFakeSecureHandle))
.Times(1);
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kOk)));
}
EXPECT_CALL(*this, OnWaiting(_)).Times(0);
std::move(saved_decrypt_cb).Run(Decryptor::kNoKey, nullptr);
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, TranscryptNoKeyWaitRetry) {
InitializeForTranscrypt();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kNoKey, nullptr);
});
EXPECT_CALL(*this, OnWaiting(WaitingReason::kNoDecryptionKey)).Times(1);
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(&decryptor_);
testing::Mock::VerifyAndClearExpectations(this);
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([this](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kSuccess, transcrypted_buffer_);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(transcrypted_buffer_, _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kOk)));
}
event_callbacks_.Notify(CdmContext::Event::kHasAdditionalUsableKey);
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, TranscryptError) {
InitializeForTranscrypt();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo, encrypted_buffer_, _))
.WillOnce([](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kError, nullptr);
});
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kFailed)));
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
TEST_F(VideoDecoderPipelineTest, SecureBufferFailure) {
InitializeForTranscrypt();
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(encrypted_buffer_))
.WillOnce(Return(CroStatus::Codes::kUnableToAllocateSecureBuffer));
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kFailed)));
}
decoder_->Decode(encrypted_buffer_,
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
#if BUILDFLAG(USE_V4L2_CODEC)
TEST_F(VideoDecoderPipelineTest, SplitVp9Superframe) {
InitializeForTranscrypt(true);
constexpr uint8_t kEncryptedSuperframe[] = {
1,
2,
3,
4,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
5,
6,
7,
8,
9,
10,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
0xc1,
0x14,
0x16,
0xc1,
};
constexpr uint8_t kEncryptedFrame0[] = {
1,
2,
3,
4,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
};
constexpr uint8_t kEncryptedFrame1[] = {
5,
6,
7,
8,
9,
10,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
};
scoped_refptr<DecoderBuffer> superframe_buffer =
DecoderBuffer::CopyFrom(kEncryptedSuperframe);
superframe_buffer->set_decrypt_config(DecryptConfig::CreateCencConfig(
"fakekey", std::string(16, '0'),
{SubsampleEntry(4, 16), SubsampleEntry(6, 16), SubsampleEntry(4, 0)}));
std::string iv(16, '0');
scoped_refptr<DecoderBuffer> frame0_buffer =
DecoderBuffer::CopyFrom(kEncryptedFrame0);
frame0_buffer->set_decrypt_config(
DecryptConfig::CreateCencConfig("fakekey", iv, {SubsampleEntry(4, 16)}));
scoped_refptr<DecoderBuffer> frame1_buffer =
DecoderBuffer::CopyFrom(kEncryptedFrame1);
iv[15]++;
frame1_buffer->set_decrypt_config(
DecryptConfig::CreateCencConfig("fakekey", iv, {SubsampleEntry(6, 16)}));
{
InSequence sequence;
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(MatchesDecoderBuffer(frame0_buffer)))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo,
MatchesDecoderBuffer(frame0_buffer), _))
.WillOnce([&frame0_buffer](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kSuccess, frame0_buffer);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(MatchesDecoderBuffer(frame0_buffer), _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
AttachSecureBuffer(MatchesDecoderBuffer(frame1_buffer)))
.WillOnce(Return(CroStatus::Codes::kOk));
EXPECT_CALL(decryptor_, Decrypt(Decryptor::kVideo,
MatchesDecoderBuffer(frame1_buffer), _))
.WillOnce([&frame1_buffer](Decryptor::StreamType stream_type,
scoped_refptr<DecoderBuffer> encrypted,
Decryptor::DecryptCB decrypt_cb) {
std::move(decrypt_cb).Run(Decryptor::kSuccess, frame1_buffer);
});
EXPECT_CALL(*reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()),
Decode(MatchesDecoderBuffer(frame1_buffer), _))
.WillOnce([](scoped_refptr<DecoderBuffer> transcrypted,
VideoDecoderMixin::DecodeCB decode_cb) {
std::move(decode_cb).Run(DecoderStatus::Codes::kOk);
});
EXPECT_CALL(*this,
OnDecodeDone(MatchesStatusCode(DecoderStatus::Codes::kOk)));
}
decoder_->Decode(std::move(superframe_buffer),
base::BindOnce(&VideoDecoderPipelineTest::OnDecodeDone,
base::Unretained(this)));
task_environment_.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&decryptor_);
testing::Mock::VerifyAndClearExpectations(
reinterpret_cast<MockDecoder*>(GetUnderlyingDecoder()));
testing::Mock::VerifyAndClearExpectations(this);
}
#endif
#endif
TEST_F(VideoDecoderPipelineTest, PickDecoderOutputFormat) { … }
#if BUILDFLAG(USE_VAAPI) && !BUILDFLAG(IS_LINUX) && \
!BUILDFLAG(IS_CHROMEOS_LACROS)
TEST_F(VideoDecoderPipelineTest, PickDecoderOutputFormatLinearModifier) {
constexpr gfx::Size kSize(320, 240);
constexpr gfx::Rect kVisibleRect(320, 240);
constexpr size_t kNumCodecReferenceFrames = 4u;
const Fourcc kFourcc(Fourcc::NV12);
auto image_processor =
std::make_unique<MockImageProcessor>(GetDecoderTaskRunner());
ImageProcessorBackend::PortConfig port_config(
Fourcc(Fourcc::NV12), gfx::Size(320, 240), {}, gfx::Rect(320, 240), {});
EXPECT_CALL(*image_processor, input_config())
.WillRepeatedly(testing::ReturnRef(port_config));
EXPECT_CALL(*image_processor, output_config())
.WillRepeatedly(testing::ReturnRef(port_config));
base::MockCallback<VideoDecoderPipeline::CreateImageProcessorCBForTesting>
image_processor_cb;
EXPECT_CALL(image_processor_cb, Run(_, _, kSize, _))
.WillOnce(Return(testing::ByMove(std::move(image_processor))));
SetCreateImageProcessorCBForTesting(image_processor_cb.Get());
GpuBufferLayout gpu_buffer_layout = *GpuBufferLayout::Create(
kFourcc, kSize,
std::vector<ColorPlaneLayout>(
VideoFrame::NumPlanes(kFourcc.ToVideoPixelFormat())),
DRM_FORMAT_MOD_LINEAR);
EXPECT_CALL(*pool_, Initialize(_, _, _, _, _, _, _))
.WillRepeatedly(Return(gpu_buffer_layout));
PixelLayoutCandidate candidate{Fourcc(Fourcc::NV12), kSize,
~DRM_FORMAT_MOD_LINEAR};
auto status_or_chosen_candidate = decoder_->PickDecoderOutputFormat(
{candidate}, kVisibleRect,
kVisibleRect.size(),
std::nullopt,
kNumCodecReferenceFrames,
false, false, std::nullopt);
EXPECT_TRUE(status_or_chosen_candidate.has_value());
EXPECT_TRUE(DecoderHasImageProcessor());
DetachDecoderSequenceChecker();
}
TEST_F(VideoDecoderPipelineTest, PickDecoderOutputFormatUnsupportedModifier) {
constexpr gfx::Size kSize(320, 240);
constexpr gfx::Rect kVisibleRect(320, 240);
constexpr size_t kNumCodecReferenceFrames = 4u;
const Fourcc kFourcc(Fourcc::NV12);
GpuBufferLayout gpu_buffer_layout = *GpuBufferLayout::Create(
kFourcc, kSize,
std::vector<ColorPlaneLayout>(
VideoFrame::NumPlanes(kFourcc.ToVideoPixelFormat())),
~DRM_FORMAT_MOD_LINEAR);
EXPECT_CALL(*pool_, Initialize(_, _, _, _, _, _, _))
.WillRepeatedly(Return(gpu_buffer_layout));
constexpr uint64_t modifier = ~DRM_FORMAT_MOD_LINEAR + 1;
PixelLayoutCandidate candidate{Fourcc(Fourcc::NV12), kSize, modifier};
auto status_or_chosen_candidate = decoder_->PickDecoderOutputFormat(
{candidate}, kVisibleRect,
kVisibleRect.size(),
std::nullopt,
kNumCodecReferenceFrames,
false, false, std::nullopt);
EXPECT_FALSE(status_or_chosen_candidate.has_value());
EXPECT_FALSE(DecoderHasImageProcessor());
DetachDecoderSequenceChecker();
}
#endif
TEST_F(VideoDecoderPipelineTest, RebuildFramePoolsOnStateLost) { … }
}