// Copyright Epic Games, Inc. All Rights Reserved. #include "DefaultGameMoviePlayer.h" #include "HAL/PlatformSplash.h" #include "Misc/ScopeLock.h" #include "Misc/CoreDelegates.h" #include "EngineGlobals.h" #include "Widgets/SViewport.h" #include "Engine/GameEngine.h" #include "Framework/Application/SlateApplication.h" #include "Input/HittestGrid.h" #include "Widgets/Layout/SBox.h" #include "GlobalShader.h" #include "MoviePlayerProxy.h" #include "MoviePlayerThreading.h" #include "MoviePlayerSettings.h" #include "ShaderCompiler.h" #include "IHeadMountedDisplay.h" #include "IXRTrackingSystem.h" #include "IXRLoadingScreen.h" #include "Misc/ConfigCacheIni.h" #include "HAL/FileManager.h" #include "Widgets/SVirtualWindow.h" #include "Rendering/SlateDrawBuffer.h" #include "HAL/PlatformApplicationMisc.h" #include "Http.h" #include "HttpManager.h" #include "Widgets/Layout/SDPIScaler.h" #include "Engine/UserInterfaceSettings.h" DEFINE_LOG_CATEGORY_STATIC(LogMoviePlayer, Log, All); class SDefaultMovieBorder : public SBorder { public: SLATE_BEGIN_ARGS(SDefaultMovieBorder) : _OnKeyDown() {} SLATE_EVENT(FPointerEventHandler, OnMouseButtonDown) SLATE_EVENT(FOnKeyDown, OnKeyDown) SLATE_DEFAULT_SLOT(FArguments, Content) SLATE_END_ARGS() /** * Construct this widget * * @param InArgs The declaration data for this widget */ void Construct(const FArguments& InArgs) { OnKeyDownHandler = InArgs._OnKeyDown; SBorder::Construct(SBorder::FArguments() .BorderImage(FCoreStyle::Get().GetBrush(TEXT("BlackBrush"))) .OnMouseButtonDown(InArgs._OnMouseButtonDown) .Padding(0)[InArgs._Content.Widget]); } /** * Set the handler to be invoked when the user presses a key. * * @param InHandler Method to execute when the user presses a key */ void SetOnOnKeyDown(const FOnKeyDown& InHandler) { OnKeyDownHandler = InHandler; } /** * Overrides SWidget::OnKeyDown() * executes OnKeyDownHandler if it is bound */ FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override { if (OnKeyDownHandler.IsBound()) { // If a handler is assigned, call it. return OnKeyDownHandler.Execute(MyGeometry, InKeyEvent); } return SBorder::OnKeyDown(MyGeometry, InKeyEvent); } /** * Overrides SWidget::SupportsKeyboardFocus() * Must support keyboard focus to accept OnKeyDown events */ bool SupportsKeyboardFocus() const override { return true; } protected: FOnKeyDown OnKeyDownHandler; }; TSharedPtr FDefaultGameMoviePlayer::MoviePlayer; FDefaultGameMoviePlayer* FDefaultGameMoviePlayer::Get() { return MoviePlayer.Get(); } FDefaultGameMoviePlayer::FDefaultGameMoviePlayer() : FTickableObjectRenderThread(false, true) , SyncMechanism(NULL) , MovieStreamingIsDone(1) , LoadingIsDone(1) , IsMoviePlaying(false) , bUserCalledFinish(false) , bMainWindowClosed(false) , LoadingScreenAttributes() , LastPlayTime(0.0) , bInitialized(false) , bIsPlayOnBlockingEnabled(false) , bIsSlateThreadAllowed(true) , ViewportDPIScale(1.0f) , BlockingRefCount(0) , LastBlockingTickTime(0.0) { FCoreDelegates::IsLoadingMovieCurrentlyPlaying.BindRaw(this, &FDefaultGameMoviePlayer::IsMovieCurrentlyPlaying); FCoreDelegates::RegisterMovieStreamerDelegate.AddRaw(this, &FDefaultGameMoviePlayer::RegisterMovieStreamer); } FDefaultGameMoviePlayer::~FDefaultGameMoviePlayer() { if ( bInitialized ) { // This should not happen if initialize was called correctly. This is a fallback to ensure that the movie player rendering tickable gets unregistered on the rendering thread correctly Shutdown(); } else if (GIsRHIInitialized) { // Even when uninitialized we must safely unregister the movie player on the render thread FDefaultGameMoviePlayer* InMoviePlayer = this; ENQUEUE_RENDER_COMMAND(UnregisterMoviePlayerTickable)( [InMoviePlayer](FRHICommandListImmediate& RHICmdList) { InMoviePlayer->Unregister(); }); } FCoreDelegates::IsLoadingMovieCurrentlyPlaying.Unbind(); FlushRenderingCommands(); } void FDefaultGameMoviePlayer::RegisterMovieStreamer(TSharedPtr InMovieStreamer) { if (InMovieStreamer.IsValid() && !MovieStreamers.Contains(InMovieStreamer)) { MovieStreamers.Add(InMovieStreamer); InMovieStreamer->OnCurrentMovieClipFinished().AddRaw(this, &FDefaultGameMoviePlayer::BroadcastMovieClipFinished); } } void FDefaultGameMoviePlayer::Initialize(FSlateRenderer& InSlateRenderer, TSharedPtr TargetRenderWindow) { if(bInitialized) { return; } LLM_SCOPE_BYNAME(TEXT("SlateMoviePlayer")); UE_LOG(LogMoviePlayer, Log, TEXT("Initializing movie player")); FDefaultGameMoviePlayer* InMoviePlayer = this; ENQUEUE_RENDER_COMMAND(RegisterMoviePlayerTickable)( [InMoviePlayer](FRHICommandListImmediate& RHICmdList) { InMoviePlayer->Register(); }); bInitialized = true; // Initialize shaders, because otherwise they might not be guaranteed to exist at this point if (!FPlatformProperties::RequiresCookedData()) { TArray ShaderMapIds; ShaderMapIds.Add(GlobalShaderMapId); GShaderCompilingManager->FinishCompilation(TEXT("Global"), ShaderMapIds); } // Add a delegate to start playing movies when we start loading a map FCoreUObjectDelegates::PreLoadMap.AddRaw( this, &FDefaultGameMoviePlayer::OnPreLoadMap ); // Shutdown the movie player if the app is exiting FCoreDelegates::OnPreExit.AddRaw( this, &FDefaultGameMoviePlayer::Shutdown ); FPlatformSplash::Hide(); // Use the passed in RenderWindow if it was provided, create one otherwise const TSharedRef GameWindow = TargetRenderWindow.IsValid() ? TargetRenderWindow.ToSharedRef() : UGameEngine::CreateGameWindow(); TSharedPtr MovieViewport; VirtualRenderWindow = SNew(SVirtualWindow) .Size(GameWindow->GetClientSizeInScreen()); WidgetRenderer = MakeShared(GameWindow, VirtualRenderWindow, &InSlateRenderer); LoadingScreenContents = SNew(SDefaultMovieBorder) .OnKeyDown(this, &FDefaultGameMoviePlayer::OnLoadingScreenKeyDown) .OnMouseButtonDown(this, &FDefaultGameMoviePlayer::OnLoadingScreenMouseButtonDown) [ SNew(SOverlay) +SOverlay::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(SBox) .WidthOverride(this, &FDefaultGameMoviePlayer::GetMovieWidth) .HeightOverride(this, &FDefaultGameMoviePlayer::GetMovieHeight) [ SAssignNew(MovieViewport, SViewport) .EnableGammaCorrection(false) .Visibility(this, &FDefaultGameMoviePlayer::GetViewportVisibility) ] ] +SOverlay::Slot() [ SAssignNew(UserWidgetDPIScaler, SDPIScaler) [ SAssignNew(UserWidgetHolder, SBorder) .BorderImage(FCoreStyle::Get().GetBrush(TEXT("NoBorder"))) .Padding(0) ] ] ]; MovieViewportWeakPtr = MovieViewport; MovieViewport->SetActive(true); MainWindow = GameWindow; GameWindow->GetOnWindowClosedEvent().AddRaw(this, &FDefaultGameMoviePlayer::OnMainWindowClosed); } void FDefaultGameMoviePlayer::OnMainWindowClosed(const TSharedRef& Window) { bMainWindowClosed = true; } void FDefaultGameMoviePlayer::Shutdown() { UE_LOG(LogMoviePlayer, Log, TEXT("Shutting down movie player")); FMoviePlayerProxy::UnregisterServer(); TSharedPtr MainWindowShared = MainWindow.Pin(); if (MainWindowShared.IsValid()) { MainWindowShared->GetOnWindowClosedEvent().RemoveAll(this); MainWindowShared.Reset(); } StopMovie(); WaitForMovieToFinish(); FDefaultGameMoviePlayer* InMoviePlayer = this; ENQUEUE_RENDER_COMMAND(UnregisterMoviePlayerTickable)( [InMoviePlayer](FRHICommandListImmediate& RHICmdList) { InMoviePlayer->Unregister(); }); bInitialized = false; FCoreDelegates::OnPreExit.RemoveAll(this); FCoreUObjectDelegates::PreLoadMap.RemoveAll( this ); FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this); LoadingScreenContents.Reset(); UserWidgetHolder.Reset(); MainWindow.Reset(); VirtualRenderWindow.Reset(); MovieStreamers.Empty(); ActiveMovieStreamer.Reset(); LoadingScreenAttributes = FLoadingScreenAttributes(); if( SyncMechanism ) { SyncMechanism->DestroySlateThread(); FScopeLock SyncMechanismLock(&SyncMechanismCriticalSection); delete SyncMechanism; SyncMechanism = NULL; } } void FDefaultGameMoviePlayer::PassLoadingScreenWindowBackToGame() const { UGameEngine* GameEngine = Cast(GEngine); if (MainWindow.IsValid() && GameEngine) { GameEngine->GameViewportWindow = MainWindow; } else { UE_LOG(LogMoviePlayer, Warning, TEXT("PassLoadingScreenWindowBackToGame failed. No Window") ); } } void FDefaultGameMoviePlayer::SetupLoadingScreen(const FLoadingScreenAttributes& InLoadingScreenAttributes) { if (!CanPlayMovie()) { LoadingScreenAttributes = FLoadingScreenAttributes(); UE_LOG(LogMoviePlayer, Warning, TEXT("Initial loading screen disabled from BaseDeviceProfiles.ini: r.AndroidDisableThreadedRenderingFirstLoad=1")); } else { LoadingScreenAttributes = InLoadingScreenAttributes; } } bool FDefaultGameMoviePlayer::HasEarlyStartupMovie() const { #if PLATFORM_SUPPORTS_EARLY_MOVIE_PLAYBACK return LoadingScreenAttributes.bAllowInEarlyStartup == true; #else return false; #endif } bool FDefaultGameMoviePlayer::PlayEarlyStartupMovies() { if(HasEarlyStartupMovie()) { return PlayMovie(); } return false; } bool FDefaultGameMoviePlayer::PlayMovie() { bool bBeganPlaying = false; // Allow systems to hook onto the movie player and provide loading screen data on demand // if it has not been setup explicitly by the user. if ( !LoadingScreenIsPrepared() ) { OnPrepareLoadingScreenDelegate.Broadcast(); } if (LoadingScreenIsPrepared() && !IsMovieCurrentlyPlaying() && FPlatformMisc::NumberOfCores() > 1) { check(LoadingScreenAttributes.IsValid()); bUserCalledFinish = false; LastPlayTime = FPlatformTime::Seconds(); ActiveMovieStreamer.Reset(); if (MovieStreamingIsPrepared()) { for (TSharedPtr MovieStreamer : MovieStreamers) { if (MovieStreamer->Init(LoadingScreenAttributes.MoviePaths, LoadingScreenAttributes.PlaybackType)) { ActiveMovieStreamer = MovieStreamer; if (MovieViewportWeakPtr.IsValid()) { MovieViewportWeakPtr.Pin()->SetViewportInterface(MovieStreamer->GetViewportInterface().ToSharedRef()); } break; } } } if (ActiveMovieStreamer.IsValid() || !MovieStreamingIsPrepared()) { MovieStreamingIsDone.Set(MovieStreamingIsPrepared() ? 0 : 1); LoadingIsDone.Set(0); IsMoviePlaying = true; UserWidgetDPIScaler->SetDPIScale(GetViewportDPIScale()); UserWidgetHolder->SetContent(LoadingScreenAttributes.WidgetLoadingScreen.IsValid() ? LoadingScreenAttributes.WidgetLoadingScreen.ToSharedRef() : SNullWidget::NullWidget); VirtualRenderWindow->Resize(MainWindow.Pin()->GetClientSizeInScreen()); VirtualRenderWindow->SetContent(LoadingScreenContents.ToSharedRef()); // Register the movie viewport so that it can receive user input. // There is only a valid viewport if we have a movie streamer as the streamer sets it. if ((!FPlatformProperties::SupportsWindowedMode()) && (ActiveMovieStreamer.IsValid())) { TSharedPtr MovieViewport = MovieViewportWeakPtr.Pin(); if (MovieViewport.IsValid()) { // Let the streamer know about the previous viewport interface. TSharedPtr GameViewport = FSlateApplication::Get().GetGameViewport(); if (GameViewport.IsValid()) { TSharedPtr ViewportInterface = GameViewport->GetViewportInterface().Pin(); ActiveMovieStreamer->PreviousViewportInterface(ViewportInterface); } FSlateApplication::Get().RegisterGameViewport(MovieViewport.ToSharedRef()); } } { FScopeLock SyncMechanismLock(&SyncMechanismCriticalSection); SyncMechanism = new FSlateLoadingSynchronizationMechanism(WidgetRenderer, ActiveMovieStreamer); SyncMechanism->Initialize(); } bBeganPlaying = true; LastBlockingTickTime = FPlatformTime::Seconds(); OnAsyncLoadingFlushUpdateDelegateHandle = FCoreDelegates::OnAsyncLoadingFlushUpdate.AddRaw(this, &FDefaultGameMoviePlayer::BlockingTick); } //Allow anything that set up this LoadingScreenAttribute to know the loading screen is now displaying if (bBeganPlaying) { OnMoviePlaybackStarted().Broadcast(); } } return bBeganPlaying; } /** Check if the device can render on a parallel thread on the initial loading*/ bool FDefaultGameMoviePlayer::CanPlayMovie() const { const IConsoleVariable *const CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.AndroidDisableThreadedRenderingFirstLoad")); if (CVar && CVar->GetInt() != 0) { return (GEngine && GEngine->IsInitialized()); } return true; } void FDefaultGameMoviePlayer::StopMovie() { LastPlayTime = 0; bUserCalledFinish = true; } void FDefaultGameMoviePlayer::WaitForMovieToFinish(bool bAllowEngineTick) { const bool bEnforceMinimumTime = LoadingScreenAttributes.MinimumLoadingScreenDisplayTime >= 0.0f; if (LoadingScreenIsPrepared() && IsMovieCurrentlyPlaying()) { if (SyncMechanism) { SyncMechanism->DestroySlateThread(); FScopeLock SyncMechanismLock(&SyncMechanismCriticalSection); delete SyncMechanism; SyncMechanism = nullptr; } if( !bEnforceMinimumTime ) { LoadingIsDone.Set(1); } if (TSharedPtr MainWindowPtr = MainWindow.Pin()) { // Transfer the content to the main window MainWindowPtr->SetContent(LoadingScreenContents.ToSharedRef()); } if (VirtualRenderWindow.IsValid()) { VirtualRenderWindow->SetContent(SNullWidget::NullWidget); } const bool bAutoCompleteWhenLoadingCompletes = LoadingScreenAttributes.bAutoCompleteWhenLoadingCompletes; const bool bWaitForManualStop = LoadingScreenAttributes.bWaitForManualStop; FSlateApplication& SlateApp = FSlateApplication::Get(); // Make sure the movie player widget has user focus to accept keypresses if (LoadingScreenContents.IsValid()) { SlateApp.SetAllUserFocus(LoadingScreenContents); } // Continue to wait until the user calls finish (if enabled) or when loading completes or the minimum enforced time (if any) has been reached. // Don't continue playing on game shutdown while ( !IsEngineExitRequested() && ((bWaitForManualStop && !bUserCalledFinish) || (!bUserCalledFinish && !bEnforceMinimumTime && !IsMovieStreamingFinished() && !bAutoCompleteWhenLoadingCompletes) || (bEnforceMinimumTime && (FPlatformTime::Seconds() - LastPlayTime) < LoadingScreenAttributes.MinimumLoadingScreenDisplayTime))) { BeginExitIfRequested(); // If we are in a loading loop, and this is the last movie in the playlist.. assume you can break out. if (ActiveMovieStreamer.IsValid() && LoadingScreenAttributes.PlaybackType == MT_LoadingLoop && ActiveMovieStreamer->IsLastMovieInPlaylist()) { break; } if (FSlateApplication::IsInitialized()) { // Break out of the loop if the main window is closed during the movie. if ( !MainWindow.IsValid() || bMainWindowClosed.Load() ) { if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->ForceCompletion(); } break; } FPlatformApplicationMisc::PumpMessages(true); SlateApp.PollGameDeviceState(); // Gives widgets a chance to process any accumulated input SlateApp.FinishedInputThisFrame(); float DeltaTime = SlateApp.GetDeltaTime(); if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->TickPreEngine(); } if (GEngine && bAllowEngineTick && LoadingScreenAttributes.bAllowEngineTick) { GEngine->Tick(DeltaTime, false); } if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->TickPostEngine(); } FDefaultGameMoviePlayer* InMoviePlayer = this; ENQUEUE_RENDER_COMMAND(BeginLoadingMovieFrameAndTickMovieStreamer)( [InMoviePlayer, DeltaTime](FRHICommandListImmediate& RHICmdList) { GFrameNumberRenderThread++; InMoviePlayer->TickStreamer(DeltaTime); } ); { FSlateRenderer* SlateRenderer = SlateApp.GetRenderer(); FScopeLock ScopeLock(SlateRenderer->GetResourceCriticalSection()); SlateApp.Tick(); // Synchronize the game thread and the render thread so that the render thread doesn't get too far behind. SlateRenderer->Sync(); } ENQUEUE_RENDER_COMMAND(FinishLoadingMovieFrame)( [](FRHICommandListImmediate& RHICmdList) { GRHICommandList.GetImmediateCommandList().EndFrame(); GRHICommandList.GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources); } ); FlushRenderingCommands(); if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->TickPostRender(); } } } if (UserWidgetHolder.IsValid()) { UserWidgetHolder->SetContent(SNullWidget::NullWidget); } LoadingIsDone.Set(1); IsMoviePlaying = false; FCoreDelegates::OnAsyncLoadingFlushUpdate.Remove(OnAsyncLoadingFlushUpdateDelegateHandle); OnAsyncLoadingFlushUpdateDelegateHandle.Reset(); IXRLoadingScreen* LoadingScreen; if (GEngine && GEngine->XRSystem.IsValid() && (LoadingScreen = GEngine->XRSystem->GetLoadingScreen()) != nullptr && SyncMechanism == nullptr) { LoadingScreen->ClearSplashes(); } MovieStreamingIsDone.Set(1); FlushRenderingCommands(); if( ActiveMovieStreamer.IsValid() ) { ActiveMovieStreamer->ForceCompletion(); } // Allow the movie streamer to clean up any resources it uses once there are no movies to play. if( ActiveMovieStreamer.IsValid() ) { ActiveMovieStreamer->Cleanup(); } // Finally, clear out the loading screen attributes, forcing users to always // explicitly set the loading screen they want (rather than have stale loading screens) LoadingScreenAttributes = FLoadingScreenAttributes(); BroadcastMoviePlaybackFinished(); } else { UGameEngine* GameEngine = Cast(GEngine); // Don't switch the window on game shutdown if (GameEngine && !IsEngineExitRequested()) { GameEngine->SwitchGameWindowToUseGameViewport(); } } } bool FDefaultGameMoviePlayer::IsLoadingFinished() const { return LoadingIsDone.GetValue() != 0; } bool FDefaultGameMoviePlayer::IsMovieCurrentlyPlaying() const { return SyncMechanism != NULL; } bool FDefaultGameMoviePlayer::IsMovieStreamingFinished() const { return MovieStreamingIsDone.GetValue() != 0; } void FDefaultGameMoviePlayer::BlockingStarted() { if ((bIsPlayOnBlockingEnabled) && (bIsSlateThreadAllowed)) { UE_LOG(LogMoviePlayer, Verbose, TEXT("BlockingStarted %d"), BlockingRefCount); BlockingRefCount++; PlayMovie(); } } void FDefaultGameMoviePlayer::BlockingTick() { check(IsInGameThread()); if (IsMovieCurrentlyPlaying()) { // Has enough time passed since the last tick? double Time = FPlatformTime::Seconds(); double DeltaTime = Time - LastBlockingTickTime; if (DeltaTime > 0.1f) { // Yes. Time for another tick. LastBlockingTickTime = Time; // Call HTTP manager. FHttpManager& HttpManager = FHttpModule::Get().GetHttpManager(); HttpManager.Tick(0.0f); // Callbacks. OnMoviePlaybackTick().Broadcast((float)DeltaTime); UE_LOG(LogMoviePlayer, VeryVerbose, TEXT("BlockingTick deltatime:%f"), DeltaTime); } } } void FDefaultGameMoviePlayer::BlockingFinished() { if (bIsPlayOnBlockingEnabled) { // Only call WaitForMovieToFinish if we are playing a movie, // as WaitForMovieToFinish has side effects if a movie is not playing, // and this can cause a hang. if (LoadingScreenIsPrepared() && IsMovieCurrentlyPlaying()) { UE_LOG(LogMoviePlayer, Verbose, TEXT("BlockingFinished. Refcount: %d."), BlockingRefCount); WaitForMovieToFinish(); } BlockingRefCount = 0; } } void FDefaultGameMoviePlayer::SetIsSlateThreadAllowed(bool bInIsSlateThreadAllowed) { if (bIsSlateThreadAllowed != bInIsSlateThreadAllowed) { bIsSlateThreadAllowed = bInIsSlateThreadAllowed; // Can we use the Slate thread? if (bIsSlateThreadAllowed == false) { // Nope. Make sure its no longer running. BlockingFinished(); } } } void FDefaultGameMoviePlayer::Tick( float DeltaTime ) { check(IsInRenderingThread()); if (MainWindow.IsValid() && VirtualRenderWindow.IsValid() && !IsLoadingFinished() && GDynamicRHI && !GDynamicRHI->RHIIsRenderingSuspended()) { FScopeLock SyncMechanismLock(&SyncMechanismCriticalSection); if(SyncMechanism) { if(SyncMechanism->IsSlateDrawPassEnqueued()) { GFrameNumberRenderThread++; TickStreamer(DeltaTime); SyncMechanism->ResetSlateDrawPassEnqueued(); GRHICommandList.GetImmediateCommandList().EndFrame(); GRHICommandList.GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources); } } } } void FDefaultGameMoviePlayer::TickStreamer(float DeltaTime) { if (MovieStreamingIsPrepared() && ActiveMovieStreamer.IsValid() && !IsMovieStreamingFinished()) { const bool bMovieIsDone = ActiveMovieStreamer->Tick(DeltaTime); if (bMovieIsDone) { MovieStreamingIsDone.Set(1); } // commenting loading screen currently as we don't support changing/adding/removing splash screens on the renderthread /*IXRLoadingScreen* LoadingScreen; if (GEngine && GEngine->XRSystem.IsValid() && (LoadingScreen = GEngine->XRSystem->GetLoadingScreen()) != nullptr) { FTextureRHIRef Movie2DTexture = ActiveMovieStreamer->GetTexture(); LoadingScreen->ClearSplashes(); if (Movie2DTexture.IsValid() && !bMovieIsDone) { IXRLoadingScreen::FSplashDesc Splash; Splash.Texture = (FRHITexture*)Movie2DTexture.GetReference(); Splash.bIsDynamic = true; const FIntPoint TextureSize = Movie2DTexture->GetSizeXY(); const float InvAspectRatio = (TextureSize.X > 0) ? float(TextureSize.Y) / float(TextureSize.X) : 1.0f; Splash.bIgnoreAlpha = true; Splash.Transform = FTransform(FVector(5.0f, 0.0f, 1.0f)); Splash.QuadSize = FVector2D(8.0f, 8.0f*InvAspectRatio); LoadingScreen->AddSplash(Splash); } }*/ } } TStatId FDefaultGameMoviePlayer::GetStatId() const { RETURN_QUICK_DECLARE_CYCLE_STAT(FDefaultGameMoviePlayer, STATGROUP_Tickables); } bool FDefaultGameMoviePlayer::IsTickable() const { return true; } bool FDefaultGameMoviePlayer::LoadingScreenIsPrepared() const { return LoadingScreenAttributes.WidgetLoadingScreen.IsValid() || MovieStreamingIsPrepared(); } void FDefaultGameMoviePlayer::SetupLoadingScreenFromIni() { // We may have already setup a movie from a startup module if( !LoadingScreenAttributes.IsValid() ) { // fill out the attributes FLoadingScreenAttributes LoadingScreen; bool bWaitForMoviesToComplete = false; // Note: this code is executed too early so we cannot access UMoviePlayerSettings because the configs for that object have not been loaded and coalesced . Have to read directly from the configs instead GConfig->GetBool(TEXT("/Script/MoviePlayer.MoviePlayerSettings"), TEXT("bWaitForMoviesToComplete"), bWaitForMoviesToComplete, GGameIni); GConfig->GetBool(TEXT("/Script/MoviePlayer.MoviePlayerSettings"), TEXT("bMoviesAreSkippable"), LoadingScreen.bMoviesAreSkippable, GGameIni); LoadingScreen.bAutoCompleteWhenLoadingCompletes = !bWaitForMoviesToComplete; TArray StartupMovies; GConfig->GetArray(TEXT("/Script/MoviePlayer.MoviePlayerSettings"), TEXT("StartupMovies"), StartupMovies, GGameIni); if (StartupMovies.Num() == 0) { StartupMovies.Add(TEXT("Default_Startup")); } // double check that the movies exist // We dont know the extension so compare against any file in the directory with the same name for now // @todo New Movie Player: movies should have the extension on them when set via the project settings TArray ExistingMovieFiles; IFileManager::Get().FindFiles(ExistingMovieFiles, *(FPaths::ProjectContentDir() + TEXT("Movies"))); bool bHasValidMovie = false; for(const FString& Movie : StartupMovies) { bool bFound = ExistingMovieFiles.ContainsByPredicate( [&Movie](const FString& ExistingMovie) { return ExistingMovie.Contains(Movie); }); if(bFound) { bHasValidMovie = true; LoadingScreen.MoviePaths.Add(Movie); } } if(bHasValidMovie) { // These movies are all considered safe to play in very early startup sequences LoadingScreen.bAllowInEarlyStartup = true; // now setup the actual loading screen SetupLoadingScreen(LoadingScreen); } } } void FDefaultGameMoviePlayer::SetViewportDPIScale(float InViewportDPIScale) { ViewportDPIScale = InViewportDPIScale; // Turn off scale in the renderer as we have our own scale. if (WidgetRenderer != nullptr) { WidgetRenderer->EnableDPIScale(false); } } bool FDefaultGameMoviePlayer::MovieStreamingIsPrepared() const { return MovieStreamers.Num() > 0 && LoadingScreenAttributes.MoviePaths.Num() > 0; } FVector2D FDefaultGameMoviePlayer::GetMovieSize() const { const FVector2D ScreenSize = MainWindow.Pin()->GetClientSizeInScreen(); if (MovieStreamingIsPrepared() && ActiveMovieStreamer.IsValid()) { const double MovieAspectRatio = ActiveMovieStreamer->GetAspectRatio(); const double ScreenAspectRatio = ScreenSize.X / ScreenSize.Y; if (MovieAspectRatio < ScreenAspectRatio) { return FVector2D(ScreenSize.Y * MovieAspectRatio, ScreenSize.Y); } else { return FVector2D(ScreenSize.X, ScreenSize.X / MovieAspectRatio); } } // No movie, so simply return the size of the window return ScreenSize; } FOptionalSize FDefaultGameMoviePlayer::GetMovieWidth() const { return (float)GetMovieSize().X; } FOptionalSize FDefaultGameMoviePlayer::GetMovieHeight() const { return (float)GetMovieSize().Y; } EVisibility FDefaultGameMoviePlayer::GetSlateBackgroundVisibility() const { return MovieStreamingIsPrepared() && ActiveMovieStreamer.IsValid() && !IsMovieStreamingFinished() ? EVisibility::Collapsed : EVisibility::Visible; } EVisibility FDefaultGameMoviePlayer::GetViewportVisibility() const { return MovieStreamingIsPrepared() && ActiveMovieStreamer.IsValid() && !IsMovieStreamingFinished() ? EVisibility::Visible : EVisibility::Collapsed; } FReply FDefaultGameMoviePlayer::OnLoadingScreenMouseButtonDown(const FGeometry& Geometry, const FPointerEvent& PointerEvent) { return OnAnyDown(); } FReply FDefaultGameMoviePlayer::OnLoadingScreenKeyDown(const FGeometry& Geometry, const FKeyEvent& KeyEvent) { return OnAnyDown(); } FReply FDefaultGameMoviePlayer::OnAnyDown() { if (IsLoadingFinished()) { if (LoadingScreenAttributes.bMoviesAreSkippable) { MovieStreamingIsDone.Set(1); if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->ForceCompletion(); } } if (IsMovieStreamingFinished()) { bUserCalledFinish = true; } } return FReply::Handled(); } void FDefaultGameMoviePlayer::OnPreLoadMap(const FString& LevelName) { if (bIsPlayOnBlockingEnabled == false) { UE_LOG(LogMoviePlayer, Verbose, TEXT("PreLoadMap")); FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this); if (PlayMovie()) { FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FDefaultGameMoviePlayer::OnPostLoadMap); } } } void FDefaultGameMoviePlayer::OnPostLoadMap(UWorld* LoadedWorld) { if (bIsPlayOnBlockingEnabled == false) { UE_LOG(LogMoviePlayer, Verbose, TEXT("PostLoadMap")); if (!LoadingScreenAttributes.bAllowEngineTick) { // If engine tick is enabled, we don't want to tick here and instead want to run from the WaitForMovieToFinish call in LaunchEngineLoop WaitForMovieToFinish(); } } } void FDefaultGameMoviePlayer::SetSlateOverlayWidget(TSharedPtr NewOverlayWidget) { if (ActiveMovieStreamer.IsValid() && UserWidgetHolder.IsValid()) { UserWidgetHolder->SetContent(NewOverlayWidget.ToSharedRef()); } } bool FDefaultGameMoviePlayer::WillAutoCompleteWhenLoadFinishes() { return LoadingScreenAttributes.bAutoCompleteWhenLoadingCompletes || (LoadingScreenAttributes.PlaybackType == MT_LoadingLoop && (ActiveMovieStreamer.IsValid() && ActiveMovieStreamer->IsLastMovieInPlaylist())); } FString FDefaultGameMoviePlayer::GetMovieName() { return ActiveMovieStreamer.IsValid() ? ActiveMovieStreamer->GetMovieName() : TEXT(""); } bool FDefaultGameMoviePlayer::IsLastMovieInPlaylist() { return ActiveMovieStreamer.IsValid() ? ActiveMovieStreamer->IsLastMovieInPlaylist() : false; } FMoviePlayerWidgetRenderer::FMoviePlayerWidgetRenderer(TSharedPtr InMainWindow, TSharedPtr InVirtualRenderWindow, FSlateRenderer* InRenderer) : MainWindow(InMainWindow.Get()) , VirtualRenderWindow(InVirtualRenderWindow.ToSharedRef()) , SlateRenderer(InRenderer) , bIsDPIScaleEnabled(true) { HittestGrid = MakeShareable(new FHittestGrid); } void FMoviePlayerWidgetRenderer::EnableDPIScale(bool bShouldEnable) { bIsDPIScaleEnabled = bShouldEnable; } void FMoviePlayerWidgetRenderer::DrawWindow(float DeltaTime) { if (GDynamicRHI && GDynamicRHI->RHIIsRenderingSuspended()) { // This avoids crashes if we Suspend rendering whilst the loading screen is up // as we don't want Slate to submit any more draw calls until we Resume. return; } float Scale = FSlateApplication::Get().GetApplicationScale(); if (bIsDPIScaleEnabled) { Scale *= MainWindow->GetDPIScaleFactor(); } FVector2D DrawSize = VirtualRenderWindow->GetClientSizeInScreen() / Scale; FSlateApplication::Get().Tick(ESlateTickType::Time); FGeometry WindowGeometry = FGeometry::MakeRoot(DrawSize, FSlateLayoutTransform(Scale)); VirtualRenderWindow->SlatePrepass(WindowGeometry.Scale); FSlateRect ClipRect = WindowGeometry.GetLayoutBoundingRect(); HittestGrid->SetHittestArea(VirtualRenderWindow->GetPositionInScreen(), VirtualRenderWindow->GetViewportSize()); HittestGrid->Clear(); { // Get the free buffer & add our virtual window FSlateRenderer::FScopedAcquireDrawBuffer ScopedDrawBuffer{ *SlateRenderer }; FSlateWindowElementList& WindowElementList = ScopedDrawBuffer.GetDrawBuffer().AddWindowElementList(VirtualRenderWindow); WindowElementList.SetRenderTargetWindow(MainWindow); int32 MaxLayerId = 0; { FPaintArgs PaintArgs(nullptr, *HittestGrid, FVector2D::ZeroVector, FSlateApplication::Get().GetCurrentTime(), FSlateApplication::Get().GetDeltaTime()); // Paint the window MaxLayerId = VirtualRenderWindow->Paint( PaintArgs, WindowGeometry, ClipRect, WindowElementList, 0, FWidgetStyle(), VirtualRenderWindow->IsEnabled()); } SlateRenderer->DrawWindows(ScopedDrawBuffer.GetDrawBuffer()); ScopedDrawBuffer.GetDrawBuffer().ViewOffset = FVector2D::ZeroVector; } } float FDefaultGameMoviePlayer::GetViewportDPIScale() const { return ViewportDPIScale; } void FDefaultGameMoviePlayer::ForceCompletion() { bUserCalledFinish = true; MovieStreamingIsDone.Set(1); if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->ForceCompletion(); } } /*Interrupts*/ void FDefaultGameMoviePlayer::Suspend() { if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->Suspend(); } } void FDefaultGameMoviePlayer::Resume() { if (ActiveMovieStreamer.IsValid()) { ActiveMovieStreamer->Resume(); } } void FDefaultGameMoviePlayer::SetIsPlayOnBlockingEnabled(bool bIsEnabled) { if (bIsPlayOnBlockingEnabled != bIsEnabled) { bIsPlayOnBlockingEnabled = bIsEnabled; if (bIsPlayOnBlockingEnabled) { FMoviePlayerProxy::RegisterServer(this); } else { BlockingFinished(); FMoviePlayerProxy::UnregisterServer(); } } }