Files
UnrealEngine/Engine/Plugins/Media/BinkMedia/Source/BinkMediaPlayer/Private/BinkMovieStreamer.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

324 lines
9.1 KiB
C++

// Copyright Epic Games Tools LLC
// Licenced under the Unreal Engine EULA
#include "BinkMovieStreamer.h"
#include "BinkMediaPlayerPrivate.h"
#include "BinkMediaPlayer.h"
#include "BinkMoviePlayerSettings.h"
#include "HDRHelper.h"
#include "HAL/PlatformFileManager.h"
#include "Misc/Paths.h"
#include "RenderingThread.h"
#include "binkplugin_ue4.h"
#include "egttypes.h"
DEFINE_LOG_CATEGORY(LogBinkMoviePlayer);
FBinkMovieStreamer::FBinkMovieStreamer()
: bnk()
{
MovieViewport = MakeShareable(new FMovieViewport());
PlaybackType = MT_Normal;
}
FBinkMovieStreamer::~FBinkMovieStreamer()
{
CloseMovie();
Cleanup();
FlushRenderingCommands();
TextureFreeList.Empty();
}
bool FBinkMovieStreamer::Init(const TArray<FString>& MoviePaths, TEnumAsByte<EMoviePlaybackType> inPlaybackType)
{
if (MoviePaths.Num() == 0)
{
return false;
}
MovieIndex = -1;
PlaybackType = inPlaybackType;
StoredMoviePaths = MoviePaths;
return OpenNextMovie();
}
FString FBinkMovieStreamer::GetMovieName()
{
return StoredMoviePaths.IsValidIndex(MovieIndex) ? StoredMoviePaths[MovieIndex] : TEXT("");
}
bool FBinkMovieStreamer::IsLastMovieInPlaylist()
{
return MovieIndex == StoredMoviePaths.Num() -1;
}
void FBinkMovieStreamer::ForceCompletion()
{
CloseMovie();
}
bool FBinkMovieStreamer::Tick(FRHICommandListBase& RHICmdList, float DeltaTime)
{
// Loops through all movies while not valid
if (!bnk)
{
CloseMovie();
if (MovieIndex < StoredMoviePaths.Num() - 1)
{
OpenNextMovie();
}
else if (PlaybackType != MT_Normal)
{
MovieIndex = PlaybackType == MT_LoadingLoop ? StoredMoviePaths.Num() - 2 : -1;
OpenNextMovie();
}
else
{
return true;
}
}
BINKPLUGININFO bpinfo = {};
BinkPluginInfo(bnk, &bpinfo);
if (bpinfo.PlaybackState == 3)
{
CloseMovie();
if (MovieIndex < StoredMoviePaths.Num() - 1)
{
OpenNextMovie();
}
else if (PlaybackType != MT_Normal)
{
MovieIndex = PlaybackType == MT_LoadingLoop ? StoredMoviePaths.Num() - 2 : -1;
OpenNextMovie();
}
else
{
return true;
}
}
if (!bnk)
{
return false;
}
FSlateTexture2DRHIRef* CurrentTexture = Texture.Get();
if(CurrentTexture)
{
FVector2D destUpperLeft = GetDefault<UBinkMoviePlayerSettings>()->BinkDestinationUpperLeft;
FVector2D destLowerRight = GetDefault<UBinkMoviePlayerSettings>()->BinkDestinationLowerRight;
if( !CurrentTexture->IsInitialized() )
{
CurrentTexture->InitResource(RHICmdList);
}
FTextureRHIRef tex = CurrentTexture->GetTypedResource();
uint32 binkw = tex.GetReference()->GetSizeX();
uint32 binkh = tex.GetReference()->GetSizeY();
bool is_hdr = tex.GetReference()->GetFormat() != PF_B8G8R8A8;
float ulx = destUpperLeft.X;
float uly = destUpperLeft.Y;
float lrx = destLowerRight.X;
float lry = destLowerRight.Y;
static const auto CVarHDROutputEnabled = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.EnableHDROutput"));
static const auto CVarDisplayOutputDevice = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.OutputDevice"));
if (GRHISupportsHDROutput && CVarHDROutputEnabled->GetValueOnRenderThread() != 0)
{
EDisplayOutputFormat outDev = static_cast<EDisplayOutputFormat>(CVarDisplayOutputDevice->GetValueOnRenderThread());
float DisplayMaxLuminance = HDRGetDisplayMaximumLuminance();
switch (outDev)
{
// LDR
case EDisplayOutputFormat::SDR_sRGB:
case EDisplayOutputFormat::SDR_Rec709:
case EDisplayOutputFormat::SDR_ExplicitGammaMapping:
BinkPluginSetHdrSettings(bnk, 1, 1.0f, 80);
break;
// 1k nits
case EDisplayOutputFormat::HDR_ACES_1000nit_ST2084:
case EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB:
BinkPluginSetHdrSettings(bnk, 1, 1.0f, DisplayMaxLuminance);
break;
// 2k nits
case EDisplayOutputFormat::HDR_ACES_2000nit_ST2084:
case EDisplayOutputFormat::HDR_ACES_2000nit_ScRGB:
BinkPluginSetHdrSettings(bnk, 1, 1.0f, DisplayMaxLuminance);
break;
// no tonemap
default:
BinkPluginSetHdrSettings(bnk, 0, 1.0f, 1000);
break;
}
}
else
{
BinkPluginSetHdrSettings(bnk, 1, 1.0f, 80);
}
BinkPluginSetRenderTargetFormat(bnk, is_hdr ? 1 : 0);
BinkPluginScheduleToTexture(bnk, ulx, uly, lrx, lry, 0, tex.GetReference(), binkw, binkh);
BINKPLUGINFRAMEINFO FrameInfo = {};
FrameInfo.cmdBuf = &RHICmdList;
BinkPluginSetPerFrameInfo(&FrameInfo);
BinkPluginAllScheduled();
BinkPluginProcessBinks(0);
BinkPluginDraw(1, 0);
MovieViewport->SetTexture(Texture);
}
return false;
}
void FBinkMovieStreamer::Cleanup()
{
FlushRenderingCommands();
for( int32 TextureIndex = 0; TextureIndex < TextureFreeList.Num(); ++TextureIndex )
{
BeginReleaseResource( TextureFreeList[TextureIndex].Get() );
}
}
bool FBinkMovieStreamer::OpenNextMovie()
{
MovieIndex++;
check(StoredMoviePaths.Num() > 0 && MovieIndex < StoredMoviePaths.Num());
if (GEngine) {
BinkPluginLimitSpeakers(GetNumSpeakers());
}
BINKPLUGINBUFFERING bufferMode = (BINKPLUGINBUFFERING)(int)GetDefault<UBinkMoviePlayerSettings>()->BinkBufferMode;
BINKPLUGINSNDTRACK soundTrack = (BINKPLUGINSNDTRACK)(int)GetDefault<UBinkMoviePlayerSettings>()->BinkSoundTrack;
int soundTrackStart = GetDefault<UBinkMoviePlayerSettings>()->BinkSoundTrackStart;
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
FString MoviePathTbl[] =
{
StoredMoviePaths[MovieIndex] + TEXT(".bk2")
};
for (int i = 0; i < sizeof(MoviePathTbl) / sizeof(MoviePathTbl[0]) && !bnk; ++i)
{
FString FullMoviePath = BINKMOVIEPATH + MoviePathTbl[i];
#if PLATFORM_ANDROID
// Don't bother trying to play it if we can't find it
if (!IAndroidPlatformFile::GetPlatformPhysical().FileExists(*FullMoviePath)) {
continue;
}
int64 FileOffset = IAndroidPlatformFile::GetPlatformPhysical().FileStartOffset(*FullMoviePath);
FString FileRootPath = IAndroidPlatformFile::GetPlatformPhysical().FileRootPath(*FullMoviePath);
if (IAndroidPlatformFile::GetPlatformPhysical().IsAsset(*FullMoviePath))
{
if (JNIEnv* env = FAndroidApplication::GetJavaEnv())
{
extern struct android_app* GNativeAndroidApp;
jclass clazz = env->GetObjectClass(GNativeAndroidApp->activity->clazz);
jmethodID methodID = env->GetMethodID(clazz, "getPackageCodePath", "()Ljava/lang/String;");
jobject result = env->CallObjectMethod(GNativeAndroidApp->activity->clazz, methodID);
jboolean isCopy;
const char *apkPath = env->GetStringUTFChars((jstring)result, &isCopy);
bnk = BinkPluginOpen(apkPath, soundTrack, soundTrackStart, bufferMode, FileOffset);
env->ReleaseStringUTFChars((jstring)result, apkPath);
}
}
else
{
bnk = BinkPluginOpen(TCHAR_TO_ANSI(*FileRootPath), soundTrack, soundTrackStart, bufferMode, FileOffset);
}
#else
FString ExternalPath = PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*FullMoviePath);
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*ExternalPath))
{
#if PLATFORM_WINDOWS
bnk = BinkPluginOpenUTF16((unsigned short*)*ExternalPath, soundTrack, soundTrackStart, bufferMode, 0);
#else
bnk = BinkPluginOpen(TCHAR_TO_ANSI(*ExternalPath), soundTrack, soundTrackStart, bufferMode, 0);
#endif
}
if (!bnk)
{
FString CookPath = *BinkUE4CookOnTheFlyPath(FPaths::ConvertRelativePathToFull(BINKMOVIEPATH), *MoviePathTbl[i]);
ExternalPath = PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*FullMoviePath);
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*ExternalPath))
{
#if PLATFORM_WINDOWS
bnk = BinkPluginOpenUTF16((unsigned short *)*ExternalPath, soundTrack, soundTrackStart, bufferMode, 0);
#else
bnk = BinkPluginOpen(TCHAR_TO_ANSI(*ExternalPath), soundTrack, soundTrackStart, bufferMode, 0);
#endif
}
}
#endif
}
if (!bnk)
{
return false;
}
FIntPoint VideoDimensions = FIntPoint::ZeroValue;
BINKPLUGININFO bpinfo = {};
BinkPluginInfo(bnk, &bpinfo);
VideoDimensions.X = bpinfo.Width;
VideoDimensions.Y = bpinfo.Height;
if( VideoDimensions.X > 0 && VideoDimensions.Y > 0 )
{
EPixelFormat pixelFmt = bink_force_pixel_format == PF_Unknown ? (EPixelFormat)GetDefault<UBinkMoviePlayerSettings>()->BinkPixelFormat : bink_force_pixel_format;
if( TextureFreeList.Num() > 0 )
{
Texture = TextureFreeList.Pop();
if( Texture->GetWidth() != VideoDimensions.X || Texture->GetHeight() != VideoDimensions.Y )
{
FSlateTexture2DRHIRef *ref = Texture.Get();
ENQUEUE_RENDER_COMMAND(UpdateMovieTexture)([ref,VideoDimensions](FRHICommandListImmediate& RHICmdList)
{
ref->Resize( VideoDimensions.X, VideoDimensions.Y );
});
}
}
else
{
const bool bCreateEmptyTexture = true;
Texture = MakeShareable(new FSlateTexture2DRHIRef(VideoDimensions.X, VideoDimensions.Y, pixelFmt, NULL, TexCreate_RenderTargetable, bCreateEmptyTexture));
FSlateTexture2DRHIRef *ref = Texture.Get();
ENQUEUE_RENDER_COMMAND(InitMovieTexture)([ref](FRHICommandListImmediate& RHICmdList)
{
ref->InitResource(RHICmdList);
});
}
}
return true;
}
void FBinkMovieStreamer::CloseMovie()
{
if (GetMoviePlayer() != nullptr)
{
BroadcastCurrentMovieClipFinished(GetMovieName());
}
if (Texture.IsValid())
{
TextureFreeList.Add(Texture);
MovieViewport->SetTexture(NULL);
Texture.Reset();
}
if(bnk)
{
BinkPluginClose(bnk);
bnk = NULL;
}
}