715 lines
20 KiB
C++
715 lines
20 KiB
C++
// Copyright Epic Games Tools LLC
|
|
// Licenced under the Unreal Engine EULA
|
|
|
|
#include "BinkMediaPlayer.h"
|
|
|
|
#include "BinkMediaPlayerPrivate.h"
|
|
|
|
#include "BinkMovieStreamer.h"
|
|
#include "Engine/GameViewportClient.h"
|
|
#include "Engine/Texture.h"
|
|
#include "Misc/Paths.h"
|
|
#include "RenderingThread.h"
|
|
#include "HDRHelper.h"
|
|
|
|
|
|
#include "HAL/PlatformFileManager.h"
|
|
#include "Internationalization/Culture.h"
|
|
#include "SubtitleManager.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "binkplugin_ue4.h"
|
|
#include "egttypes.h"
|
|
#include "RectLightTexture.h"
|
|
|
|
//#include "Media/Public/IMediaEventSink.h" //For EMediaEvent
|
|
|
|
static void Command_ShowBinks()
|
|
{
|
|
for (TObjectIterator<UBinkMediaPlayer> It; It; ++It)
|
|
{
|
|
if (!It->HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
const TCHAR* StateStr = TEXT("Unloaded");
|
|
if (It->IsInitialized())
|
|
{
|
|
if (It->IsPaused())
|
|
{
|
|
StateStr = TEXT("Paused");
|
|
}
|
|
else
|
|
{
|
|
StateStr = TEXT("Playing");
|
|
}
|
|
}
|
|
|
|
const TCHAR* DrawStyleStr = TEXT("RenderToTexture");
|
|
if (It->BinkDrawStyle != BMASM_Bink_DS_RenderToTexture)
|
|
{
|
|
DrawStyleStr = TEXT("Overlay");
|
|
}
|
|
|
|
const double DurationSeconds = It->GetDuration().GetTotalSeconds();
|
|
const double PlaybackCompletion = (DurationSeconds > 0.0) ? It->GetTime().GetTotalSeconds() / DurationSeconds : 0.0;
|
|
|
|
UE_LOG(LogBinkMoviePlayer, Display, TEXT("[%s] URL: %s, DrawStyle: %s, State: %s, PlaybackPercentage: %d%%"), *It->GetName(), *It->GetUrl(), DrawStyleStr, StateStr, static_cast<int>(PlaybackCompletion * 100.f));
|
|
}
|
|
}
|
|
}
|
|
|
|
static FAutoConsoleCommand ConsoleCmdShowBinks(
|
|
TEXT("bink.List"),
|
|
TEXT("Display all Bink Media Player objects and their current state"),
|
|
FConsoleCommandDelegate::CreateStatic(&Command_ShowBinks));
|
|
|
|
UBinkMediaPlayer::UBinkMediaPlayer( const FObjectInitializer& ObjectInitializer )
|
|
: Super(ObjectInitializer)
|
|
, Looping(true)
|
|
, StartImmediately(true)
|
|
, DelayedOpen(true)
|
|
, BinkDestinationUpperLeft(0,0)
|
|
, BinkDestinationLowerRight(1,1)
|
|
, BinkBufferMode(BMASM_Bink_Stream)
|
|
, BinkSoundTrack(BMASM_Bink_Sound_None)
|
|
, BinkSoundTrackStart(0)
|
|
, BinkDrawStyle()
|
|
, BinkLayerDepth()
|
|
, CurrentBinkBufferMode(BMASM_Bink_MAX)
|
|
, CurrentBinkSoundTrack(BMASM_Bink_Sound_MAX)
|
|
, CurrentBinkSoundTrackStart(-1)
|
|
, CurrentUrl()
|
|
, CurrentDrawStyle()
|
|
, CurrentLayerDepth()
|
|
, bnk()
|
|
, paused()
|
|
, reached_end()
|
|
{
|
|
}
|
|
|
|
bool UBinkMediaPlayer::CanPause() const { return IsPlaying(); }
|
|
bool UBinkMediaPlayer::CanPlay() const { return IsReady(); }
|
|
bool UBinkMediaPlayer::IsStopped() const { return !IsReady(); }
|
|
const FString& UBinkMediaPlayer::GetUrl() const { return CurrentUrl; }
|
|
bool UBinkMediaPlayer::Pause() { return SetRate(0.0f); }
|
|
bool UBinkMediaPlayer::Play() { return SetRate(1.0f); }
|
|
bool UBinkMediaPlayer::Rewind() { return Seek(FTimespan::Zero()); }
|
|
bool UBinkMediaPlayer::SupportsRate( float Rate, bool Unthinned ) const { return Rate == 1; }
|
|
bool UBinkMediaPlayer::SupportsScrubbing() const { return true; }
|
|
bool UBinkMediaPlayer::SupportsSeeking() const { return true; }
|
|
FString UBinkMediaPlayer::GetDesc() { return TEXT("UBinkMediaPlayer"); }
|
|
|
|
FTimespan UBinkMediaPlayer::GetDuration() const
|
|
{
|
|
double ms = 0;
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
ms = ((double)bpinfo.Frames) * ((double)bpinfo.FrameRateDiv) * 1000.0 / ((double)bpinfo.FrameRate);
|
|
}
|
|
return FTimespan::FromMilliseconds(ms);
|
|
}
|
|
|
|
float UBinkMediaPlayer::GetRate() const
|
|
{
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
return bpinfo.PlaybackState == 0 && !paused ? 1 : 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
FTimespan UBinkMediaPlayer::GetTime() const
|
|
{
|
|
double ms = 0;
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
ms = ((double)bpinfo.FrameNum) * ((double)bpinfo.FrameRateDiv) *1000.0 / ((double)bpinfo.FrameRate);
|
|
}
|
|
return FTimespan::FromMilliseconds(ms);
|
|
}
|
|
|
|
bool UBinkMediaPlayer::IsLooping() const
|
|
{
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
if (bpinfo.PlaybackState < 3 && !bpinfo.LoopsRemaining)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UBinkMediaPlayer::IsPaused() const
|
|
{
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
return bpinfo.PlaybackState == 1 || paused; // TODO: When the video is paused, the PlaybackState is 0 (should be 1).
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UBinkMediaPlayer::IsPlaying() const
|
|
{
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
return bpinfo.PlaybackState == 0 && !paused;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UBinkMediaPlayer::IsGotoing() const
|
|
{
|
|
if (bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
return bpinfo.PlaybackState == 2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UBinkMediaPlayer::OpenUrl( const FString& NewUrl )
|
|
{
|
|
if (NewUrl.IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
URL = NewUrl;
|
|
InitializePlayer();
|
|
return CurrentUrl == NewUrl;
|
|
}
|
|
|
|
void UBinkMediaPlayer::CloseUrl( )
|
|
{
|
|
URL = "";
|
|
InitializePlayer();
|
|
}
|
|
|
|
bool UBinkMediaPlayer::SetLooping( bool InLooping )
|
|
{
|
|
if(bnk)
|
|
{
|
|
BinkPluginLoop(bnk, InLooping ? 0 : 1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UBinkMediaPlayer::SetRate( float Rate )
|
|
{
|
|
if(bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
if((bpinfo.PlaybackState == 1 || paused) && Rate == 1) // If paused and set the rate to 1.0
|
|
{
|
|
BinkPluginPause(bnk, 0);
|
|
paused = false;
|
|
reached_end = false;
|
|
//MediaEvent.Broadcast(EMediaEvent::PlaybackResumed);
|
|
return true;
|
|
}
|
|
else if(bpinfo.PlaybackState == 3 && Rate == 1)
|
|
{
|
|
BinkPluginGoto(bnk, 1, -1);
|
|
BinkPluginPause(bnk, 0);
|
|
paused = false;
|
|
reached_end = false;
|
|
//MediaEvent.Broadcast(EMediaEvent::PlaybackResumed);
|
|
return true;
|
|
}
|
|
else if (bpinfo.PlaybackState == 0 && Rate == 0)
|
|
{
|
|
BinkPluginPause(bnk, -1);
|
|
paused = true;
|
|
//MediaEvent.Broadcast(EMediaEvent::PlaybackSuspended);
|
|
OnPlaybackSuspended.Broadcast();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UBinkMediaPlayer::SetVolume( float Volume )
|
|
{
|
|
if(bnk)
|
|
{
|
|
// Clamp 0 .. 1
|
|
Volume = Volume < 0 ? 0 : Volume > 1 ? 1 : Volume;
|
|
BinkPluginVolume(bnk, Volume);
|
|
}
|
|
}
|
|
|
|
bool UBinkMediaPlayer::Seek( const FTimespan& InTime )
|
|
{
|
|
if (!bnk)
|
|
{
|
|
return false;
|
|
}
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
U32 desiredFrame = (U32)(InTime.GetTotalMilliseconds() * ((double)bpinfo.FrameRate) / (1000.0*((double)bpinfo.FrameRateDiv)));
|
|
if (bpinfo.FrameNum != (desiredFrame + 1))
|
|
{
|
|
BinkPluginGoto(bnk, desiredFrame + 1, -1);
|
|
reached_end = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void UBinkMediaPlayer::BeginDestroy()
|
|
{
|
|
Super::BeginDestroy();
|
|
Close();
|
|
}
|
|
|
|
void UBinkMediaPlayer::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
if (!HasAnyFlags(RF_ClassDefaultObject) && !GIsBuildMachine && (!DelayedOpen || StartImmediately))
|
|
{
|
|
InitializePlayer();
|
|
}
|
|
}
|
|
|
|
#if BINKPLUGIN_UE4_EDITOR
|
|
|
|
void UBinkMediaPlayer::PostEditChangeProperty( FPropertyChangedEvent& PropertyChangedEvent )
|
|
{
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
InitializePlayer();
|
|
}
|
|
|
|
#endif
|
|
|
|
bool UBinkMediaPlayer::Open(const FString& Url)
|
|
{
|
|
if (!BinkInitialize())
|
|
{
|
|
UE_LOG(LogBinkMoviePlayer, Error, TEXT("UBinkMediaPlayer::Open: failed to initialize bink!"));
|
|
return false;
|
|
}
|
|
|
|
if (Url.IsEmpty())
|
|
{
|
|
UE_LOG(LogBinkMoviePlayer, Error, TEXT("UBinkMediaPlayer::Open: Failed! Url is empty."));
|
|
return false;
|
|
}
|
|
|
|
if (IsPlaying())
|
|
{
|
|
UE_LOG(LogBinkMoviePlayer, Error, TEXT("UBinkMediaPlayer::Open: Failed! Already playing."));
|
|
return false;
|
|
}
|
|
|
|
if(bnk)
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(BinkMediaPlayer_Open_CloseBink)([bnk=bnk](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
BinkPluginClose(bnk);
|
|
});
|
|
bnk = NULL;
|
|
}
|
|
|
|
BinkPluginLimitSpeakers(GetNumSpeakers());
|
|
|
|
// Use the platform file layer to open the media file. We
|
|
// need to access Bink specific information to allow
|
|
// for playing media that is embedded in the APK, OBBs,
|
|
// and/or PAKs.
|
|
|
|
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
|
|
|
#if PLATFORM_ANDROID
|
|
// Construct a canonical path for the movie.
|
|
FString MoviePath = Url;
|
|
FPaths::NormalizeFilename(MoviePath);
|
|
|
|
// Don't bother trying to play it if we can't find it.
|
|
if (!IAndroidPlatformFile::GetPlatformPhysical().FileExists(*MoviePath)) {
|
|
UE_LOG(LogBinkMoviePlayer, Error, TEXT("UBinkMediaPlayer::Open: Failed! File doesn't exist. URL :%s File :%s"), *Url, *MoviePath);
|
|
return false;
|
|
}
|
|
|
|
// Get information about the movie.
|
|
int64 FileOffset = IAndroidPlatformFile::GetPlatformPhysical().FileStartOffset(*MoviePath);
|
|
FString FileRootPath = IAndroidPlatformFile::GetPlatformPhysical().FileRootPath(*MoviePath);
|
|
|
|
// Play the movie as a file or asset.
|
|
if (IAndroidPlatformFile::GetPlatformPhysical().IsAsset(*MoviePath))
|
|
{
|
|
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, BinkSoundTrack, BinkSoundTrackStart, BinkBufferMode, FileOffset);
|
|
env->ReleaseStringUTFChars((jstring)result, apkPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bnk = BinkPluginOpen(TCHAR_TO_ANSI(*FileRootPath), BinkSoundTrack, BinkSoundTrackStart, BinkBufferMode, FileOffset);
|
|
}
|
|
#else
|
|
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*Url))
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
bnk = BinkPluginOpenUTF16((unsigned short *)*PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*Url), BinkSoundTrack, BinkSoundTrackStart, BinkBufferMode, 0);
|
|
#else
|
|
bnk = BinkPluginOpen(TCHAR_TO_ANSI(*PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*Url)), BinkSoundTrack, BinkSoundTrackStart, BinkBufferMode, 0);
|
|
#endif
|
|
}
|
|
#endif
|
|
if(!bnk)
|
|
{
|
|
//MediaEvent.Broadcast(EMediaEvent::MediaOpenFailed);
|
|
UE_LOG(LogBinkMoviePlayer, Error, TEXT("UBinkMediaPlayer::Open: Failed! BinkPluginOpen failed. Invalid bnk."));
|
|
return false;
|
|
}
|
|
|
|
// Try to open subtitles...
|
|
{
|
|
// Determine what out current culture is, and grab the most appropriate set of subtitles for it
|
|
FInternationalization& Internationalization = FInternationalization::Get();
|
|
|
|
const TArray<FString> PrioritizedLanguageNames = Internationalization.GetPrioritizedCultureNames(Internationalization.GetCurrentLanguage()->GetName());
|
|
|
|
CurrentHasSubtitles = 0;
|
|
for (const FString& LanguageName : PrioritizedLanguageNames)
|
|
{
|
|
int32 Pos = INDEX_NONE;
|
|
if (Url.FindLastChar(TEXT('.'), Pos))
|
|
{
|
|
const int32 PathEndPos = Url.FindLastCharByPredicate([](TCHAR C) { return C == TEXT('/') || C == TEXT('\\'); });
|
|
if (PathEndPos != INDEX_NONE && PathEndPos > Pos)
|
|
{
|
|
// The dot found was part of the path rather than the name
|
|
Pos = INDEX_NONE;
|
|
}
|
|
}
|
|
FString SubtitleUrl = (Pos == INDEX_NONE ? Url : Url.Left(Pos)) + "_" + LanguageName + ".srt";
|
|
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*SubtitleUrl))
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
if (BinkPluginLoadSubtitlesUTF16(bnk, (unsigned short*)*PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*SubtitleUrl)))
|
|
#else
|
|
if (BinkPluginLoadSubtitles(bnk, TCHAR_TO_ANSI(*PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*SubtitleUrl))))
|
|
#endif
|
|
{
|
|
CurrentHasSubtitles = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
paused = false;
|
|
reached_end = false;
|
|
HandleMediaPlayerMediaOpened(Url);
|
|
return true;
|
|
}
|
|
|
|
void UBinkMediaPlayer::Close()
|
|
{
|
|
if(bnk)
|
|
{
|
|
if (CurrentHasSubtitles)
|
|
{
|
|
CurrentHasSubtitles = 0;
|
|
|
|
// Clear the movie subtitle for this object
|
|
TArray<FString> StopSubtitles = { TEXT("") };
|
|
FSubtitleManager::GetSubtitleManager()->SetMovieSubtitle(this, StopSubtitles);
|
|
}
|
|
|
|
ENQUEUE_RENDER_COMMAND(BinkMediaPlayer_Close_CloseBink)([bnk=bnk](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
BinkPluginClose(bnk);
|
|
});
|
|
bnk = NULL;
|
|
}
|
|
CurrentUrl = FString();
|
|
HandleMediaPlayerMediaClosed();
|
|
}
|
|
|
|
void UBinkMediaPlayer::InitializePlayer()
|
|
{
|
|
if (URL != CurrentUrl
|
|
|| BinkBufferMode != CurrentBinkBufferMode
|
|
|| BinkSoundTrack != CurrentBinkSoundTrack
|
|
|| BinkSoundTrackStart != CurrentBinkSoundTrackStart
|
|
|| BinkDrawStyle != CurrentDrawStyle
|
|
|| BinkLayerDepth != CurrentLayerDepth )
|
|
{
|
|
Close();
|
|
|
|
if (URL.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// open the new media file
|
|
bool OpenedSuccessfully = false;
|
|
|
|
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
|
FString FullUrl = FPaths::ConvertRelativePathToFull(FPaths::IsRelative(URL) ? BINKCONTENTPATH / URL : URL);
|
|
OpenedSuccessfully = Open(FullUrl);
|
|
if (!OpenedSuccessfully)
|
|
{
|
|
FString cookpath = BinkUE4CookOnTheFlyPath(FPaths::ConvertRelativePathToFull(BINKCONTENTPATH), *URL);
|
|
OpenedSuccessfully = Open(cookpath);
|
|
}
|
|
|
|
// finish initialization
|
|
if (OpenedSuccessfully)
|
|
{
|
|
CurrentUrl = URL;
|
|
CurrentBinkBufferMode = BinkBufferMode;
|
|
CurrentBinkSoundTrack = BinkSoundTrack;
|
|
CurrentBinkSoundTrackStart = BinkSoundTrackStart;
|
|
CurrentDrawStyle = BinkDrawStyle;
|
|
CurrentLayerDepth = BinkLayerDepth;
|
|
}
|
|
}
|
|
|
|
SetLooping(Looping);
|
|
SetRate(StartImmediately ? 1 : 0);
|
|
}
|
|
|
|
void UBinkMediaPlayer::HandleMediaPlayerMediaClosed()
|
|
{
|
|
MediaChangedEvent.Broadcast();
|
|
OnMediaClosed.Broadcast();
|
|
//MediaEvent.Broadcast(EMediaEvent::MediaClosed);
|
|
}
|
|
|
|
void UBinkMediaPlayer::HandleMediaPlayerMediaOpened( FString OpenedUrl )
|
|
{
|
|
MediaChangedEvent.Broadcast();
|
|
OnMediaOpened.Broadcast(OpenedUrl);
|
|
//MediaEvent.Broadcast(EMediaEvent::MediaOpened);
|
|
}
|
|
|
|
void UBinkMediaPlayer::Tick(float DeltaTime)
|
|
{
|
|
// Check for if we should issue the reached end event
|
|
if (bnk && !reached_end && !IsPlaying() && !IsGotoing() && !IsPaused() && !IsLooping())
|
|
{
|
|
OnMediaReachedEnd.Broadcast();
|
|
reached_end = true;
|
|
//MediaEvent.Broadcast(EMediaEvent::PlaybackEndReached);
|
|
}
|
|
if (bnk && GEngine && GEngine->GameViewport)
|
|
{
|
|
if (!IsPlaying() && !IsPaused())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FVector2D screenSize;
|
|
GEngine->GameViewport->GetViewportSize(screenSize);
|
|
if (CurrentHasSubtitles == 1)
|
|
{
|
|
TArray<FString> SubtitlesText;
|
|
unsigned i = 0;
|
|
while(const char *sub = BinkPluginCurrentSubtitle(bnk, &i))
|
|
{
|
|
SubtitlesText.Add((UTF8CHAR*)sub);
|
|
}
|
|
|
|
FSubtitleManager::GetSubtitleManager()->SetMovieSubtitle(this, SubtitlesText);
|
|
}
|
|
|
|
if (BinkDrawStyle != 0)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
int binkw = bpinfo.Width;
|
|
int binkh = bpinfo.Height;
|
|
|
|
float ulx = BinkDestinationUpperLeft.X;
|
|
float uly = BinkDestinationUpperLeft.Y;
|
|
float lrx = BinkDestinationLowerRight.X;
|
|
float lry = BinkDestinationLowerRight.Y;
|
|
|
|
// figure out the x,y screencoords for all of the overlay types
|
|
if (BinkDrawStyle == 1/*BNK_DS_OverlayFillScreenWithAspectRatio*/)
|
|
{
|
|
lrx = binkw / screenSize.X;
|
|
lry = binkh / screenSize.Y;
|
|
|
|
if (lrx > lry)
|
|
{
|
|
lry /= lrx;
|
|
lrx = 1;
|
|
}
|
|
else
|
|
{
|
|
lrx /= lry;
|
|
lry = 1;
|
|
}
|
|
ulx = (1.0f - lrx) / 2.0f;
|
|
uly = (1.0f - lry) / 2.0f;
|
|
lrx += ulx;
|
|
lry += uly;
|
|
}
|
|
else if (BinkDrawStyle == 2/*BNK_DS_OverlayOriginalMovieSize*/)
|
|
{
|
|
ulx = (screenSize.X - binkw) / (2.0f * screenSize.X);
|
|
uly = (screenSize.Y - binkh) / (2.0f * screenSize.Y);
|
|
lrx = binkw / screenSize.X + ulx;
|
|
lry = binkh / screenSize.Y + uly;
|
|
}
|
|
|
|
#if PLATFORM_ANDROID
|
|
uly = 1 - uly;
|
|
lry = 1 - lry;
|
|
#endif
|
|
|
|
ENQUEUE_RENDER_COMMAND(BinkScheduleOverlay)([bnk=bnk,ulx,uly,lrx,lry](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
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:
|
|
BinkPluginSetHdrSettings(bnk, 2, 1.0f, DisplayMaxLuminance);
|
|
break;
|
|
case EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB:
|
|
BinkPluginSetHdrSettings(bnk, 1, 1.0f, DisplayMaxLuminance);
|
|
break;
|
|
// 2k nits
|
|
case EDisplayOutputFormat::HDR_ACES_2000nit_ST2084:
|
|
BinkPluginSetHdrSettings(bnk, 2, 1.0f, DisplayMaxLuminance);
|
|
break;
|
|
case EDisplayOutputFormat::HDR_ACES_2000nit_ScRGB:
|
|
BinkPluginSetHdrSettings(bnk, 1, 1.0f, DisplayMaxLuminance);
|
|
break;
|
|
// no tonemap
|
|
default:
|
|
BinkPluginSetHdrSettings(bnk, 0, 1.0f, 1000);
|
|
break;
|
|
}
|
|
BinkPluginSetRenderTargetFormat(bnk, 1);
|
|
}
|
|
else
|
|
{
|
|
BinkPluginSetHdrSettings(bnk, 1, 1.0f, 80);
|
|
BinkPluginSetRenderTargetFormat(bnk, 0);
|
|
}
|
|
BinkPluginSetDrawFlags(bnk, 0);
|
|
BinkPluginScheduleOverlay(bnk, ulx, uly, lrx, lry, 0);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void UBinkMediaPlayer::UpdateTexture(const UTexture *tex, FRHICommandListImmediate &RHICmdList, FTextureRHIRef ref, void *nativePtr, int width, int height, bool isEditor, bool tonemap, int output_nits, float alpha, bool srgb_decode, bool is_hdr)
|
|
{
|
|
check(IsInRenderingThread());
|
|
|
|
if (bnk && (BinkDrawStyle == 0 || isEditor))
|
|
{
|
|
BinkPluginSetHdrSettings(bnk, tonemap, 1.0f, output_nits);
|
|
BinkPluginSetAlphaSettings(bnk, alpha);
|
|
BinkPluginSetDrawFlags(bnk, srgb_decode ? BinkDrawDecodeSRGB : 0);
|
|
BinkPluginSetRenderTargetFormat(bnk, is_hdr ? 1 : 0);
|
|
BinkPluginScheduleToTexture(bnk, BinkDestinationUpperLeft.X, BinkDestinationUpperLeft.Y, BinkDestinationLowerRight.X, BinkDestinationLowerRight.Y, 0, ref.GetReference(), width, height);
|
|
BinkActiveTextureRefs.Push(ref);
|
|
|
|
// Update rect-lights with this texture
|
|
if (tex)
|
|
{
|
|
// Lock/Enqueue rect atlas refresh if that texture is used by a rect. light
|
|
RectLightAtlas::FAtlasTextureInvalidationScope InvalidationScope(tex);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UBinkMediaPlayer::Draw(UTexture *texture, bool tonemap, int out_nits, float alpha, bool srgb_decode, bool hdr)
|
|
{
|
|
if (!bnk || !texture->GetResource())
|
|
{
|
|
return;
|
|
}
|
|
FTextureRHIRef ref = texture->GetResource()->TextureRHI->GetTexture2D();
|
|
if ((!IsPlaying() && !IsPaused()) || !ref)
|
|
{
|
|
return;
|
|
}
|
|
int width = texture->GetSurfaceWidth();
|
|
int height = texture->GetSurfaceHeight();
|
|
void *native = ref->GetNativeResource();
|
|
if (!native)
|
|
{
|
|
texture->UpdateResource();
|
|
return;
|
|
}
|
|
|
|
struct parms_t
|
|
{
|
|
UBinkMediaPlayer *player;
|
|
UTexture* tex;
|
|
FTextureRHIRef ref;
|
|
void *native;
|
|
int width, height;
|
|
bool tonemap;
|
|
bool srgb_decode;
|
|
bool hdr;
|
|
int out_nits;
|
|
float alpha;
|
|
} parms = { this, texture, ref, native, width, height, tonemap, srgb_decode, hdr, out_nits, alpha };
|
|
ENQUEUE_RENDER_COMMAND(BinkMediaPlayer_Draw)([parms](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
parms.player->UpdateTexture(parms.tex, RHICmdList, parms.ref, parms.native, parms.width, parms.height, false, parms.tonemap, parms.out_nits, parms.alpha, parms.srgb_decode, parms.hdr);
|
|
});
|
|
}
|
|
|
|
FIntPoint UBinkMediaPlayer::GetDimensions() const
|
|
{
|
|
if (bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
return FIntPoint(bpinfo.Width, bpinfo.Height);
|
|
}
|
|
return FIntPoint(0, 0);
|
|
}
|
|
|
|
float UBinkMediaPlayer::GetFrameRate() const
|
|
{
|
|
if (bnk)
|
|
{
|
|
BINKPLUGININFO bpinfo = {};
|
|
BinkPluginInfo(bnk, &bpinfo);
|
|
return (float)(((double)bpinfo.FrameRate) / ((double)bpinfo.FrameRateDiv));
|
|
}
|
|
return 0;
|
|
}
|