309 lines
10 KiB
C++
309 lines
10 KiB
C++
// Copyright Epic Games Tools LLC
|
|
// Licenced under the Unreal Engine EULA
|
|
|
|
#include "BinkFunctionLibrary.h"
|
|
#include "BinkMediaPlayerPrivate.h"
|
|
#include "BinkMoviePlayerSettings.h"
|
|
#include "Engine/GameViewportClient.h"
|
|
#include "BinkMovieStreamer.h"
|
|
#include "Misc/Paths.h"
|
|
#include "RenderingThread.h"
|
|
#include "HAL/PlatformFileManager.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "binkplugin_ue4.h"
|
|
#include "egttypes.h"
|
|
|
|
DEFINE_LOG_CATEGORY(LogBink);
|
|
|
|
TSharedPtr<FBinkMovieStreamer, ESPMode::ThreadSafe> MovieStreamer;
|
|
|
|
TArray< FTextureRHIRef > BinkActiveTextureRefs;
|
|
|
|
#if BINKPLUGIN_UE4_EDITOR
|
|
class UFactory;
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "Editor.h"
|
|
#include "EditorDirectories.h"
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
#include "IDetailChildrenBuilder.h"
|
|
#include "IDetailCustomization.h"
|
|
#include "ISettingsModule.h"
|
|
#include "Interfaces/IMainFrameModule.h"
|
|
#include "PropertyCustomizationHelpers.h"
|
|
#include "SourceControlHelpers.h"
|
|
#include "Widgets/Input/SFilePathPicker.h"
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
|
|
#include "BinkMediaPlayer.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "BinkMediaPlayerModule"
|
|
|
|
struct FBinkMoviePlayerSettingsDetails : public IDetailCustomization {
|
|
static TSharedRef<IDetailCustomization> MakeInstance() { return MakeShareable(new FBinkMoviePlayerSettingsDetails); }
|
|
virtual void CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) override {
|
|
IDetailCategoryBuilder& MoviesCategory = DetailLayout.EditCategory("Movies");
|
|
|
|
StartupMoviesPropertyHandle = DetailLayout.GetProperty("StartupMovies");
|
|
|
|
TSharedRef<FDetailArrayBuilder> StartupMoviesBuilder = MakeShareable(new FDetailArrayBuilder(StartupMoviesPropertyHandle.ToSharedRef()));
|
|
StartupMoviesBuilder->OnGenerateArrayElementWidget(FOnGenerateArrayElementWidget::CreateSP(this, &FBinkMoviePlayerSettingsDetails::GenerateArrayElementWidget));
|
|
|
|
MoviesCategory.AddProperty("bWaitForMoviesToComplete");
|
|
MoviesCategory.AddProperty("bMoviesAreSkippable");
|
|
|
|
const bool bForAdvanced = false;
|
|
MoviesCategory.AddCustomBuilder(StartupMoviesBuilder, bForAdvanced);
|
|
}
|
|
|
|
void GenerateArrayElementWidget(TSharedRef<IPropertyHandle> PropertyHandle, int32 ArrayIndex, IDetailChildrenBuilder& ChildrenBuilder) {
|
|
IDetailPropertyRow& FilePathRow = ChildrenBuilder.AddProperty(PropertyHandle);
|
|
FilePathRow.CustomWidget(false)
|
|
.NameContent()
|
|
[
|
|
PropertyHandle->CreatePropertyNameWidget()
|
|
]
|
|
.ValueContent()
|
|
.MaxDesiredWidth(0.0f)
|
|
.MinDesiredWidth(125.0f)
|
|
[
|
|
SNew(SFilePathPicker)
|
|
.BrowseButtonImage(FAppStyle::GetBrush("PropertyWindow.Button_Ellipsis"))
|
|
.BrowseButtonStyle(FAppStyle::Get(), "HoverHintOnly")
|
|
.BrowseButtonToolTip(LOCTEXT("FileButtonToolTipText", "Choose a file from this computer"))
|
|
.BrowseDirectory(FEditorDirectories::Get().GetLastDirectory(ELastDirectory::GENERIC_OPEN))
|
|
.BrowseTitle(LOCTEXT("PropertyEditorTitle", "File picker..."))
|
|
.FilePath(this, &FBinkMoviePlayerSettingsDetails::HandleFilePathPickerFilePath, PropertyHandle)
|
|
.FileTypeFilter(TEXT("Bink 2 Movie (*.bk2)|*.bk2"))
|
|
.OnPathPicked(this, &FBinkMoviePlayerSettingsDetails::HandleFilePathPickerPathPicked, PropertyHandle)
|
|
];
|
|
}
|
|
|
|
FString HandleFilePathPickerFilePath( TSharedRef<IPropertyHandle> Property ) const {
|
|
FString FilePath;
|
|
Property->GetValue(FilePath);
|
|
return FilePath;
|
|
}
|
|
|
|
void HandleFilePathPickerPathPicked( const FString& PickedPath, TSharedRef<IPropertyHandle> Property ) {
|
|
const FString MoviesBaseDir = FPaths::ConvertRelativePathToFull( BINKMOVIEPATH );
|
|
const FString FullPath = FPaths::ConvertRelativePathToFull(PickedPath);
|
|
|
|
if (FullPath.StartsWith(MoviesBaseDir)) {
|
|
FText FailReason;
|
|
if (SourceControlHelpers::CheckoutOrMarkForAdd(PickedPath, LOCTEXT("MovieFileDescription", "movie"), FOnPostCheckOut(), FailReason)) {
|
|
Property->SetValue(FPaths::GetBaseFilename(FullPath.RightChop(MoviesBaseDir.Len())));
|
|
} else {
|
|
FNotificationInfo Info(FailReason);
|
|
Info.ExpireDuration = 3.0f;
|
|
FSlateNotificationManager::Get().AddNotification(Info);
|
|
}
|
|
} else if (!PickedPath.IsEmpty()) {
|
|
const FString FileName = FPaths::GetCleanFilename(PickedPath);
|
|
const FString DestPath = MoviesBaseDir / FileName;
|
|
FText FailReason;
|
|
if (SourceControlHelpers::CopyFileUnderSourceControl(DestPath, PickedPath, LOCTEXT("MovieFileDescription", "movie"), FailReason)) {
|
|
// trim the path so we just have a partial path with no extension (the movie player expects this)
|
|
Property->SetValue(FPaths::GetBaseFilename(DestPath.RightChop(MoviesBaseDir.Len())));
|
|
} else {
|
|
FNotificationInfo FailureInfo(FailReason);
|
|
FailureInfo.ExpireDuration = 3.0f;
|
|
FSlateNotificationManager::Get().AddNotification(FailureInfo);
|
|
}
|
|
} else {
|
|
Property->SetValue(PickedPath);
|
|
}
|
|
}
|
|
|
|
TSharedPtr<IPropertyHandle> StartupMoviesPropertyHandle;
|
|
};
|
|
#endif //BINKPLUGIN_UE4_EDITOR
|
|
|
|
unsigned bink_gpu_api;
|
|
unsigned bink_gpu_api_hdr;
|
|
EPixelFormat bink_force_pixel_format = PF_Unknown;
|
|
static bool bink_initialized = false;
|
|
|
|
#ifdef BINK_NDA_GPU_ALLOC
|
|
extern void* BinkAllocGpu(UINTa Amt, U32 Align);
|
|
extern void BinkFreeGpu(void* ptr, UINTa Amt);
|
|
#else
|
|
#define BinkAllocGpu 0
|
|
#define BinkFreeGpu 0
|
|
#endif
|
|
|
|
#ifdef BINK_NDA_CPU_ALLOC
|
|
extern void* BinkAllocCpu(UINTa Amt, U32 Align);
|
|
extern void BinkFreeCpu(void* ptr);
|
|
#elif defined BINK_NO_CPU_ALLOC
|
|
#define BinkAllocCpu 0
|
|
#define BinkFreeCpu 0
|
|
#else
|
|
static void *BinkAllocCpu(UINTa Amt, U32 Align) { return FMemory::Malloc(Amt, Align); }
|
|
static void BinkFreeCpu(void * ptr) { FMemory::Free(ptr); }
|
|
#endif
|
|
|
|
bool BinkInitialize() {
|
|
if (bink_initialized)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// TODO: make this an INI setting and/or configurable in Project Settings
|
|
//BinkPluginTurnOnGPUAssist();
|
|
|
|
static BINKPLUGININITINFO InitInfo = { 0 };
|
|
InitInfo.queue = 0;
|
|
InitInfo.physical_device = 0;
|
|
InitInfo.alloc = BinkAllocCpu;
|
|
InitInfo.free = BinkFreeCpu;
|
|
InitInfo.gpu_alloc = BinkAllocGpu;
|
|
InitInfo.gpu_free = BinkFreeGpu;
|
|
bink_gpu_api = BinkRHI;
|
|
|
|
bink_initialized = (bool)BinkPluginInit(0, &InitInfo, bink_gpu_api);
|
|
return bink_initialized;
|
|
}
|
|
|
|
FString BinkUE4CookOnTheFlyPath(FString path, const TCHAR *filename)
|
|
{
|
|
// If this isn't a shipping build, copy the file to our temp directory (so that cook-on-the-fly works)
|
|
FString toPath = FPaths::ConvertRelativePathToFull(BINKTEMPPATH) + filename;
|
|
FString fromPath = path + filename;
|
|
toPath = toPath.Replace(TEXT("/./"), TEXT("/"));
|
|
fromPath = fromPath.Replace(TEXT("/./"), TEXT("/"));
|
|
FPlatformFileManager::Get().GetPlatformFile().CopyFile(*toPath, *fromPath);
|
|
return fromPath;
|
|
}
|
|
|
|
struct FBinkMediaPlayerModule : IModuleInterface, FTickableGameObject
|
|
{
|
|
virtual void StartupModule() override
|
|
{
|
|
if (IsRunningCommandlet())
|
|
{
|
|
return;
|
|
}
|
|
|
|
bPluginInitialized = BinkInitialize();
|
|
|
|
if (!bPluginInitialized)
|
|
{
|
|
printf("Bink Error: %s\n", BinkPluginError());
|
|
}
|
|
|
|
MovieStreamer = MakeShareable(new FBinkMovieStreamer);
|
|
|
|
GetMoviePlayer()->RegisterMovieStreamer(MovieStreamer);
|
|
|
|
#if BINKPLUGIN_UE4_EDITOR
|
|
static ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
|
if (SettingsModule && !IsRunningGame())
|
|
{
|
|
SettingsModule->RegisterSettings("Project", "Project", "Bink Movies",
|
|
LOCTEXT("MovieSettingsName", "Bink Movies"),
|
|
LOCTEXT("MovieSettingsDescription", "Bink Movie player settings"),
|
|
GetMutableDefault<UBinkMoviePlayerSettings>()
|
|
);
|
|
|
|
//SettingsModule->RegisterViewer("Project", *this);
|
|
|
|
static IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
|
|
MainFrameModule.OnMainFrameCreationFinished().AddRaw(this, &FBinkMediaPlayerModule::Initialize);
|
|
|
|
FEditorDelegates::BeginPIE.AddRaw(this, &FBinkMediaPlayerModule::HandleEditorTogglePIE);
|
|
FEditorDelegates::EndPIE.AddRaw(this, &FBinkMediaPlayerModule::HandleEditorTogglePIE);
|
|
}
|
|
#endif
|
|
GetMutableDefault<UBinkMoviePlayerSettings>()->LoadConfig();
|
|
}
|
|
|
|
virtual void ShutdownModule() override
|
|
{
|
|
if (bPluginInitialized)
|
|
{
|
|
BinkPluginShutdown();
|
|
if (overlayHook.IsValid() && GEngine && GEngine->GameViewport)
|
|
{
|
|
GEngine->GameViewport->OnDrawn().Remove(overlayHook);
|
|
}
|
|
}
|
|
MovieStreamer.Reset();
|
|
#if BINKPLUGIN_UE4_EDITOR
|
|
FEditorDelegates::BeginPIE.RemoveAll(this);
|
|
FEditorDelegates::EndPIE.RemoveAll(this);
|
|
#endif
|
|
}
|
|
|
|
#if BINKPLUGIN_UE4_EDITOR
|
|
void Initialize(TSharedPtr<SWindow> InRootWindow, bool bIsRunningStartupDialog)
|
|
{
|
|
// This overrides the
|
|
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
PropertyModule.RegisterCustomClassLayout("MoviePlayerSettings", FOnGetDetailCustomizationInstance::CreateStatic(&FBinkMoviePlayerSettingsDetails::MakeInstance));
|
|
PropertyModule.NotifyCustomizationModuleChanged();
|
|
}
|
|
|
|
void HandleEditorTogglePIE(bool bIsSimulating)
|
|
{
|
|
for (TObjectIterator<UBinkMediaPlayer> It; It; ++It)
|
|
{
|
|
UBinkMediaPlayer* Player = *It;
|
|
Player->Close();
|
|
if(Player->StartImmediately)
|
|
{
|
|
Player->InitializePlayer();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
virtual bool IsTickable() const override { return bPluginInitialized; }
|
|
virtual bool IsTickableInEditor() const override { return true; }
|
|
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(FBinkMediaPlayerModule, STATGROUP_Tickables); }
|
|
|
|
void DrawBinks()
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(BinkProcess)([](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
BINKPLUGINFRAMEINFO FrameInfo = {};
|
|
BinkPluginSetPerFrameInfo(&FrameInfo);
|
|
BinkPluginProcessBinks(0);
|
|
BinkPluginAllScheduled();
|
|
if(BinkActiveTextureRefs.Num())
|
|
{
|
|
BinkPluginDraw(1, 0);
|
|
BinkActiveTextureRefs.Empty(256);
|
|
}
|
|
});
|
|
UBinkFunctionLibrary::Bink_DrawOverlays();
|
|
}
|
|
|
|
virtual void Tick(float DeltaTime) override
|
|
{
|
|
if (GEngine && GEngine->GameViewport)
|
|
{
|
|
if (overlayHook.IsValid())
|
|
{
|
|
GEngine->GameViewport->OnDrawn().Remove(overlayHook);
|
|
}
|
|
overlayHook = GEngine->GameViewport->OnDrawn().AddRaw(this, &FBinkMediaPlayerModule::DrawBinks);
|
|
}
|
|
else
|
|
{
|
|
DrawBinks();
|
|
}
|
|
}
|
|
|
|
FDelegateHandle overlayHook;
|
|
|
|
#if BINKPLUGIN_UE4_EDITOR
|
|
FDelegateHandle pieBeginHook;
|
|
FDelegateHandle pieEndHook;
|
|
#endif
|
|
bool bPluginInitialized = false;
|
|
};
|
|
|
|
IMPLEMENT_MODULE( FBinkMediaPlayerModule, BinkMediaPlayer )
|
|
|
|
#undef LOCTEXT_NAMESPACE
|