2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-02-27 11:57:17 -05:00
# include "GameplayMediaEncoder.h"
# include "Engine/GameEngine.h"
# include "HAL/IConsoleManager.h"
# include "Framework/Application/SlateApplication.h"
# include "Modules/ModuleManager.h"
# include "RendererInterface.h"
# include "ScreenRendering.h"
# include "ShaderCore.h"
# include "PipelineStateCache.h"
# include "ProfilingDebugging/CsvProfiler.h"
# include "IbmLiveStreaming.h"
2021-04-29 19:32:06 -04:00
# include "VideoEncoderFactory.h"
# include "VideoEncoderInput.h"
# include "ClearQuad.h"
# include "CommonRenderResources.h"
# include "AudioEncoderFactory.h"
2019-02-27 11:57:17 -05:00
DEFINE_LOG_CATEGORY ( GameplayMediaEncoder ) ;
CSV_DEFINE_CATEGORY ( GameplayMediaEncoder , true ) ;
2021-10-26 09:13:01 -04:00
// right now we support only 48KHz audio sample rate as it's the only config UE4 seems to output
2019-02-27 11:57:17 -05:00
// WMF AAC encoder supports also 44100Hz so its support can be easily added
const uint32 HardcodedAudioSamplerate = 48000 ;
// for now we downsample to stereo. WMF AAC encoder also supports 6 (5.1) channels
// so it can be added too
const uint32 HardcodedAudioNumChannels = 2 ;
// currently neither IVideoRecordingSystem neither HighlightFeature APIs allow to configure
// audio stream parameters
2019-11-26 17:28:51 -05:00
const uint32 HardcodedAudioBitrate = 192000 ;
2019-02-27 11:57:17 -05:00
// currently neither IVideoRecordingSystem neither HighlightFeature APIs allow to configure
// video stream parameters
# if PLATFORM_WINDOWS
const uint32 HardcodedVideoFPS = 60 ;
# else
const uint32 HardcodedVideoFPS = 30 ;
# endif
2021-06-22 00:27:54 -04:00
const uint32 HardcodedVideoBitrate = 20000000 ;
2019-02-27 11:57:17 -05:00
const uint32 MinVideoBitrate = 1000000 ;
const uint32 MaxVideoBitrate = 20000000 ;
const uint32 MinVideoFPS = 10 ;
const uint32 MaxVideoFPS = 60 ;
const uint32 MaxWidth = 1920 ;
const uint32 MaxHeight = 1080 ;
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FAutoConsoleCommand GameplayMediaEncoderInitialize ( TEXT ( " GameplayMediaEncoder.Initialize " ) , TEXT ( " Constructs the audio/video encoding objects. Does not start encoding " ) , FConsoleCommandDelegate : : CreateStatic ( & FGameplayMediaEncoder : : InitializeCmd ) ) ;
2019-02-27 11:57:17 -05:00
2021-10-26 09:13:01 -04:00
FAutoConsoleCommand GameplayMediaEncoderStart ( TEXT ( " GameplayMediaEncoder.Start " ) , TEXT ( " Starts encoding " ) , FConsoleCommandDelegate : : CreateStatic ( & FGameplayMediaEncoder : : StartCmd ) ) ;
2019-02-27 11:57:17 -05:00
2021-10-26 09:13:01 -04:00
FAutoConsoleCommand GameplayMediaEncoderStop ( TEXT ( " GameplayMediaEncoder.Stop " ) , TEXT ( " Stops encoding " ) , FConsoleCommandDelegate : : CreateStatic ( & FGameplayMediaEncoder : : StopCmd ) ) ;
2019-02-27 11:57:17 -05:00
2021-10-26 09:13:01 -04:00
FAutoConsoleCommand GameplayMediaEncoderShutdown ( TEXT ( " GameplayMediaEncoder.Shutdown " ) , TEXT ( " Releases all systems. " ) , FConsoleCommandDelegate : : CreateStatic ( & FGameplayMediaEncoder : : ShutdownCmd ) ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
//////////////////////////////////////////////////////////////////////////
//
// FGameplayMediaEncoder
//
//////////////////////////////////////////////////////////////////////////
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2023-08-16 14:28:25 -04:00
TSharedPtr < FGameplayMediaEncoder , ESPMode : : ThreadSafe > FGameplayMediaEncoder : : Singleton = { } ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
FGameplayMediaEncoder * FGameplayMediaEncoder : : Get ( )
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
{
2023-08-16 14:28:25 -04:00
// Constructed and captured as a thread safe shared pointer as this is required by the audio submix listener interface
if ( ! Singleton . IsValid ( ) )
2019-11-26 17:28:51 -05:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2023-08-16 14:28:25 -04:00
Singleton = MakeShared < FGameplayMediaEncoder > ( ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
}
2024-03-26 02:13:50 -04:00
2023-08-16 14:28:25 -04:00
return Singleton . Get ( ) ;
2019-02-27 11:57:17 -05:00
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
FGameplayMediaEncoder : : FGameplayMediaEncoder ( ) { }
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
FGameplayMediaEncoder : : ~ FGameplayMediaEncoder ( ) { Shutdown ( ) ; }
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
bool FGameplayMediaEncoder : : RegisterListener ( IGameplayMediaEncoderListener * Listener )
{
check ( IsInGameThread ( ) ) ;
FScopeLock Lock ( & ListenersCS ) ;
2021-10-26 09:13:01 -04:00
if ( Listeners . Num ( ) = = 0 )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Registering the first listener " ) ) ;
2021-10-26 09:13:01 -04:00
if ( ! Start ( ) )
2019-02-27 11:57:17 -05:00
{
return false ;
}
}
Listeners . AddUnique ( Listener ) ;
return true ;
}
void FGameplayMediaEncoder : : UnregisterListener ( IGameplayMediaEncoderListener * Listener )
{
check ( IsInGameThread ( ) ) ;
2019-04-26 05:52:24 -04:00
ListenersCS . Lock ( ) ;
2019-02-27 11:57:17 -05:00
Listeners . Remove ( Listener ) ;
2019-04-26 05:52:24 -04:00
bool bAnyListenersLeft = Listeners . Num ( ) > 0 ;
ListenersCS . Unlock ( ) ;
2021-10-26 09:13:01 -04:00
if ( bAnyListenersLeft = = false )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Unregistered the last listener " ) ) ;
Stop ( ) ;
}
}
bool FGameplayMediaEncoder : : Initialize ( )
{
MemoryCheckpoint ( " Initial " ) ;
2021-10-26 09:13:01 -04:00
if ( VideoEncoder )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Already initialized " ) ) ;
return true ;
}
2019-11-26 17:28:51 -05:00
// If some error occurs, call Shutdown to cleanup
bool bIsOk = false ;
ON_SCOPE_EXIT
2019-02-27 11:57:17 -05:00
{
2021-10-26 09:13:01 -04:00
if ( ! bIsOk )
2019-11-26 17:28:51 -05:00
{
Shutdown ( ) ;
}
2019-02-27 11:57:17 -05:00
} ;
//
// Audio
2019-11-26 17:28:51 -05:00
//
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
AVEncoder : : FAudioEncoderFactory * AudioEncoderFactory = AVEncoder : : FAudioEncoderFactory : : FindFactory ( " aac " ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
if ( ! AudioEncoderFactory )
2019-11-26 17:28:51 -05:00
{
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " No audio encoder for aac found " ) ) ;
return false ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
AudioEncoder = AudioEncoderFactory - > CreateEncoder ( " aac " ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
if ( ! AudioEncoder )
2019-11-26 17:28:51 -05:00
{
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " Could not create audio encoder " ) ) ;
return false ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
AVEncoder : : FAudioConfig AudioConfig ;
2019-11-26 17:28:51 -05:00
AudioConfig . Samplerate = HardcodedAudioSamplerate ;
2019-02-27 11:57:17 -05:00
AudioConfig . NumChannels = HardcodedAudioNumChannels ;
AudioConfig . Bitrate = HardcodedAudioBitrate ;
2021-10-26 09:13:01 -04:00
if ( ! AudioEncoder - > Initialize ( AudioConfig ) )
2019-02-27 11:57:17 -05:00
{
2019-11-26 17:28:51 -05:00
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " Could not initialize audio encoder " ) ) ;
2019-02-27 11:57:17 -05:00
return false ;
}
2019-11-26 17:28:51 -05:00
AudioEncoder - > RegisterListener ( * this ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
2019-02-27 11:57:17 -05:00
MemoryCheckpoint ( " Audio encoder initialized " ) ;
//
// Video
//
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
VideoConfig . Codec = " h264 " ;
VideoConfig . Height = VideoConfig . Width = VideoConfig . Framerate = VideoConfig . Bitrate = 0 ;
2019-02-27 11:57:17 -05:00
FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " GameplayMediaEncoder.ResY= " ) , VideoConfig . Height ) ;
2021-04-29 19:32:06 -04:00
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " GameplayMediaEncoder.ResY = %d " ) , VideoConfig . Height ) ;
2021-10-26 09:13:01 -04:00
if ( VideoConfig . Height = = 0 | | VideoConfig . Height = = 720 )
2019-02-27 11:57:17 -05:00
{
VideoConfig . Width = 1280 ;
VideoConfig . Height = 720 ;
}
2021-10-26 09:13:01 -04:00
else if ( VideoConfig . Height = = 1080 )
2019-02-27 11:57:17 -05:00
{
VideoConfig . Width = 1920 ;
VideoConfig . Height = 1080 ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
else
{
UE_LOG ( GameplayMediaEncoder , Fatal , TEXT ( " GameplayMediaEncoder.ResY can only have a value of 720 or 1080 " ) ) ;
return false ;
}
// Specifying 0 will completely disable frame skipping (therefore encoding as many frames as possible)
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " GameplayMediaEncoder.FPS= " ) , VideoConfig . Framerate ) ;
2021-10-26 09:13:01 -04:00
if ( VideoConfig . Framerate = = 0 )
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
{
// Note : When disabling frame skipping, we lie to the encoder when initializing.
// We still specify a framerate, but then feed frames without skipping
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
VideoConfig . Framerate = HardcodedVideoFPS ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
bDoFrameSkipping = false ;
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Uncapping FPS " ) ) ;
}
else
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
VideoConfig . Framerate = FMath : : Clamp ( VideoConfig . Framerate , ( uint32 ) MinVideoFPS , ( uint32 ) MaxVideoFPS ) ;
2024-03-26 02:04:41 -04:00
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Capping FPS %u " ) , VideoConfig . Framerate ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
bDoFrameSkipping = true ;
2019-02-27 11:57:17 -05:00
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
VideoConfig . Bitrate = HardcodedVideoBitrate ;
FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " GameplayMediaEncoder.Bitrate= " ) , VideoConfig . Bitrate ) ;
VideoConfig . Bitrate = FMath : : Clamp ( VideoConfig . Bitrate , ( uint32 ) MinVideoBitrate , ( uint32 ) MaxVideoBitrate ) ;
2021-05-25 02:43:26 -04:00
AVEncoder : : FVideoEncoder : : FLayerConfig videoInit ;
2021-04-29 19:32:06 -04:00
videoInit . Width = VideoConfig . Width ;
videoInit . Height = VideoConfig . Height ;
videoInit . MaxBitrate = MaxVideoBitrate ;
videoInit . TargetBitrate = VideoConfig . Bitrate ;
videoInit . MaxFramerate = VideoConfig . Framerate ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
2021-10-26 09:13:01 -04:00
if ( GDynamicRHI )
2019-11-26 17:28:51 -05:00
{
2022-02-24 11:58:36 -05:00
const ERHIInterfaceType RHIType = RHIGetInterfaceType ( ) ;
2021-04-29 19:32:06 -04:00
2022-01-27 17:52:14 -05:00
# if PLATFORM_DESKTOP && !PLATFORM_APPLE
2022-02-24 11:58:36 -05:00
if ( RHIType = = ERHIInterfaceType : : D3D11 )
2021-10-26 09:13:01 -04:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
VideoEncoderInput = AVEncoder : : FVideoEncoderInput : : CreateForD3D11 ( GDynamicRHI - > RHIGetNativeDevice ( ) , true , IsRHIDeviceAMD ( ) ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
}
2024-03-26 02:13:50 -04:00
2022-02-24 11:58:36 -05:00
else if ( RHIType = = ERHIInterfaceType : : D3D12 )
2021-10-26 09:13:01 -04:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
VideoEncoderInput = AVEncoder : : FVideoEncoderInput : : CreateForD3D12 ( GDynamicRHI - > RHIGetNativeDevice ( ) , true , IsRHIDeviceNVIDIA ( ) ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
}
2024-03-26 02:13:50 -04:00
2022-02-24 11:58:36 -05:00
else if ( RHIType = = ERHIInterfaceType : : Vulkan )
2022-01-27 17:52:14 -05:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
VideoEncoderInput = AVEncoder : : FVideoEncoderInput : : CreateForVulkan ( GDynamicRHI - > RHIGetNativeDevice ( ) , true ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2022-01-27 17:52:14 -05:00
}
2024-03-26 02:13:50 -04:00
2021-06-22 00:27:54 -04:00
else
# endif
2021-10-26 09:13:01 -04:00
{
2022-01-27 17:52:14 -05:00
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " Video encoding is not supported with the current Platform/RHI combo. " ) ) ;
return false ;
2021-10-26 09:13:01 -04:00
}
2019-11-26 17:28:51 -05:00
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-01-27 17:52:14 -05:00
const TArray < AVEncoder : : FVideoEncoderInfo > & AvailableEncodersInfo = AVEncoder : : FVideoEncoderFactory : : Get ( ) . GetAvailable ( ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2022-01-27 17:52:14 -05:00
if ( AvailableEncodersInfo . Num ( ) = = 0 )
{
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " No video encoders found. Check if relevent encoder plugins have been enabled for this project. " ) ) ;
return false ;
}
for ( const auto & EncoderInfo : AvailableEncodersInfo )
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-01-27 17:52:14 -05:00
if ( EncoderInfo . CodecType = = AVEncoder : : ECodecType : : H264 )
{
VideoEncoder = AVEncoder : : FVideoEncoderFactory : : Get ( ) . Create ( EncoderInfo . ID , VideoEncoderInput , videoInit ) ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2022-01-27 17:52:14 -05:00
}
if ( ! VideoEncoder )
{
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " No H264 video encoder found. Check if relevent encoder plugins have been enabled for this project. " ) ) ;
return false ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
VideoEncoder - > SetOnEncodedPacket ( [ this ] ( uint32 LayerIndex , const TSharedPtr < AVEncoder : : FVideoEncoderInputFrame > Frame , const AVEncoder : : FCodecPacket & Packet )
2021-10-26 09:13:01 -04:00
{ OnEncodedVideoFrame ( LayerIndex , Frame , Packet ) ; } ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
if ( ! VideoEncoder )
2019-11-26 17:28:51 -05:00
{
2022-01-27 17:52:14 -05:00
UE_LOG ( GameplayMediaEncoder , Fatal , TEXT ( " Creating video encoder failed. " ) ) ;
2019-11-26 17:28:51 -05:00
return false ;
}
2019-02-27 11:57:17 -05:00
MemoryCheckpoint ( " Video encoder initialized " ) ;
2019-11-26 17:28:51 -05:00
bIsOk = true ; // So Shutdown is not called due to the ON_SCOPE_EXIT
2019-02-27 11:57:17 -05:00
return true ;
}
bool FGameplayMediaEncoder : : Start ( )
{
2021-10-26 09:13:01 -04:00
if ( StartTime ! = 0 )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Already running " ) ) ;
return true ;
}
2021-10-26 09:13:01 -04:00
if ( ! VideoEncoder )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Not initialized yet , so also performing a Intialize() " ) ) ;
2021-10-26 09:13:01 -04:00
if ( ! Initialize ( ) )
2019-02-27 11:57:17 -05:00
{
return false ;
}
}
2021-04-29 19:32:06 -04:00
StartTime = FTimespan : : FromSeconds ( FPlatformTime : : Seconds ( ) ) ;
2019-09-26 07:27:04 -04:00
AudioClock = 0 ;
2019-02-27 11:57:17 -05:00
NumCapturedFrames = 0 ;
2021-10-26 09:13:01 -04:00
2019-02-27 11:57:17 -05:00
//
// subscribe to engine delegates for audio output and back buffer
//
2020-01-30 18:48:52 -05:00
FAudioDeviceHandle AudioDevice = GEngine - > GetMainAudioDevice ( ) ;
2021-10-26 09:13:01 -04:00
if ( AudioDevice )
2019-05-24 09:28:17 -04:00
{
2019-11-26 17:28:51 -05:00
bAudioFormatChecked = false ;
2023-08-16 14:28:25 -04:00
AudioDevice - > RegisterSubmixBufferListener ( AsShared ( ) , AudioDevice - > GetMainSubmixObject ( ) ) ;
2019-05-24 09:28:17 -04:00
}
2019-06-06 15:04:34 -04:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
FSlateApplication : : Get ( ) . GetRenderer ( ) - > OnBackBufferReadyToPresent ( ) . AddRaw ( this , & FGameplayMediaEncoder : : OnFrameBufferReady ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-06-06 15:04:34 -04:00
return true ;
2019-02-27 11:57:17 -05:00
}
void FGameplayMediaEncoder : : Stop ( )
{
check ( IsInGameThread ( ) ) ;
2021-10-26 09:13:01 -04:00
if ( StartTime = = 0 )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Not running " ) ) ;
return ;
}
2021-10-26 09:13:01 -04:00
if ( UGameEngine * GameEngine = Cast < UGameEngine > ( GEngine ) )
2019-02-27 11:57:17 -05:00
{
2023-08-16 14:28:25 -04:00
if ( FAudioDevice * AudioDevice = GameEngine - > GetMainAudioDeviceRaw ( ) )
2019-02-27 11:57:17 -05:00
{
2023-08-16 14:28:25 -04:00
AudioDevice - > UnregisterSubmixBufferListener ( AsShared ( ) , AudioDevice - > GetMainSubmixObject ( ) ) ;
2019-02-27 11:57:17 -05:00
}
2021-10-26 09:13:01 -04:00
if ( FSlateApplication : : IsInitialized ( ) )
2019-02-27 11:57:17 -05:00
{
2019-03-07 11:25:32 -05:00
FSlateApplication : : Get ( ) . GetRenderer ( ) - > OnBackBufferReadyToPresent ( ) . RemoveAll ( this ) ;
2019-02-27 11:57:17 -05:00
}
}
StartTime = 0 ;
2019-09-26 07:27:04 -04:00
AudioClock = 0 ;
2019-02-27 11:57:17 -05:00
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
AVEncoder : : FAudioConfig FGameplayMediaEncoder : : GetAudioConfig ( ) const
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-10-26 09:13:01 -04:00
if ( AudioEncoder )
2021-04-29 19:32:06 -04:00
{
auto codec = AudioEncoder - > GetType ( ) ;
auto config = AudioEncoder - > GetConfig ( ) ;
2021-10-26 09:13:01 -04:00
return { codec , config . Samplerate , config . NumChannels , config . Bitrate } ;
2021-04-29 19:32:06 -04:00
}
else
{
return { } ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
}
2019-02-27 11:57:17 -05:00
void FGameplayMediaEncoder : : Shutdown ( )
{
2021-10-26 09:13:01 -04:00
if ( StartTime ! = 0 )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Log , TEXT ( " Currently running, so also performing a Stop() " ) ) ;
Stop ( ) ;
}
{
FScopeLock Lock ( & AudioProcessingCS ) ;
2021-10-26 09:13:01 -04:00
if ( AudioEncoder )
2019-11-26 17:28:51 -05:00
{
2021-10-26 09:13:01 -04:00
// AudioEncoder->Reset();
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
AudioEncoder - > Shutdown ( ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
AudioEncoder . Reset ( ) ;
}
2019-02-27 11:57:17 -05:00
}
{
FScopeLock Lock ( & VideoProcessingCS ) ;
2021-10-26 09:13:01 -04:00
if ( VideoEncoder )
2019-02-27 11:57:17 -05:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
VideoEncoder - > Shutdown ( ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
VideoEncoder . Reset ( ) ;
2021-04-29 19:32:06 -04:00
2021-06-22 00:27:54 -04:00
BackBuffers . Empty ( ) ;
2019-02-27 11:57:17 -05:00
}
}
}
2021-10-26 09:13:01 -04:00
FTimespan FGameplayMediaEncoder : : GetMediaTimestamp ( ) const { return FTimespan : : FromSeconds ( FPlatformTime : : Seconds ( ) ) - StartTime ; }
2019-02-27 11:57:17 -05:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2023-08-16 14:28:25 -04:00
const FString & FGameplayMediaEncoder : : GetListenerName ( ) const
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2023-08-16 14:28:25 -04:00
{
static const FString & ListenerName = TEXT ( " GameplayMediaEncoderListener " ) ;
return ListenerName ;
}
2019-09-26 07:59:32 -04:00
void FGameplayMediaEncoder : : OnNewSubmixBuffer ( const USoundSubmix * OwningSubmix , float * AudioData , int32 NumSamples , int32 NumChannels , const int32 SampleRate , double /*AudioClock*/ )
2019-02-27 11:57:17 -05:00
{
CSV_SCOPED_TIMING_STAT ( GameplayMediaEncoder , OnNewSubmixBuffer ) ;
2021-10-26 09:13:01 -04:00
if ( SampleRate ! = HardcodedAudioSamplerate )
2019-02-27 11:57:17 -05:00
{
// Only report the problem once
2021-10-26 09:13:01 -04:00
if ( ! bAudioFormatChecked )
2019-02-27 11:57:17 -05:00
{
bAudioFormatChecked = true ;
UE_LOG ( GameplayMediaEncoder , Error , TEXT ( " Audio SampleRate needs to be %d HZ, current value is %d. VideoRecordingSystem won't record audio " ) , HardcodedAudioSamplerate , SampleRate ) ;
}
return ;
}
2019-09-26 07:27:04 -04:00
ProcessAudioFrame ( AudioData , NumSamples , NumChannels , SampleRate ) ;
2019-02-27 11:57:17 -05:00
}
2024-03-26 02:13:50 -04:00
void FGameplayMediaEncoder : : OnFrameBufferReady ( SWindow & SlateWindow , const FTextureRHIRef & FrameBuffer )
2019-02-27 11:57:17 -05:00
{
CSV_SCOPED_TIMING_STAT ( GameplayMediaEncoder , OnBackBufferReady ) ;
check ( IsInRenderingThread ( ) ) ;
2021-04-29 19:32:06 -04:00
ProcessVideoFrame ( FrameBuffer ) ;
}
2021-06-22 00:27:54 -04:00
void FGameplayMediaEncoder : : FloatToPCM16 ( float const * floatSamples , int32 numSamples , TArray < int16 > & out ) const
2021-04-29 19:32:06 -04:00
{
out . Reset ( numSamples ) ;
out . AddZeroed ( numSamples ) ;
float const * ptr = floatSamples ;
2021-10-26 09:13:01 -04:00
for ( auto & & sample : out )
2021-04-29 19:32:06 -04:00
{
2022-11-04 13:48:41 -04:00
int32 N = * ptr > = 0 ? FMath : : TruncToInt32 ( * ptr * int32 ( MAX_int16 ) ) : FMath : : TruncToInt32 ( * ptr * ( int32 ( MAX_int16 ) + 1 ) ) ;
2021-04-29 19:32:06 -04:00
sample = static_cast < int16 > ( FMath : : Clamp ( N , int32 ( MIN_int16 ) , int32 ( MAX_int16 ) ) ) ;
ptr + + ;
}
2019-02-27 11:57:17 -05:00
}
2019-11-26 17:28:51 -05:00
void FGameplayMediaEncoder : : ProcessAudioFrame ( const float * AudioData , int32 NumSamples , int32 NumChannels , int32 SampleRate )
2019-02-27 11:57:17 -05:00
{
2021-10-26 09:13:01 -04:00
// Don't encode audio encoder is not setup or destroyed.
if ( ! AudioEncoder . IsValid ( ) )
{
return ;
}
2021-04-29 19:32:06 -04:00
//// convert to PCM data
2021-10-26 09:13:01 -04:00
// TArray<int16> conversionBuffer;
// FloatToPCM16(AudioData, NumSamples, conversionBuffer);
2021-04-29 19:32:06 -04:00
//// add to any remainder data
2021-10-26 09:13:01 -04:00
// PCM16.Append(conversionBuffer);
2021-04-29 19:32:06 -04:00
//// encode the 10ms blocks
2021-10-26 09:13:01 -04:00
// size_t const encodeBlockSize = AudioConfig.Samplerate / 100 * AudioConfig.NumChannels;
// int32 bufferSize = PCM16.Num();
// auto timestamp = GetMediaTimestamp().GetTicks();
// auto bufferStart = PCM16.GetData();
2021-04-29 19:32:06 -04:00
2021-10-26 09:13:01 -04:00
// while (bufferSize >= encodeBlockSize)
2021-04-29 19:32:06 -04:00
//{
// rtc::Buffer encoded;
// auto encodedInfo = AudioEncoder->Encode(timestamp, { bufferStart, encodeBlockSize }, &encoded);
// if (encodedInfo.encoded_bytes > 0 || encodedInfo.send_even_if_empty)
// OnEncodedAudioFrame(encodedInfo, &encoded);
// timestamp += 10 * ETimespan::TicksPerMillisecond;
// bufferStart += encodeBlockSize;
// bufferSize -= encodeBlockSize;
//}
//// move the remainder to the start of the buffer
2021-10-26 09:13:01 -04:00
// int32 const remainderIdx = bufferStart - PCM16.GetData();
// for (int32 i = 0; i < bufferSize; ++i)
2021-04-29 19:32:06 -04:00
//{
// PCM16[i] = PCM16[remainderIdx + i];
//}
2024-01-25 14:17:31 -05:00
// PCM16.SetNum(bufferSize, EAllowShrinking::No);
2021-10-26 09:13:01 -04:00
2019-02-27 11:57:17 -05:00
Audio : : AlignedFloatBuffer InData ;
InData . Append ( AudioData , NumSamples ) ;
Audio : : TSampleBuffer < float > FloatBuffer ( InData , NumChannels , SampleRate ) ;
// Mix to stereo if required, since PixelStreaming only accept stereo at the moment
2021-10-26 09:13:01 -04:00
if ( FloatBuffer . GetNumChannels ( ) ! = HardcodedAudioNumChannels )
2019-02-27 11:57:17 -05:00
{
FloatBuffer . MixBufferToChannels ( HardcodedAudioNumChannels ) ;
}
2019-09-26 07:27:04 -04:00
// Adjust the AudioClock if for some reason it falls behind real time. This can happen if the game spikes, or if we break into the debugger.
FTimespan Now = GetMediaTimestamp ( ) ;
2021-10-26 09:13:01 -04:00
if ( AudioClock < Now . GetTotalSeconds ( ) )
2019-09-26 07:27:04 -04:00
{
2021-10-26 09:13:01 -04:00
UE_LOG ( GameplayMediaEncoder , Warning , TEXT ( " Audio clock falling behind real time clock by %.3f seconds. Ajusting audio clock " ) , Now . GetTotalSeconds ( ) - AudioClock ) ;
2019-09-26 07:27:04 -04:00
// Put it slightly ahead of the real time clock
AudioClock = Now . GetTotalSeconds ( ) + ( FloatBuffer . GetSampleDuration ( ) / 2 ) ;
}
2021-04-29 19:32:06 -04:00
// Convert to signed PCM 16-bits
2021-10-26 09:13:01 -04:00
// PCM16.Reset(FloatBuffer.GetNumSamples());
// PCM16.AddZeroed(FloatBuffer.GetNumSamples());
// int blockSize = AudioConfig.Samplerate / 100 * AudioConfig.NumChannels;
// PCM16.Reset(blockSize);
// PCM16.AddZeroed(blockSize);
// const float* Ptr = reinterpret_cast<const float*>(FloatBuffer.GetData());
// auto timestamp = GetMediaTimestamp().GetTotalMilliseconds();
// for (int i = 0; i < NumSamples * NumChannels; ++i)
2021-04-29 19:32:06 -04:00
//{
// int u;
// for (u = 0; u < blockSize && i < NumSamples; ++u, ++i, ++Ptr)
// {
// int32 N = *Ptr >= 0 ? *Ptr * int32(MAX_int16) : *Ptr * (int32(MAX_int16) + 1);
// PCM16[u] = static_cast<int16>(FMath::Clamp(N, int32(MIN_int16), int32(MAX_int16)));
// }
// rtc::Buffer encoded;
// auto encodedInfo = AudioEncoder->Encode(timestamp, { PCM16.GetData(), static_cast<size_t>(u) }, &encoded);
// OnEncodedAudioFrame(encodedInfo, &encoded);
// timestamp += 10;
//}
2021-10-26 09:13:01 -04:00
// for (int16& S : PCM16)
2021-04-29 19:32:06 -04:00
//{
// int32 N = *Ptr >= 0 ? *Ptr * int32(MAX_int16) : *Ptr * (int32(MAX_int16) + 1);
// S = static_cast<int16>(FMath::Clamp(N, int32(MIN_int16), int32(MAX_int16)));
// Ptr++;
//}
////rtc::ArrayView<const int16_t> audio(PCM16.GetData(), FloatBuffer.GetNumSamples());
2021-10-26 09:13:01 -04:00
// rtc::ArrayView<const int16_t> audio(PCM16.GetData(), blockSize);
// auto testSampleRate = AudioEncoder->SampleRateHz();
// auto testNumChannels = AudioEncoder->NumChannels();
// check(testSampleRate == AudioConfig.Samplerate);
// check(testNumChannels == AudioConfig.NumChannels);
// auto timestamp = GetMediaTimestamp().GetTotalMilliseconds();
// auto encodedInfo = AudioEncoder->Encode(timestamp, audio, &encoded);
// OnEncodedAudioFrame(encodedInfo, &encoded);
2021-04-29 19:32:06 -04:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
AVEncoder : : FAudioFrame Frame ;
Frame . Timestamp = FTimespan : : FromSeconds ( AudioClock ) ;
Frame . Duration = FTimespan : : FromSeconds ( FloatBuffer . GetSampleDuration ( ) ) ;
FloatBuffer . Clamp ( ) ;
Frame . Data = FloatBuffer ;
AudioEncoder - > Encode ( Frame ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
2019-09-26 07:27:04 -04:00
AudioClock + = FloatBuffer . GetSampleDuration ( ) ;
2019-02-27 11:57:17 -05:00
}
2024-03-26 02:13:50 -04:00
void FGameplayMediaEncoder : : ProcessVideoFrame ( const FTextureRHIRef & FrameBuffer )
2019-02-27 11:57:17 -05:00
{
2021-10-26 09:13:01 -04:00
// Early exit is video encoder is not valid because it is not setup or has been destroyed
if ( ! VideoEncoder . IsValid ( ) )
{
return ;
}
2019-02-27 11:57:17 -05:00
FScopeLock Lock ( & VideoProcessingCS ) ;
FTimespan Now = GetMediaTimestamp ( ) ;
2021-10-26 09:13:01 -04:00
if ( bDoFrameSkipping )
2019-02-27 11:57:17 -05:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
uint64 NumExpectedFrames = static_cast < uint64 > ( Now . GetTotalSeconds ( ) * VideoConfig . Framerate ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
UE_LOG ( GameplayMediaEncoder , VeryVerbose , TEXT ( " time %.3f: captured %d, expected %d " ) , Now . GetTotalSeconds ( ) , NumCapturedFrames + 1 , NumExpectedFrames ) ;
2021-10-26 09:13:01 -04:00
if ( NumCapturedFrames + 1 > NumExpectedFrames )
2019-02-27 11:57:17 -05:00
{
UE_LOG ( GameplayMediaEncoder , Verbose , TEXT ( " Framerate control dropped captured frame " ) ) ;
2019-11-26 17:28:51 -05:00
return ;
2019-02-27 11:57:17 -05:00
}
}
2021-06-22 00:27:54 -04:00
UpdateVideoConfig ( ) ;
2019-02-27 11:57:17 -05:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
TSharedPtr < AVEncoder : : FVideoEncoderInputFrame > InputFrame = ObtainInputFrame ( ) ;
2021-06-22 00:27:54 -04:00
const int32 FrameId = InputFrame - > GetFrameID ( ) ;
InputFrame - > SetTimestampUs ( Now . GetTicks ( ) ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
CopyTexture ( FrameBuffer , BackBuffers [ InputFrame ] ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
AVEncoder : : FVideoEncoder : : FEncodeOptions EncodeOptions ;
VideoEncoder - > Encode ( InputFrame , EncodeOptions ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
LastVideoInputTimestamp = Now ;
NumCapturedFrames + + ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
TSharedPtr < AVEncoder : : FVideoEncoderInputFrame > FGameplayMediaEncoder : : ObtainInputFrame ( )
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-10-10 01:07:51 -04:00
TSharedPtr < AVEncoder : : FVideoEncoderInputFrame > InputFrame = VideoEncoderInput - > ObtainInputFrame ( ) ;
InputFrame - > SetWidth ( VideoConfig . Width ) ;
InputFrame - > SetHeight ( VideoConfig . Height ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
2021-10-26 09:13:01 -04:00
if ( ! BackBuffers . Contains ( InputFrame ) )
2019-02-27 11:57:17 -05:00
{
2021-10-26 09:13:01 -04:00
# if PLATFORM_WINDOWS && PLATFORM_DESKTOP
2022-02-24 11:58:36 -05:00
const ERHIInterfaceType RHIType = RHIGetInterfaceType ( ) ;
2022-05-10 17:13:37 -04:00
const FRHITextureCreateDesc Desc =
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-05-10 17:13:37 -04:00
FRHITextureCreateDesc : : Create2D ( TEXT ( " VideoCapturerBackBuffer " ) , VideoConfig . Width , VideoConfig . Height , PF_B8G8R8A8 )
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2022-05-10 17:13:37 -04:00
. SetFlags ( ETextureCreateFlags : : Shared | ETextureCreateFlags : : RenderTargetable | ETextureCreateFlags : : UAV )
. SetInitialState ( ERHIAccess : : CopyDest ) ;
2022-02-24 11:58:36 -05:00
if ( RHIType = = ERHIInterfaceType : : D3D11 )
2021-04-29 19:32:06 -04:00
{
2021-06-22 00:27:54 -04:00
FRHIResourceCreateInfo CreateInfo ( TEXT ( " VideoCapturerBackBuffer " ) ) ;
2022-05-10 17:13:37 -04:00
FTextureRHIRef Texture = RHICreateTexture ( Desc ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
InputFrame - > SetTexture ( ( ID3D11Texture2D * ) Texture - > GetNativeResource ( ) , [ & , InputFrame ] ( ID3D11Texture2D * NativeTexture ) { BackBuffers . Remove ( InputFrame ) ; } ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
BackBuffers . Add ( InputFrame , Texture ) ;
2021-04-29 19:32:06 -04:00
}
2022-02-24 11:58:36 -05:00
else if ( RHIType = = ERHIInterfaceType : : D3D12 )
2021-04-29 19:32:06 -04:00
{
2021-06-22 00:27:54 -04:00
FRHIResourceCreateInfo CreateInfo ( TEXT ( " VideoCapturerBackBuffer " ) ) ;
2022-05-10 17:13:37 -04:00
FTextureRHIRef Texture = RHICreateTexture ( Desc ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
InputFrame - > SetTexture ( ( ID3D12Resource * ) Texture - > GetNativeResource ( ) , [ & , InputFrame ] ( ID3D12Resource * NativeTexture ) { BackBuffers . Remove ( InputFrame ) ; } ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-06-22 00:27:54 -04:00
BackBuffers . Add ( InputFrame , Texture ) ;
2021-04-29 19:32:06 -04:00
}
2021-06-22 00:27:54 -04:00
UE_LOG ( LogTemp , Log , TEXT ( " %d backbuffers currently allocated " ) , BackBuffers . Num ( ) ) ;
2021-10-26 09:13:01 -04:00
# else
unimplemented ( ) ;
# endif // PLATFORM_DESKTOP && !PLATFORM_APPLE
2019-02-27 11:57:17 -05:00
}
2021-06-22 00:27:54 -04:00
return InputFrame ;
2019-02-27 11:57:17 -05:00
}
void FGameplayMediaEncoder : : SetVideoBitrate ( uint32 Bitrate )
{
NewVideoBitrate = Bitrate ;
bChangeBitrate = true ;
}
void FGameplayMediaEncoder : : SetVideoFramerate ( uint32 Framerate )
{
NewVideoFramerate = FMath : : Clamp ( Framerate , MinVideoFPS , MaxVideoFPS ) ;
bChangeFramerate = true ;
}
2021-06-22 00:27:54 -04:00
void FGameplayMediaEncoder : : UpdateVideoConfig ( )
2019-02-27 11:57:17 -05:00
{
2021-10-26 09:13:01 -04:00
if ( bChangeBitrate | | bChangeFramerate )
2019-02-27 11:57:17 -05:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-05-25 02:43:26 -04:00
auto config = VideoEncoder - > GetLayerConfig ( 0 ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
2021-10-26 09:13:01 -04:00
if ( bChangeBitrate )
2021-05-25 02:43:26 -04:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-05-25 02:43:26 -04:00
config . MaxBitrate = MaxVideoBitrate ;
config . TargetBitrate = NewVideoBitrate ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-05-25 02:43:26 -04:00
}
2019-02-27 11:57:17 -05:00
2021-10-26 09:13:01 -04:00
if ( bChangeFramerate )
2021-05-25 02:43:26 -04:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-05-25 02:43:26 -04:00
config . MaxFramerate = NewVideoFramerate ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-05-25 02:43:26 -04:00
NumCapturedFrames = 0 ;
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-05-25 02:43:26 -04:00
VideoEncoder - > UpdateLayerConfig ( 0 , config ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-02-27 11:57:17 -05:00
bChangeFramerate = false ;
2021-05-25 02:43:26 -04:00
bChangeBitrate = false ;
2019-02-27 11:57:17 -05:00
}
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
void FGameplayMediaEncoder : : OnEncodedAudioFrame ( const AVEncoder : : FMediaPacket & Packet )
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
{
FScopeLock Lock ( & ListenersCS ) ;
2021-10-26 09:13:01 -04:00
for ( auto & & Listener : Listeners )
2019-11-26 17:28:51 -05:00
{
Listener - > OnMediaSample ( Packet ) ;
}
}
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-02-10 21:16:24 -05:00
void FGameplayMediaEncoder : : OnEncodedVideoFrame ( uint32 LayerIndex , const TSharedPtr < AVEncoder : : FVideoEncoderInputFrame > InputFrame , const AVEncoder : : FCodecPacket & Packet )
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
{
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
AVEncoder : : FMediaPacket packet ( AVEncoder : : EPacketType : : Video ) ;
2021-06-22 00:27:54 -04:00
packet . Timestamp = InputFrame - > GetTimestampUs ( ) ;
2021-04-29 19:32:06 -04:00
packet . Duration = 0 ; // This should probably be 1.0f / fps in ms
2021-12-01 16:57:33 -05:00
packet . Data = TArray < uint8 > ( Packet . Data . Get ( ) , Packet . DataSize ) ;
2021-04-29 19:32:06 -04:00
packet . Video . bKeyFrame = Packet . IsKeyFrame ;
packet . Video . Width = InputFrame - > GetWidth ( ) ;
packet . Video . Height = InputFrame - > GetHeight ( ) ;
packet . Video . FrameAvgQP = Packet . VideoQP ;
packet . Video . Framerate = VideoConfig . Framerate ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
FScopeLock Lock ( & ListenersCS ) ;
2021-10-26 09:13:01 -04:00
for ( auto & & Listener : Listeners )
2019-11-26 17:28:51 -05:00
{
2021-04-29 19:32:06 -04:00
Listener - > OnMediaSample ( packet ) ;
2019-11-26 17:28:51 -05:00
}
2021-04-29 19:32:06 -04:00
2024-03-26 02:13:50 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2021-04-29 19:32:06 -04:00
InputFrame - > Release ( ) ;
2024-03-26 02:13:50 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2019-11-26 17:28:51 -05:00
}
2024-03-26 02:13:50 -04:00
void FGameplayMediaEncoder : : CopyTexture ( const FTextureRHIRef & SourceTexture , FTextureRHIRef & DestinationTexture ) const
2019-11-26 17:28:51 -05:00
{
2021-04-29 19:32:06 -04:00
FRHICommandListImmediate & RHICmdList = FRHICommandListExecutor : : GetImmediateCommandList ( ) ;
2019-11-26 17:28:51 -05:00
2021-10-26 09:13:01 -04:00
if ( SourceTexture - > GetFormat ( ) = = DestinationTexture - > GetFormat ( ) & & SourceTexture - > GetSizeXY ( ) = = DestinationTexture - > GetSizeXY ( ) )
2021-04-29 19:32:06 -04:00
{
2022-05-20 17:14:41 -04:00
TransitionAndCopyTexture ( RHICmdList , SourceTexture , DestinationTexture , { } ) ;
2021-04-29 19:32:06 -04:00
}
else // Texture format mismatch, use a shader to do the copy.
{
IRendererModule * RendererModule = & FModuleManager : : GetModuleChecked < IRendererModule > ( " Renderer " ) ;
// #todo-renderpasses there's no explicit resolve here? Do we need one?
FRHIRenderPassInfo RPInfo ( DestinationTexture , ERenderTargetActions : : Load_Store ) ;
2022-05-10 14:47:34 -04:00
RHICmdList . Transition ( FRHITransitionInfo ( DestinationTexture , ERHIAccess : : Unknown , ERHIAccess : : RTV ) ) ;
2021-04-29 19:32:06 -04:00
RHICmdList . BeginRenderPass ( RPInfo , TEXT ( " CopyBackbuffer " ) ) ;
{
2022-11-04 13:48:41 -04:00
RHICmdList . SetViewport ( 0 , 0 , 0.0f , ( float ) DestinationTexture - > GetSizeX ( ) , ( float ) DestinationTexture - > GetSizeY ( ) , 1.0f ) ;
2021-04-29 19:32:06 -04:00
FGraphicsPipelineStateInitializer GraphicsPSOInit ;
RHICmdList . ApplyCachedRenderTargets ( GraphicsPSOInit ) ;
GraphicsPSOInit . BlendState = TStaticBlendState < > : : GetRHI ( ) ;
GraphicsPSOInit . RasterizerState = TStaticRasterizerState < > : : GetRHI ( ) ;
GraphicsPSOInit . DepthStencilState = TStaticDepthStencilState < false , CF_Always > : : GetRHI ( ) ;
// New engine version...
FGlobalShaderMap * ShaderMap = GetGlobalShaderMap ( GMaxRHIFeatureLevel ) ;
TShaderMapRef < FScreenVS > VertexShader ( ShaderMap ) ;
TShaderMapRef < FScreenPS > PixelShader ( ShaderMap ) ;
GraphicsPSOInit . BoundShaderState . VertexDeclarationRHI = GFilterVertexDeclaration . VertexDeclarationRHI ;
GraphicsPSOInit . BoundShaderState . VertexShaderRHI = VertexShader . GetVertexShader ( ) ;
GraphicsPSOInit . BoundShaderState . PixelShaderRHI = PixelShader . GetPixelShader ( ) ;
GraphicsPSOInit . PrimitiveType = PT_TriangleList ;
2021-09-03 12:04:52 -04:00
SetGraphicsPipelineState ( RHICmdList , GraphicsPSOInit , 0 ) ;
2021-04-29 19:32:06 -04:00
2023-03-01 17:43:57 -05:00
const bool bSameSize = ( DestinationTexture - > GetDesc ( ) . Extent = = SourceTexture - > GetDesc ( ) . Extent ) ;
FRHISamplerState * PixelSampler = bSameSize ? TStaticSamplerState < SF_Point > : : GetRHI ( ) : TStaticSamplerState < SF_Bilinear > : : GetRHI ( ) ;
SetShaderParametersLegacyPS ( RHICmdList , PixelShader , PixelSampler , SourceTexture ) ;
2021-04-29 19:32:06 -04:00
2021-10-26 09:13:01 -04:00
RendererModule - > DrawRectangle ( RHICmdList , 0 , 0 , // Dest X, Y
2022-11-04 13:48:41 -04:00
( float ) DestinationTexture - > GetSizeX ( ) , // Dest Width
( float ) DestinationTexture - > GetSizeY ( ) , // Dest Height
2021-10-26 09:13:01 -04:00
0 , 0 , // Source U, V
1 , 1 , // Source USize, VSize
DestinationTexture - > GetSizeXY ( ) , // Target buffer size
FIntPoint ( 1 , 1 ) , // Source texture size
VertexShader , EDRF_Default ) ;
2021-04-29 19:32:06 -04:00
}
RHICmdList . EndRenderPass ( ) ;
2022-05-10 14:47:34 -04:00
RHICmdList . Transition ( FRHITransitionInfo ( DestinationTexture , ERHIAccess : : RTV , ERHIAccess : : SRVMask ) ) ;
2021-04-29 19:32:06 -04:00
}
2021-06-22 00:27:54 -04:00
}