blob: 46d9fb9aee80abed11a391d30788d3df4a65f652 [file] [log] [blame]
// 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 <vector>
#include "media/base/demuxer_stream.h"
#include "media/base/media_util.h"
#include "media/base/mock_filters.h"
#include "media/base/status.h"
#include "media/base/test_helpers.h"
#include "media/base/video_decoder.h"
#include "media/filters/decoder_stream.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/modules/webcodecs/decoder_selector.h"
using ::testing::_;
using ::testing::IsNull;
using ::testing::StrictMock;
namespace blink {
namespace {
enum DecoderCapability {
kFail,
kSucceed,
};
const char kNoDecoder[] = "";
const char kDecoder1[] = "Decoder1";
const char kDecoder2[] = "Decoder2";
// Specializations for the AUDIO version of the test.
class AudioDecoderSelectorTestParam {
public:
static constexpr media::DemuxerStream::Type kStreamType =
media::DemuxerStream::AUDIO;
using MockDecoderSelector = DecoderSelector<media::DemuxerStream::AUDIO>;
using MockDecoder = media::MockAudioDecoder;
using Output = media::AudioBuffer;
static media::AudioDecoderConfig CreateConfig() {
return media::TestAudioConfig::Normal();
}
// Create a config that won't match the return of CreateConfig().
static media::AudioDecoderConfig CreateAlternateConfig() {
return media::TestAudioConfig::NormalEncrypted();
}
// Decoder::Initialize() takes different parameters depending on the type.
static void ExpectInitialize(MockDecoder* decoder,
DecoderCapability capability,
media::AudioDecoderConfig expected_config) {
EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _))
.WillRepeatedly([capability, expected_config](
const media::AudioDecoderConfig& config,
media::CdmContext*,
media::AudioDecoder::InitCB& init_cb,
const media::AudioDecoder::OutputCB&,
const media::WaitingCB&) {
EXPECT_TRUE(config.Matches(expected_config));
std::move(init_cb).Run(capability == kSucceed
? media::OkStatus()
: media::StatusCode::kCodeOnlyForTesting);
});
}
};
// Specializations for the VIDEO version of the test.
class VideoDecoderSelectorTestParam {
public:
static constexpr media::DemuxerStream::Type kStreamType =
media::DemuxerStream::VIDEO;
using MockDecoderSelector = DecoderSelector<media::DemuxerStream::VIDEO>;
using MockDecoder = media::MockVideoDecoder;
using Output = media::VideoFrame;
static media::VideoDecoderConfig CreateConfig() {
return media::TestVideoConfig::Normal();
}
// Create a config that won't match the return of CreateConfig().
static media::VideoDecoderConfig CreateAlternateConfig() {
return media::TestVideoConfig::LargeEncrypted();
}
static void ExpectInitialize(MockDecoder* decoder,
DecoderCapability capability,
media::VideoDecoderConfig expected_config) {
EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _, _))
.WillRepeatedly([capability, expected_config](
const media::VideoDecoderConfig& config,
bool low_delay, media::CdmContext*,
media::VideoDecoder::InitCB& init_cb,
const media::VideoDecoder::OutputCB&,
const media::WaitingCB&) {
EXPECT_TRUE(config.Matches(expected_config));
std::move(init_cb).Run(capability == kSucceed
? media::OkStatus()
: media::StatusCode::kCodeOnlyForTesting);
});
}
};
// Allocate storage for the member variables.
constexpr media::DemuxerStream::Type AudioDecoderSelectorTestParam::kStreamType;
constexpr media::DemuxerStream::Type VideoDecoderSelectorTestParam::kStreamType;
} // namespace
// Note: The parameter is called TypeParam in the test cases regardless of what
// we call it here. It's been named the same for convenience.
// Note: The test fixtures inherit from this class. Inside the test cases the
// test fixture class is called TestFixture.
template <typename TypeParam>
class WebCodecsDecoderSelectorTest : public ::testing::Test {
public:
// Convenience aliases.
using Self = WebCodecsDecoderSelectorTest<TypeParam>;
using Decoder = typename TypeParam::MockDecoderSelector::Decoder;
using DecoderConfig = typename TypeParam::MockDecoderSelector::DecoderConfig;
using MockDecoder = typename TypeParam::MockDecoder;
using Output = typename TypeParam::Output;
WebCodecsDecoderSelectorTest() { CreateDecoderSelector(); }
void OnOutput(scoped_refptr<Output> output) { NOTREACHED(); }
MOCK_METHOD1_T(OnDecoderSelected, void(std::string));
void OnDecoderSelectedThunk(std::unique_ptr<Decoder> decoder) {
// Report only the name of the decoder, since that's what the tests care
// about. The decoder will be destructed immediately.
OnDecoderSelected(decoder ? decoder->GetDisplayName() : kNoDecoder);
}
void AddMockDecoder(const std::string& decoder_name,
DecoderCapability capability) {
// Actual decoders are created in CreateDecoders(), which may be called
// multiple times by the DecoderSelector.
mock_decoders_to_create_.emplace_back(decoder_name, capability);
}
std::vector<std::unique_ptr<Decoder>> CreateDecoders() {
std::vector<std::unique_ptr<Decoder>> decoders;
for (const auto& info : mock_decoders_to_create_) {
std::unique_ptr<StrictMock<MockDecoder>> decoder =
std::make_unique<StrictMock<MockDecoder>>(
/*is_platform_decoder=*/false, /*supports_decryption=*/true,
info.first);
TypeParam::ExpectInitialize(decoder.get(), info.second,
last_set_decoder_config_);
decoders.push_back(std::move(decoder));
}
return decoders;
}
void CreateDecoderSelector() {
decoder_selector_ =
std::make_unique<DecoderSelector<TypeParam::kStreamType>>(
scheduler::GetSingleThreadTaskRunnerForTesting(),
base::BindRepeating(&Self::CreateDecoders, base::Unretained(this)),
base::BindRepeating(&Self::OnOutput, base::Unretained(this)));
}
void SelectDecoder(DecoderConfig config = TypeParam::CreateConfig()) {
last_set_decoder_config_ = config;
decoder_selector_->SelectDecoder(
config,
base::BindOnce(&Self::OnDecoderSelectedThunk, base::Unretained(this)));
RunUntilIdle();
}
void RunUntilIdle() { platform_->RunUntilIdle(); }
ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
media::NullMediaLog media_log_;
DecoderConfig last_set_decoder_config_;
std::unique_ptr<DecoderSelector<TypeParam::kStreamType>> decoder_selector_;
std::vector<std::pair<std::string, DecoderCapability>>
mock_decoders_to_create_;
private:
DISALLOW_COPY_AND_ASSIGN(WebCodecsDecoderSelectorTest);
};
using WebCodecsDecoderSelectorTestParams =
::testing::Types<AudioDecoderSelectorTestParam,
VideoDecoderSelectorTestParam>;
TYPED_TEST_SUITE(WebCodecsDecoderSelectorTest,
WebCodecsDecoderSelectorTestParams);
TYPED_TEST(WebCodecsDecoderSelectorTest, NoDecoders) {
EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder));
this->SelectDecoder();
}
TYPED_TEST(WebCodecsDecoderSelectorTest, OneDecoder) {
this->AddMockDecoder(kDecoder1, kSucceed);
EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
this->SelectDecoder();
}
TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders) {
this->AddMockDecoder(kDecoder1, kFail);
this->AddMockDecoder(kDecoder2, kSucceed);
EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
this->SelectDecoder();
}
TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders_SelectAgain) {
this->AddMockDecoder(kDecoder1, kSucceed);
this->AddMockDecoder(kDecoder2, kSucceed);
EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
this->SelectDecoder();
// Selecting again should give (a new instance of) the same decoder.
EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
this->SelectDecoder();
}
TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders_NewConfigSelectAgain) {
this->AddMockDecoder(kDecoder1, kSucceed);
this->AddMockDecoder(kDecoder2, kSucceed);
EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
this->SelectDecoder(TypeParam::CreateConfig());
// Selecting again should give (a new instance of) the same decoder.
EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
// Select again with a different config. Expected config verified during
// CreateDecoders() the SelectDecoder() call.
this->SelectDecoder(TypeParam::CreateAlternateConfig());
}
} // namespace blink