diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn index ed5dfa955c4b..f8a05a2d2ca3 100644 --- a/starboard/nplb/BUILD.gn +++ b/starboard/nplb/BUILD.gn @@ -110,6 +110,7 @@ target(gtest_target_type, "nplb") { "microphone_is_sample_rate_supported_test.cc", "microphone_open_test.cc", "microphone_read_test.cc", + "multiple_player_test.cc", "murmurhash2_test.cc", "mutex_acquire_test.cc", "mutex_acquire_try_test.cc", diff --git a/starboard/nplb/multiple_player_test.cc b/starboard/nplb/multiple_player_test.cc new file mode 100644 index 000000000000..049f4b7a4557 --- /dev/null +++ b/starboard/nplb/multiple_player_test.cc @@ -0,0 +1,221 @@ +// Copyright 2023 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "starboard/common/string.h" +#include "starboard/nplb/player_test_fixture.h" +#include "starboard/nplb/thread_helpers.h" +#include "starboard/testing/fake_graphics_context_provider.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +using ::starboard::testing::FakeGraphicsContextProvider; +using ::testing::ValuesIn; + +typedef SbPlayerTestFixture::GroupedSamples GroupedSamples; +typedef std::vector SbPlayerMultiplePlayerTestConfig; + +std::string GetTestConfigName(const SbPlayerTestConfig& config) { + std::string name; + + if (std::get<0>(config)) { + name += FormatString("audio_%s_", std::get<0>(config)); + } + if (std::get<1>(config)) { + name += FormatString("video_%s_", std::get<1>(config)); + } + return name; +} + +std::map TestConfigsToMap( + const SbPlayerMultiplePlayerTestConfig& test_configs) { + std::map map; + for (const auto& test_config : test_configs) { + ++map[test_config]; + } + return map; +} + +std::string GetMultipleSbPlayerTestConfigName( + ::testing::TestParamInfo info) { + const auto test_configs = info.param; + std::string name; + + const auto& map = TestConfigsToMap(test_configs); + for (const auto& it : map) { + name += GetTestConfigName(it.first); + name += "num_"; + name += std::to_string(it.second); + } + name += + (std::get<2>(test_configs.front()) == kSbPlayerOutputModeDecodeToTexture + ? "_DecodeToTexture" + : "_Punchout"); + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '(', '_'); + std::replace(name.begin(), name.end(), ')', '_'); + return name; +} + +std::vector GetSupportedTestConfigs() { + std::vector configs_to_return; + const int num_of_players = 5; + + std::vector supported_configs = + GetSupportedSbPlayerTestConfigs(); + for (auto& config : supported_configs) { + SbPlayerMultiplePlayerTestConfig multiplayer_test_config(num_of_players, + config); + configs_to_return.emplace_back(multiplayer_test_config); + } + + return configs_to_return; +} + +void SeekAndDestroy(SbPlayerTestFixture* player_fixture) { + ASSERT_NO_FATAL_FAILURE(player_fixture->Seek(kSbTimeSecond)); +} + +void NoInput(SbPlayerTestFixture* player_fixture) { + GroupedSamples samples; + if (player_fixture->HasAudio()) { + samples.AddAudioSamplesWithEOS(0, 0); + } + if (player_fixture->HasVideo()) { + samples.AddVideoSamplesWithEOS(0, 0); + } + ASSERT_NO_FATAL_FAILURE(player_fixture->Write(samples)); + ASSERT_NO_FATAL_FAILURE(player_fixture->WaitForPlayerEndOfStream()); +} + +void WriteSingleBatch(SbPlayerTestFixture* player_fixture) { + GroupedSamples samples; + if (player_fixture->HasAudio()) { + int samples_to_write = SbPlayerGetMaximumNumberOfSamplesPerWrite( + player_fixture->GetPlayer(), kSbMediaTypeAudio); + samples.AddAudioSamplesWithEOS(0, samples_to_write); + } + if (player_fixture->HasVideo()) { + int samples_to_write = SbPlayerGetMaximumNumberOfSamplesPerWrite( + player_fixture->GetPlayer(), kSbMediaTypeVideo); + samples.AddVideoSamplesWithEOS(0, samples_to_write); + } + + ASSERT_NO_FATAL_FAILURE(player_fixture->Write(samples)); + ASSERT_NO_FATAL_FAILURE(player_fixture->WaitForPlayerEndOfStream()); +} + +void WriteMultipleBatches(SbPlayerTestFixture* player_fixture) { + int samples_to_write = 0; + // Try to write multiple batches for both audio and video. + if (player_fixture->HasAudio()) { + samples_to_write = std::max( + samples_to_write, SbPlayerGetMaximumNumberOfSamplesPerWrite( + player_fixture->GetPlayer(), kSbMediaTypeAudio) + + 1); + } + if (player_fixture->HasVideo()) { + samples_to_write = std::max( + samples_to_write, SbPlayerGetMaximumNumberOfSamplesPerWrite( + player_fixture->GetPlayer(), kSbMediaTypeVideo) + + 1); + } + // TODO(b/283533109): We'd better to align the written audio and video samples + // to a same timestamp. Currently, we simply cap the batch size to 8 samples. + samples_to_write = std::min(samples_to_write, 8); + + GroupedSamples samples; + if (player_fixture->HasAudio()) { + samples.AddAudioSamplesWithEOS(0, samples_to_write); + } + if (player_fixture->HasVideo()) { + samples.AddVideoSamplesWithEOS(0, samples_to_write); + } + + ASSERT_NO_FATAL_FAILURE(player_fixture->Write(samples)); + ASSERT_NO_FATAL_FAILURE(player_fixture->WaitForPlayerEndOfStream()); +} + +class PlayerThread : public AbstractTestThread { + public: + PlayerThread(const SbPlayerTestConfig& config, + FakeGraphicsContextProvider* fake_graphics_context_provider, + const std::function& functor) + : config_(config), + functor_(functor), + fake_graphics_context_provider_(fake_graphics_context_provider) {} + + void Run() override { + SbPlayerTestFixture player(config_, fake_graphics_context_provider_); + functor_(&player); + } + + private: + SbPlayerTestConfig config_; + std::function functor_; + FakeGraphicsContextProvider* fake_graphics_context_provider_; +}; + +class MultiplePlayerTest + : public ::testing::TestWithParam { + protected: + void RunTest(const std::function& functor); + FakeGraphicsContextProvider fake_graphics_context_provider_; +}; + +void MultiplePlayerTest::RunTest( + const std::function& functor) { + const auto& test_configs = GetParam(); + + std::vector> player_threads; + for (const auto& test_config : test_configs) { + player_threads.emplace_back(std::make_unique( + test_config, &fake_graphics_context_provider_, functor)); + } + for (const auto& player_thread : player_threads) { + player_thread->Start(); + } + for (const auto& player_thread : player_threads) { + player_thread->Join(); + } +} + +TEST_P(MultiplePlayerTest, NoInput) { + RunTest(NoInput); +} + +TEST_P(MultiplePlayerTest, SeekAndDestroy) { + RunTest(SeekAndDestroy); +} + +TEST_P(MultiplePlayerTest, WriteSingleBatch) { + RunTest(WriteSingleBatch); +} + +TEST_P(MultiplePlayerTest, WriteMultipleBatches) { + RunTest(WriteMultipleBatches); +} + +INSTANTIATE_TEST_CASE_P(MultiplePlayerTests, + MultiplePlayerTest, + ValuesIn(GetSupportedTestConfigs()), + GetMultipleSbPlayerTestConfigName); + +} // namespace +} // namespace nplb +} // namespace starboard diff --git a/starboard/nplb/player_test_fixture.cc b/starboard/nplb/player_test_fixture.cc index 9d8273e74476..3f8887bf2ee2 100644 --- a/starboard/nplb/player_test_fixture.cc +++ b/starboard/nplb/player_test_fixture.cc @@ -46,8 +46,12 @@ SbPlayerTestFixture::CallbackEvent::CallbackEvent(SbPlayer player, player_state(state), ticket(ticket) {} -SbPlayerTestFixture::SbPlayerTestFixture(const SbPlayerTestConfig& config) - : output_mode_(std::get<2>(config)), key_system_(std::get<3>(config)) { +SbPlayerTestFixture::SbPlayerTestFixture( + const SbPlayerTestConfig& config, + FakeGraphicsContextProvider* fake_graphics_context_provider) + : output_mode_(std::get<2>(config)), + key_system_(std::get<3>(config)), + fake_graphics_context_provider_(fake_graphics_context_provider) { SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture || output_mode_ == kSbPlayerOutputModePunchOut); @@ -257,10 +261,10 @@ void SbPlayerTestFixture::Initialize() { video_codec = video_dmp_reader_->video_codec(); } player_ = CallSbPlayerCreate( - fake_graphics_context_provider_.window(), video_codec, audio_codec, + fake_graphics_context_provider_->window(), video_codec, audio_codec, drm_system_, audio_stream_info, "", DummyDeallocateSampleFunc, DecoderStatusCallback, PlayerStatusCallback, ErrorCallback, this, - output_mode_, fake_graphics_context_provider_.decoder_target_provider()); + output_mode_, fake_graphics_context_provider_->decoder_target_provider()); ASSERT_TRUE(SbPlayerIsValid(player_)); ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateInitialized)); ASSERT_NO_FATAL_FAILURE(Seek(0)); @@ -432,7 +436,7 @@ void SbPlayerTestFixture::GetDecodeTargetWhenSupported() { return; } #if SB_HAS(GLES2) - fake_graphics_context_provider_.RunOnGlesContextThread([&]() { + fake_graphics_context_provider_->RunOnGlesContextThread([&]() { ASSERT_TRUE(SbPlayerIsValid(player_)); if (output_mode_ != kSbPlayerOutputModeDecodeToTexture) { ASSERT_EQ(SbPlayerGetCurrentFrame(player_), kSbDecodeTargetInvalid); diff --git a/starboard/nplb/player_test_fixture.h b/starboard/nplb/player_test_fixture.h index 9a66f602d872..7006cb744b15 100644 --- a/starboard/nplb/player_test_fixture.h +++ b/starboard/nplb/player_test_fixture.h @@ -74,7 +74,9 @@ class SbPlayerTestFixture { bool write_video_eos_ = false; }; - explicit SbPlayerTestFixture(const SbPlayerTestConfig& config); + explicit SbPlayerTestFixture( + const SbPlayerTestConfig& config, + testing::FakeGraphicsContextProvider* fake_graphics_context_provider); ~SbPlayerTestFixture(); void Seek(const SbTime time); @@ -188,7 +190,7 @@ class SbPlayerTestFixture { std::string key_system_; scoped_ptr audio_dmp_reader_; scoped_ptr video_dmp_reader_; - testing::FakeGraphicsContextProvider fake_graphics_context_provider_; + testing::FakeGraphicsContextProvider* fake_graphics_context_provider_; SbPlayer player_ = kSbPlayerInvalid; SbDrmSystem drm_system_ = kSbDrmSystemInvalid; diff --git a/starboard/nplb/player_write_sample_test.cc b/starboard/nplb/player_write_sample_test.cc index cf33cb4909c5..b66af68097f7 100644 --- a/starboard/nplb/player_write_sample_test.cc +++ b/starboard/nplb/player_write_sample_test.cc @@ -26,12 +26,17 @@ namespace { using ::testing::ValuesIn; typedef SbPlayerTestFixture::GroupedSamples GroupedSamples; +typedef testing::FakeGraphicsContextProvider FakeGraphicsContextProvider; class SbPlayerWriteSampleTest - : public ::testing::TestWithParam {}; + : public ::testing::TestWithParam { + protected: + FakeGraphicsContextProvider fake_graphics_context_provider_; +}; TEST_P(SbPlayerWriteSampleTest, SeekAndDestroy) { - SbPlayerTestFixture player_fixture(GetParam()); + SbPlayerTestFixture player_fixture(GetParam(), + &fake_graphics_context_provider_); if (HasFatalFailure()) { return; } @@ -39,7 +44,8 @@ TEST_P(SbPlayerWriteSampleTest, SeekAndDestroy) { } TEST_P(SbPlayerWriteSampleTest, NoInput) { - SbPlayerTestFixture player_fixture(GetParam()); + SbPlayerTestFixture player_fixture(GetParam(), + &fake_graphics_context_provider_); if (HasFatalFailure()) { return; } @@ -56,7 +62,8 @@ TEST_P(SbPlayerWriteSampleTest, NoInput) { } TEST_P(SbPlayerWriteSampleTest, WriteSingleBatch) { - SbPlayerTestFixture player_fixture(GetParam()); + SbPlayerTestFixture player_fixture(GetParam(), + &fake_graphics_context_provider_); if (HasFatalFailure()) { return; } @@ -78,7 +85,8 @@ TEST_P(SbPlayerWriteSampleTest, WriteSingleBatch) { } TEST_P(SbPlayerWriteSampleTest, WriteMultipleBatches) { - SbPlayerTestFixture player_fixture(GetParam()); + SbPlayerTestFixture player_fixture(GetParam(), + &fake_graphics_context_provider_); if (HasFatalFailure()) { return; }