/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MediaMIMETypes.h" #include "PlatformDecoderModule.h" #include "WebrtcEnvironmentWrapper.h" #include "WebrtcGmpVideoCodec.h" #include "WebrtcVideoCodecFactory.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "mozilla/gtest/WaitFor.h" using testing::_; using testing::MockFunction; using testing::Test; namespace mozilla { enum class TestType : uint8_t { DecoderFactory, EncoderFactory }; struct TestWebrtcVideoDecoderFactory : public WebrtcVideoDecoderFactory, public Test { static constexpr TestType kTestType = TestType::DecoderFactory; RefPtr mEnvironment; TestWebrtcVideoDecoderFactory() : WebrtcVideoDecoderFactory(GetCurrentSerialEventTarget(), /*aPCHandle=*/"dummy_handle", /*aTrackingId=*/{}), mEnvironment(WebrtcEnvironmentWrapper::Create( dom::RTCStatsTimestampMaker::Create())) {} void SetUp() override { Preferences::SetBool("media.navigator.mediadatadecoder_h264_enabled", false); media::DecodeSupportSet h264GmpSupport = WebrtcVideoDecoderFactory::SupportsCodec( *MakeMediaExtendedMIMEType("video/H264"_ns), SupportDecoderParams(VideoInfo())); #ifdef ANDROID ASSERT_EQ(h264GmpSupport, media::DecodeSupportSet{}); GTEST_SKIP() << "fakeopenh264 plugin does not load on Android"; #else ASSERT_EQ(h264GmpSupport, media::DecodeSupportSet{media::DecodeSupport::SoftwareDecode}); #endif } void TearDown() override { Preferences::ClearUser("media.navigator.mediadatadecoder_h264_enabled"); } }; struct TestWebrtcVideoEncoderFactory : public WebrtcVideoEncoderFactory, public Test { static constexpr TestType kTestType = TestType::EncoderFactory; RefPtr mEnvironment; TestWebrtcVideoEncoderFactory() : WebrtcVideoEncoderFactory(GetCurrentSerialEventTarget(), /*aPCHandle=*/"dummy_handle"), mEnvironment(WebrtcEnvironmentWrapper::Create( dom::RTCStatsTimestampMaker::Create())) {} void SetUp() override { Preferences::SetInt("media.webrtc.encoder_creation_strategy", 0); Preferences::SetBool("media.webrtc.simulcast.h264.enabled", false); media::EncodeSupportSet h264GmpSupport = WebrtcVideoEncoderFactory::SupportsCodec(EncoderConfig( CodecType::H264, {640, 480}, Usage::Realtime, EncoderConfig::SampleFormat(dom::ImageBitmapFormat::YUV420P), 30, 240, 100'000, 50'000, 1'000'000, BitrateMode::Constant, HardwarePreference::None, ScalabilityMode::None, EncoderConfig::CodecSpecific(H264Specific()))); #ifdef ANDROID ASSERT_EQ(h264GmpSupport, media::EncodeSupportSet{}); GTEST_SKIP() << "fakeopenh264 plugin does not load on Android"; #else ASSERT_EQ(h264GmpSupport, media::EncodeSupportSet{media::EncodeSupport::SoftwareEncode}); #endif } void TearDown() override { Preferences::ClearUser("media.webrtc.encoder_creation_strategy"); Preferences::ClearUser("media.webrtc.simulcast.h264.enabled"); } }; template void DoTestRacyGmpPluginForwards(auto& aFactory) { static_assert(kTestType == TestType::DecoderFactory || kTestType == TestType::EncoderFactory); constexpr bool kIsDecoderTest = kTestType == TestType::DecoderFactory; using CodecImplType = std::conditional_t; nsCOMPtr owningThread = GetCurrentSerialEventTarget(); enum EventType { Created, Released }; MockFunction checkpoint; MediaEventListener createdListener = aFactory.CreatedGmpPluginEvent().Connect( owningThread, [&](uint64_t aId) { checkpoint.Call(Created, aId); }); MediaEventListener releasedListener = aFactory.ReleasedGmpPluginEvent().Connect( owningThread, [&](uint64_t aId) { checkpoint.Call(Released, aId); }); // It's a tight race, use a decent number. Still quick (~10ms opt non-debug). constexpr size_t kIterations = 1000; EXPECT_CALL(checkpoint, Call(Created, _)).Times(kIterations); EXPECT_CALL(checkpoint, Call(Released, _)).Times(kIterations); auto donePromise = TakeN(aFactory.ReleasedGmpPluginEvent(), kIterations); for (size_t i = 0; i < kIterations; ++i) { nsCOMPtr worker; MOZ_ALWAYS_SUCCEEDS( NS_CreateBackgroundTaskQueue(__func__, getter_AddRefs(worker))); MOZ_ALWAYS_SUCCEEDS(worker->Dispatch(NS_NewRunnableFunction(__func__, [&] { auto codec = aFactory.Create( aFactory.mEnvironment->Environment(), webrtc::SdpVideoFormat( webrtc::CodecTypeToPayloadString(webrtc::kVideoCodecH264))); EXPECT_TRUE(codec); auto* impl = static_cast(codec.get()); auto init = TakeN(*impl->InitPluginEvent(), 1); if constexpr (kIsDecoderTest) { EXPECT_TRUE(codec->Configure({})); } else { webrtc::VideoCodec codec_properties; webrtc::VideoEncoder::Capabilities capabilities( /*loss_notification=*/false); webrtc::VideoEncoder::Settings settings( capabilities, /*number_of_cores=*/1, /*max_payload_size=*/0); EXPECT_EQ(codec->InitEncode(&codec_properties, settings), 0); } init->Then( GetCurrentSerialEventTarget(), "RacyGmpPluginForwards", [codec = std::move(codec)] { EXPECT_EQ(codec->Release(), 0); }); }))); } (void)WaitFor(donePromise); aFactory.DisconnectAll(); createdListener.Disconnect(); releasedListener.Disconnect(); } TEST_F(TestWebrtcVideoDecoderFactory, RacyGmpPluginForwards) { DoTestRacyGmpPluginForwards(*this); } TEST_F(TestWebrtcVideoEncoderFactory, RacyGmpPluginForwards) { DoTestRacyGmpPluginForwards(*this); } } // namespace mozilla