Files
UnrealEngine/Engine/Plugins/Media/PixelStreaming2/Source/PixelStreaming2Settings/Private/PixelStreaming2PluginSettings.cpp
2025-05-18 13:04:45 +08:00

1439 lines
60 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PixelStreaming2PluginSettings.h"
#include "IPixelStreaming2Streamer.h"
#include "Logging.h"
#include "Misc/CommandLine.h"
#include "UObject/ReflectedTypeAccessors.h"
namespace
{
template <typename TEnumType>
static void CheckConsoleEnum(IConsoleVariable* InConsoleVariable)
{
FString ConsoleString = InConsoleVariable->GetString();
if (StaticEnum<TEnumType>()->GetIndexByNameString(ConsoleString) == INDEX_NONE)
{
// Legacy CVar values were the enum values but underscores (LOW_LATENCY) instead of the camel case UENUM string (LowLatency). They are still valid we just need to remove the underscores when we check them.
if (ConsoleString = ConsoleString.Replace(TEXT("_"), TEXT("")); StaticEnum<TEnumType>()->GetIndexByNameString(ConsoleString) != INDEX_NONE)
{
InConsoleVariable->Set(*ConsoleString, ECVF_SetByConsole);
}
else
{
FString ConsoleObjectName = IConsoleManager::Get().FindConsoleObjectName(InConsoleVariable);
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Invalid value {0} received for enum {1} of type {2}", ConsoleString, ConsoleObjectName, StaticEnum<TEnumType>()->GetName());
InConsoleVariable->Set(*InConsoleVariable->GetDefaultValue(), ECVF_SetByConsole);
}
}
}
static void VerifyCVarVideoSettings(IConsoleVariable* /* We ignore the passed in console variable as this method is called by many different CVars */)
{
IConsoleVariable* SimulcastCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("PixelStreaming2.Encoder.EnableSimulcast"));
IConsoleVariable* CodecCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("PixelStreaming2.Encoder.Codec"));
IConsoleVariable* ScalabilityModeCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("PixelStreaming2.Encoder.ScalabilityMode"));
// Verify that the video codec and scalability mode strings correctly map to an enum
CheckConsoleEnum<EVideoCodec>(CodecCVar);
CheckConsoleEnum<EScalabilityMode>(ScalabilityModeCVar);
if (SimulcastCVar->GetBool())
{
// Check that the selected codec supports simulcast
FString Codec = CodecCVar->GetString();
if (Codec != TEXT("H264") && Codec != TEXT("VP8"))
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Selected codec doesn't support simulcast! Resetting default codec to {0}", CodecCVar->GetDefaultValue());
CodecCVar->Set(*CodecCVar->GetDefaultValue(), ECVF_SetByConsole);
}
}
FString Codec = CodecCVar->GetString();
FString ScalabilityMode = ScalabilityModeCVar->GetString();
if ((Codec == TEXT("H264") || Codec == TEXT("VP8"))
&& (ScalabilityMode != TEXT("L1T1") && ScalabilityMode != TEXT("L1T2") && ScalabilityMode != TEXT("L1T3")))
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Selected codec doesn't support the {0} scalability mode! Resetting scalability mode to {1}", ScalabilityMode, ScalabilityModeCVar->GetDefaultValue());
ScalabilityModeCVar->Set(*ScalabilityModeCVar->GetDefaultValue(), ECVF_SetByConsole);
}
}
static void VerifyCVarDefaultStreamerType(IConsoleVariable* CVar)
{
TArray<FString> AvailableFactoryTypes = IPixelStreaming2StreamerFactory::GetAvailableFactoryTypes();
FString SpecifiedFactory = CVar->GetString();
if (AvailableFactoryTypes.Num() == 0)
{
// This code path executes when the cvar is initially set and no factories have been registered
return;
}
bool bValid = false;
for (const FString& AvailableFactory : AvailableFactoryTypes)
{
if (SpecifiedFactory == AvailableFactory)
{
bValid = true;
break;
}
}
if (!bValid)
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "\"{0}\" isn't a registered streamer type. Valid types: [{1}]. Restoring to \"{2}\"", SpecifiedFactory, FString::Join(AvailableFactoryTypes, TEXT(",")), CVar->GetDefaultValue());
CVar->SetWithCurrentPriority(*CVar->GetDefaultValue());
}
}
FString ConsoleVariableToCommandArgValue(const FString InCVarName)
{
// CVars are . deliminated by section. To get their equivilent commandline arg for parsing
// we need to remove the . and add a "="
return InCVarName.Replace(TEXT("."), TEXT("")).Replace(TEXT("PixelStreaming2"), TEXT("PixelStreaming")).Append(TEXT("="));
}
FString ConsoleVariableToCommandArgParam(const FString InCVarName)
{
// CVars are . deliminated by section. To get their equivilent commandline arg parameter, we need to to remove the .
return InCVarName.Replace(TEXT("."), TEXT("")).Replace(TEXT("PixelStreaming2"), TEXT("PixelStreaming"));
}
static void ParseLegacyCommandLineValue(const TCHAR* Match, TAutoConsoleVariable<FString>& CVar)
{
FString Value;
if (FParse::Value(FCommandLine::Get(), Match, Value))
{
CVar->Set(*Value, ECVF_SetByCommandline);
}
};
static void ParseLegacyCommandLineOption(const TCHAR* Match, TAutoConsoleVariable<bool>& CVar)
{
FString ValueMatch(Match);
ValueMatch.Append(TEXT("="));
FString Value;
if (FParse::Value(FCommandLine::Get(), *ValueMatch, Value))
{
if (Value.Equals(FString(TEXT("true")), ESearchCase::IgnoreCase))
{
CVar->Set(true, ECVF_SetByCommandline);
}
else if (Value.Equals(FString(TEXT("false")), ESearchCase::IgnoreCase))
{
CVar->Set(false, ECVF_SetByCommandline);
}
}
else if (FParse::Param(FCommandLine::Get(), Match))
{
CVar->Set(true, ECVF_SetByCommandline);
}
}
static FString FindPropertyFromCVar(const TSet<TPair<FString, FString>> Set, const FString& Key)
{
for (const TPair<FString, FString>& Pair : Set)
{
if (Pair.Key == Key)
{
return Pair.Value;
}
}
return "";
}
static FString FindCVarFromProperty(const TSet<TPair<FString, FString>> Set, const FString& Value)
{
for (const TPair<FString, FString>& Pair : Set)
{
if (Pair.Value == Value)
{
return Pair.Key;
}
}
return "";
}
} // namespace
// Map of Property Names to their commandline args as GetMetaData() is not avaliable in packaged projects
static const TSet<TPair<FString, FString>> GetCmdArg = {
{ "PixelStreaming2.LogStats", "LogStats" },
{ "PixelStreaming2.EpicRtcLogFilter", "EpicRtcLogFilter" },
{ "PixelStreaming2.SendPlayerIdAsInteger", "SendPlayerIdAsInteger" },
{ "PixelStreaming2.DisableLatencyTester", "DisableLatencyTester" },
{ "PixelStreaming2.DecoupleFrameRate", "DecoupleFramerate" },
{ "PixelStreaming2.DecoupleWaitFactor", "DecoupleWaitFactor" },
{ "PixelStreaming2.SignalingReconnectInterval", "SignalingReconnectInterval" },
{ "PixelStreaming2.SignalingMaxReconnectAttempts", "SignalingMaxReconnectAttempts" },
{ "PixelStreaming2.SignalingKeepAliveInterval", "SignalingKeepAliveInterval" },
{ "PixelStreaming2.UseMediaCapture", "UseMediaCapture" },
{ "PixelStreaming2.ID", "DefaultStreamerID" },
{ "PixelStreaming2.DefaultStreamerType", "DefaultStreamerType" },
{ "PixelStreaming2.AutoStartStream", "AutoStartStream" },
{ "PixelStreaming2.ConnectionURL", "ConnectionURL" },
{ "PixelStreaming2.CaptureUseFence", "CaptureUseFence" },
{ "PixelStreaming2.Encoder.Codec", "Codec" },
{ "PixelStreaming2.Encoder.TargetBitrate", "EncoderTargetBitrate" },
{ "PixelStreaming2.Encoder.MinQuality", "EncoderMinQuality" },
{ "PixelStreaming2.Encoder.MaxQuality", "EncoderMaxQuality" },
{ "PixelStreaming2.Encoder.ScalabilityMode", "ScalabilityMode" },
{ "PixelStreaming2.Encoder.KeyframeInterval", "KeyframeInterval" },
{ "PixelStreaming2.Encoder.MaxSessions", "MaxSessions" },
{ "PixelStreaming2.Encoder.EnableSimulcast", "EnableSimulcast" },
{ "PixelStreaming2.WebRTC.Fps", "WebRTCFps" },
{ "PixelStreaming2.WebRTC.StartBitrate", "WebRTCStartBitrate" },
{ "PixelStreaming2.WebRTC.MinBitrate", "WebRTCMinBitrate" },
{ "PixelStreaming2.WebRTC.MaxBitrate", "WebRTCMaxBitrate" },
{ "PixelStreaming2.WebRTC.DisableReceiveAudio", "WebRTCDisableReceiveAudio" },
{ "PixelStreaming2.WebRTC.DisableReceiveVideo", "WebRTCDisableReceiveVideo" },
{ "PixelStreaming2.WebRTC.DisableTransmitAudio", "WebRTCDisableTransmitAudio" },
{ "PixelStreaming2.WebRTC.DisableTransmitVideo", "WebRTCDisableTransmitVideo" },
{ "PixelStreaming2.WebRTC.DisableAudioSync", "WebRTCDisableAudioSync" },
{ "PixelStreaming2.WebRTC.EnableFlexFec", "WebRTCEnableFlexFec" },
{ "PixelStreaming2.WebRTC.DisableStats", "WebRTCDisableStats" },
{ "PixelStreaming2.WebRTC.StatsInterval", "WebRTCStatsInterval" },
{ "PixelStreaming2.WebRTC.NegotiateCodecs", "WebRTCNegotiateCodecs" },
{ "PixelStreaming2.WebRTC.AudioGain", "WebRTCAudioGain" },
{ "PixelStreaming2.WebRTC.PortAllocatorFlags", "WebRTCPortAllocatorFlags" },
{ "PixelStreaming2.WebRTC.MinPort", "WebRTCMinPort" },
{ "PixelStreaming2.WebRTC.MaxPort", "WebRTCMaxPort" },
{ "PixelStreaming2.WebRTC.FieldTrials", "WebRTCFieldTrials" },
{ "PixelStreaming2.WebRTC.DisableFrameDropper", "WebRTCDisableFrameDropper" },
{ "PixelStreaming2.WebRTC.VideoPacing.MaxDelay", "WebRTCVideoPacingMaxDelay" },
{ "PixelStreaming2.WebRTC.VideoPacing.Factor", "WebRTCVideoPacingFactor" },
{ "PixelStreaming2.Editor.StartOnLaunch", "EditorStartOnLaunch" },
{ "PixelStreaming2.Editor.UseRemoteSignallingServer", "EditorUseRemoteSignallingServer" },
{ "PixelStreaming2.HMD.Enable", "HMDEnable" },
{ "PixelStreaming2.HMD.MatchAspectRatio", "HMDMatchAspectRatio" },
{ "PixelStreaming2.HMD.ApplyEyePosition", "HMDAppleEyePosition" },
{ "PixelStreaming2.HMD.ApplyEyeRotation", "HMDApplyEyeRotation" },
{ "PixelStreaming2.HMD.HFOV", "HMDHFOV" },
{ "PixelStreaming2.HMD.VFOV", "HMDVFOV" },
{ "PixelStreaming2.HMD.IPD", "HMDIPD" },
{ "PixelStreaming2.HMD.ProjectionOffsetX", "HMDProjectionOffsetX" },
{ "PixelStreaming2.HMD.ProjectionOffsetY", "HMDProjectionOffsetY" },
{ "PixelStreaming2.AllowPixelStreamingCommands", "InputAllowConsoleCommands" },
{ "PixelStreaming2.KeyFilter", "InputKeyFilter" },
{ "PixelStreaming2.WebRTC.CodecPreferences", "WebRTCCodecPreferences" }
};
static const TSet<TPair<FString, FString>> GetMappedCmdArg = {
{ "PixelStreaming2.InputController", "InputController" },
{ "PixelStreaming2.Encoder.QualityPreset", "QualityPreset" },
{ "PixelStreaming2.Encoder.LatencyMode", "LatencyMode" },
{ "PixelStreaming2.Encoder.H264Profile", "H264Profile" },
{ "PixelStreaming2.Editor.Source", "EditorSource" }
};
// Map a legacy cvar to its new property
static const TSet<TPair<FString, FString>> GetLegacyCmdArg = {
{ "PixelStreaming2.Encoder.MinQp", "EncoderMaxQuality" }, // Renamed to MaxQuality
{ "PixelStreaming2.Encoder.MaxQp", "EncoderMinQuality" }, // Renamed to MinQuality
{ "PixelStreaming2.IP", "ConnectionURL" }, // Moved to ConnectionURL
{ "PixelStreaming2.Port", "ConnectionURL" }, // Moved to ConnectionURL
{ "PixelStreaming2.URL", "ConnectionURL" }, // Renamed to ConnectionURL
{ "PixelStreaming2.SignallingURL", "ConnectionURL" }, // Renamed to ConnectionURL
{ "AllowPixelStreamingCommands", "InputAllowConsoleCommands" }, // Renamed to InputAllowConsoleCommands
{ "PixelStreaming2.NegotiateCodecs", "WebRTCNegotiateCodecs" }, // Renamed to PixelStreaming2.WebRTC.NegotiateCodecs
{ "PixelStreaming2.EnableHMD", "HMDEnable" }, // Renamed to PixelStreaming2.HMDEnable
{ "Editor.PixelStreaming2.StartOnLaunch", "EditorStartOnLaunch" }, // Renamed to PixelStreaming2.Editor.StartOnLaunch
{ "Editor.PixelStreaming2.UseRemoteSignallingServer", "EditorUseRemoteSignallingServer" }, // Renamed to PixelStreaming2.Editor.UseRemoteSignallingServer
{ "Editor.PixelStreaming2.Source", "EditorSource" } // Renamed to PixelStreaming2.Editor.Source
};
// Begin Pixel Streaming Plugin CVars
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarLogStats(
TEXT("PixelStreaming2.LogStats"),
false,
TEXT("Whether to show PixelStreaming stats in the log (default: false)."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnLogStatsChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEpicRtcLogFilter(
TEXT("PixelStreaming2.EpicRtcLogFilter"),
"",
TEXT("Double forward slash (\"//\") separated list of regex patterns to filter from the EpicRtc logs (default: \"\")."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnEpicRtcLogFilterChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarDisableLatencyTester(
TEXT("PixelStreaming2.DisableLatencyTester"),
false,
TEXT("If true disables latency tester being triggerable."),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarInputController(
TEXT("PixelStreaming2.InputController"),
TEXT("Any"),
TEXT("Various modes of input control supported by Pixel Streaming, currently: \"Any\" or \"Host\". Default: Any"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarDecoupleFramerate(
TEXT("PixelStreaming2.DecoupleFramerate"),
false,
TEXT("Whether we should only stream as fast as we render or at some fixed interval. Coupled means only stream what we render."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnDecoupleFramerateChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarDecoupleWaitFactor(
TEXT("PixelStreaming2.DecoupleWaitFactor"),
1.25f,
TEXT("Frame rate factor to wait for a captured frame when streaming in decoupled mode. Higher factor waits longer but may also result in higher latency."),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarSignalingReconnectInterval(
TEXT("PixelStreaming2.SignalingReconnectInterval"),
2.0f,
TEXT("Changes the number of seconds between attempted reconnects to the signaling server. This is useful for reducing the log spam produced from attempted reconnects. A value <= 0 results in no reconnect. Default: 2.0s"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarSignalingMaxReconnectAttempts(
TEXT("PixelStreaming2.SignalingMaxReconnectAttempts"),
-1,
TEXT("Changes the number of attempts that will be made to reconnect to the signalling server. This is useful for triggering application shutdowns if this value is exceeded. A value of < 0 results in unlimited attempts. Default: -1"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarSignalingKeepAliveInterval(
TEXT("PixelStreaming2.SignalingKeepAliveInterval"),
30.0f,
TEXT("Changes the number of seconds between pings to the signaling server. This is useful for keeping the connection active. A value <= 0 results in no pings. Default: 30.0"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarUseMediaCapture(
TEXT("PixelStreaming2.UseMediaCapture"),
true,
TEXT("Use Media Capture from MediaIOFramework to capture frames rather than Pixel Streamings internal backbuffer sources."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnUseMediaCaptureChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarDefaultStreamerID(
TEXT("PixelStreaming2.ID"),
TEXT("DefaultStreamer"),
TEXT("Default Streamer ID to be used when not specified elsewhere."),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarDefaultStreamerType(
TEXT("PixelStreaming2.DefaultStreamerType"),
TEXT("DefaultRtc"),
TEXT("Default Streamer Type to be used when not specified elsewhere."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { VerifyCVarDefaultStreamerType(Var); }),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarAutoStartStream(
TEXT("PixelStreaming2.AutoStartStream"),
true,
TEXT("Configure the PixelStreaming2 plugin to automatically start streaming once loaded (if not in editor). You may wish to set this value to false and manually call StartStreaming at a later point from your c++ code. Default: true"),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarConnectionURL(
TEXT("PixelStreaming2.ConnectionURL"),
TEXT(""),
TEXT("Default URL to connect to. This can be a URL to a signalling server or some other endpoint with the format (protocol)://(host):(port)"),
ECVF_Default);
FAutoConsoleVariableDeprecated UPixelStreaming2PluginSettings::CVarSignallingURL(TEXT("PixelStreaming2.SignallingURL"), TEXT("PixelStreaming2.ConnectionURL"), TEXT("5.6"));
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarCaptureUseFence(
TEXT("PixelStreaming2.CaptureUseFence"),
true,
TEXT("Whether the texture copy we do during image capture should use a fence or not (non-fenced is faster but less safe)."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnCaptureUseFenceChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarDebugDumpAudio(
TEXT("PixelStreaming2.DumpDebugAudio"),
false,
TEXT("Dumps mixed audio from PS2 to a file on disk for debugging purposes."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnDebugDumpAudioChanged.Broadcast(Var); }),
ECVF_Default);
// Begin Encoder CVars
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarEncoderTargetBitrate(
TEXT("PixelStreaming2.Encoder.TargetBitrate"),
-1,
TEXT("Target bitrate (bps). Ignore the bitrate WebRTC wants (not recommended). Set to -1 to disable. Default -1."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarEncoderMinQuality(
TEXT("PixelStreaming2.Encoder.MinQuality"),
0,
TEXT("0-100, Higher values result in a better minimum quality but higher average bitrates. Default 0 - i.e. no limit on a minimum Quality."),
ECVF_Default);
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarEncoderMaxQuality(
TEXT("PixelStreaming2.Encoder.MaxQuality"),
100,
TEXT("0-100, Lower values result in lower average bitrates but reduces maximum achievable quality. Default 100 - i.e. no limit on a maximum Quality."),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEncoderQualityPreset(
TEXT("PixelStreaming2.Encoder.QualityPreset"),
TEXT("Default"),
TEXT("PixelStreaming encoder presets that affecting Quality vs Bitrate. Supported modes are: `ULTRA_LOW_QUALITY`, `LOW_QUALITY`, `DEFAULT`, `HIGH_QUALITY` or `LOSSLESS`"),
FConsoleVariableDelegate::CreateStatic(&CheckConsoleEnum<EAVPreset>),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEncoderLatencyMode(
TEXT("PixelStreaming2.Encoder.LatencyMode"),
TEXT("UltraLowLatency"),
TEXT("PixelStreaming encoder mode that affecting Quality vs Latency. Supported modes are: `ULTRA_LOW_LATENCY`, `LOW_LATENCY` or `DEFAULT`"),
ECVF_Default);
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarEncoderKeyframeInterval(
TEXT("PixelStreaming2.Encoder.KeyframeInterval"),
-1,
TEXT("How many frames before a key frame is sent. Default: -1 which disables the sending of periodic key frames. Note: NVENC reqires a reinitialization when this changes."),
ECVF_Default);
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarEncoderMaxSessions(
TEXT("PixelStreaming2.Encoder.MaxSessions"),
-1,
TEXT("-1 implies no limit. Maximum number of concurrent hardware encoder sessions for Pixel Streaming. Note GeForce gpus only support 8 concurrent sessions and will rollover to software encoding when that number is exceeded."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarEncoderEnableSimulcast(
TEXT("PixelStreaming2.Encoder.EnableSimulcast"),
false,
TEXT("Enables simulcast. When enabled, the encoder will encode at full resolution, 1/2 resolution and 1/4 resolution simultaneously. Note: Simulcast is only supported with `H264` and `VP8` and you must use the SFU from the infrastructure to fully utilise this functionality."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { VerifyCVarVideoSettings(nullptr); Delegates()->OnSimulcastEnabledChanged.Broadcast(Var); }),
ECVF_Default);
FString UE::PixelStreaming2::GSelectedCodec = TEXT("H264");
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEncoderCodec(
TEXT("PixelStreaming2.Encoder.Codec"),
UE::PixelStreaming2::GSelectedCodec,
TEXT("PixelStreaming default encoder codec. Supported values are: `H264`, `VP8`, `VP9` or `AV1`"),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { UE::PixelStreaming2::GSelectedCodec = Var->GetString(); VerifyCVarVideoSettings(nullptr); }),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEncoderScalabilityMode(
TEXT("PixelStreaming2.Encoder.ScalabilityMode"),
TEXT("L1T1"),
TEXT("Indicates number of Spatial and temporal layers used, default: L1T1. For a full list of values refer to https://www.w3.org/TR/webrtc-svc/#scalabilitymodes*"),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { VerifyCVarVideoSettings(nullptr); Delegates()->OnScalabilityModeChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEncoderH264Profile(
TEXT("PixelStreaming2.Encoder.H264Profile"),
TEXT("Baseline"),
TEXT("PixelStreaming encoder profile. Supported modes are: `AUTO`, `BASELINE`, `MAIN`, `HIGH`, `PROGRESSIVE_HIGH`, `CONSTRAINED_HIGH` or `HIGH444`"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarEncoderDebugDumpFrame(
TEXT("PixelStreaming2.Encoder.DumpDebugFrames"),
false,
TEXT("Dumps frames from the encoder to a file on disk for debugging purposes."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnEncoderDebugDumpFrameChanged.Broadcast(Var); }),
ECVF_Default);
// Begin WebRTC CVars
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarWebRTCFps(
TEXT("PixelStreaming2.WebRTC.Fps"),
60,
TEXT("Framerate for WebRTC encoding. Default: 60"),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnWebRTCFpsChanged.Broadcast(Var); }),
ECVF_Default);
// Note: 1 megabit is the maximum allowed in WebRTC for a start bitrate.
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarWebRTCStartBitrate(
TEXT("PixelStreaming2.WebRTC.StartBitrate"),
1000000,
TEXT("Start bitrate (bps) that WebRTC will try begin the stream with. Must be between Min/Max bitrates. Default: 1000000"),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarWebRTCMinBitrate(
TEXT("PixelStreaming2.WebRTC.MinBitrate"),
100000,
TEXT("Min bitrate (bps) that WebRTC will not request below. Careful not to set too high otherwise WebRTC will just drop frames. Default: 100000"),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnWebRTCBitrateChanged.Broadcast(Var); }),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> UPixelStreaming2PluginSettings::CVarWebRTCMaxBitrate(
TEXT("PixelStreaming2.WebRTC.MaxBitrate"),
40000000,
TEXT("Max bitrate (bps) that WebRTC will not request above. Default: 40000000 aka 40 megabits/per second."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnWebRTCBitrateChanged.Broadcast(Var); }),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableReceiveAudio(
TEXT("PixelStreaming2.WebRTC.DisableReceiveAudio"),
false,
TEXT("Disables receiving audio from the browser into UE."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableReceiveVideo(
TEXT("PixelStreaming2.WebRTC.DisableReceiveVideo"),
true,
TEXT("Disables receiving video from the browser into UE."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableTransmitAudio(
TEXT("PixelStreaming2.WebRTC.DisableTransmitAudio"),
false,
TEXT("Disables transmission of UE audio to the browser."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableTransmitVideo(
TEXT("PixelStreaming2.WebRTC.DisableTransmitVideo"),
false,
TEXT("Disables transmission of UE video to the browser."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableAudioSync(
TEXT("PixelStreaming2.WebRTC.DisableAudioSync"),
true,
TEXT("Disables the synchronization of audio and video tracks in WebRTC. This can be useful in low latency usecases where synchronization is not required."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCEnableFlexFec(
TEXT("PixelStreaming2.WebRTC.EnableFlexFec"),
false,
TEXT("Signals support for Flexible Forward Error Correction to WebRTC. This can cause a reduction in quality if total bitrate is low."),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableStats(
TEXT("PixelStreaming2.WebRTC.DisableStats"),
false,
TEXT("Disables the collection of WebRTC stats."),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnWebRTCDisableStatsChanged.Broadcast(Var); }),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarWebRTCStatsInterval(
TEXT("PixelStreaming2.WebRTC.StatsInterval"),
1.f,
TEXT("Configures how often WebRTC stats are collected in seconds. Values less than 0.0f disable stats collection. Default: 1.0f"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCNegotiateCodecs(
TEXT("PixelStreaming2.WebRTC.NegotiateCodecs"),
false,
TEXT("Whether PS should send all its codecs during sdp handshake so peers can negotiate or just send a single selected codec."),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarWebRTCCodecPreferences(
TEXT("PixelStreaming2.WebRTC.CodecPreferences"),
TEXT("AV1,H264,VP9,VP8"),
TEXT("A comma separated list of video codecs specifying the prefered order PS will signal during sdp handshake"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarWebRTCAudioGain(
TEXT("PixelStreaming2.WebRTC.AudioGain"),
1.0f,
TEXT("Sets the amount of gain to apply to audio. Default: 1.0"),
ECVF_Default);
// End WebRTC CVars
// Begin EditorStreaming CVars
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarEditorStartOnLaunch(
TEXT("PixelStreaming2.Editor.StartOnLaunch"),
false,
TEXT("Start Editor Streaming as soon as the Unreal Editor is launched. Default: false"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarEditorUseRemoteSignallingServer(
TEXT("PixelStreaming2.Editor.UseRemoteSignallingServer"),
false,
TEXT("Enables the use of a remote signalling server. Default: false"),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarEditorSource(
TEXT("PixelStreaming2.Editor.Source"),
TEXT("Editor"),
TEXT("Editor PixelStreaming source. Supported values are `Editor`, `LevelEditorViewport`. Default: `Editor`"),
FConsoleVariableDelegate::CreateStatic(&CheckConsoleEnum<EPixelStreaming2EditorStreamTypes>),
ECVF_Default);
// End EditorStreaming CVars
// Begin HMD CVars
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarHMDEnable(
TEXT("PixelStreaming2.HMD.Enable"),
false,
TEXT("Enables HMD specific functionality for Pixel Streaming. Namely input handling and stereoscopic rendering. Default: false"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarHMDMatchAspectRatio(
TEXT("PixelStreaming2.HMD.MatchAspectRatio"),
true,
TEXT("If true automatically resize the rendering resolution to match the aspect ratio determined by the HFoV and VFoV. Default: true"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarHMDApplyEyePosition(
TEXT("PixelStreaming2.HMD.ApplyEyePosition"),
true,
TEXT("If true automatically position each eye's rendering by whatever amount WebXR reports for each left-right XRView. If false do no eye positioning. Default: true"),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarHMDApplyEyeRotation(
TEXT("PixelStreaming2.HMD.ApplyEyeRotation"),
true,
TEXT("If true automatically rotate each eye's rendering by whatever amount WebXR reports for each left-right XRView. If false do no eye rotation. Default: true"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarHMDHFOV(
TEXT("PixelStreaming2.HMD.HFOV"),
-1.0f,
TEXT("Overrides the horizontal field of view for HMD rendering, values are in degrees and values less than 0.0f disable the override. Default: -1.0f"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarHMDVFOV(
TEXT("PixelStreaming2.HMD.VFOV"),
-1.0f,
TEXT("Overrides the vertical field of view for HMD rendering, values are in degrees and values less than 0.0f disable the override. Default: -1.0f"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarHMDIPD(
TEXT("PixelStreaming2.HMD.IPD"),
-1.0f,
TEXT("Overrides the HMD IPD (interpupillary distance), values are in centimeters and values less than 0.0f disable the override. Default: -1.0f"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarHMDProjectionOffsetX(
TEXT("PixelStreaming2.HMD.ProjectionOffsetX"),
-1.0f,
TEXT("Overrides the left/right eye projection matrix x-offset, values are in clip space and values less than 0.0f disable the override. Default: -1.0f"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarHMDProjectionOffsetY(
TEXT("PixelStreaming2.HMD.ProjectionOffsetY"),
-1.0f,
TEXT("Overrides the left-right eye projection matrix y-offset, values are in clip space and values less than 0.0f disable the override. Default: -1.0f"),
ECVF_Default);
// End HMD CVars
// Begin Input CVars
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarInputAllowConsoleCommands(
TEXT("PixelStreaming2.AllowPixelStreamingCommands"),
false,
TEXT("If true browser can send consoleCommand payloads that execute in UE's console. Default: false"),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarInputKeyFilter(
TEXT("PixelStreaming2.KeyFilter"),
"",
TEXT("Comma separated list of keys to ignore from streaming clients. Default: \"\""),
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* Var) { Delegates()->OnInputKeyFilterChanged.Broadcast(Var); }),
ECVF_Default);
// End Input CVars
TArray<EVideoCodec> UPixelStreaming2PluginSettings::GetCodecPreferences()
{
TArray<EVideoCodec> OutCodecPreferences;
FString StringOptions = UPixelStreaming2PluginSettings::CVarWebRTCCodecPreferences.GetValueOnAnyThread();
if (StringOptions.IsEmpty())
{
return OutCodecPreferences;
}
TArray<FString> CodecArray;
StringOptions.ParseIntoArray(CodecArray, TEXT(","), true);
for (const FString& CodecString : CodecArray)
{
uint64 EnumIndex = StaticEnum<EVideoCodec>()->GetIndexByNameString(CodecString);
checkf(EnumIndex != INDEX_NONE, TEXT("CVar was not containing valid enum string"));
OutCodecPreferences.Add(static_cast<EVideoCodec>(StaticEnum<EVideoCodec>()->GetValueByIndex(EnumIndex)));
}
return OutCodecPreferences;
}
EPortAllocatorFlags UPixelStreaming2PluginSettings::GetPortAllocationFlags()
{
EPortAllocatorFlags OutPortAllocatorFlags = EPortAllocatorFlags::None;
FString StringOptions = UPixelStreaming2PluginSettings::CVarWebRTCPortAllocatorFlags.GetValueOnAnyThread();
if (StringOptions.IsEmpty())
{
return OutPortAllocatorFlags;
}
TArray<FString> FlagArray;
StringOptions.ParseIntoArray(FlagArray, TEXT(","), true);
int OptionCount = FlagArray.Num();
while (OptionCount > 0)
{
FString Flag = FlagArray[OptionCount - 1];
// Flags must match EpicRtc\Include\epic_rtc\core\connection_config.h
if (Flag == "DISABLE_UDP")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableUdp;
}
else if (Flag == "DISABLE_STUN")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableStun;
}
else if (Flag == "DISABLE_RELAY")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableRelay;
}
else if (Flag == "DISABLE_TCP")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableTcp;
}
else if (Flag == "ENABLE_IPV6")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::EnableIPV6;
}
else if (Flag == "ENABLE_SHARED_SOCKET")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::EnableSharedSocket;
}
else if (Flag == "ENABLE_STUN_RETRANSMIT_ATTRIBUTE")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::EnableStunRetransmitAttribute;
}
else if (Flag == "DISABLE_ADAPTER_ENUMERATION")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableAdapterEnumeration;
}
else if (Flag == "DISABLE_DEFAULT_LOCAL_CANDIDATE")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableDefaultLocalCandidate;
}
else if (Flag == "DISABLE_UDP_RELAY")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableUdpRelay;
}
else if (Flag == "DISABLE_COSTLY_NETWORKS")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableCostlyNetworks;
}
else if (Flag == "ENABLE_IPV6_ON_WIFI")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::EnableIPV6OnWifi;
}
else if (Flag == "ENABLE_ANY_ADDRESS_PORTS")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::EnableAnyAddressPort;
}
else if (Flag == "DISABLE_LINK_LOCAL_NETWORKS")
{
OutPortAllocatorFlags |= EPortAllocatorFlags::DisableLinkLocalNetworks;
}
else
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Unknown port allocator flag: {0}", Flag);
}
OptionCount--;
}
return OutPortAllocatorFlags;
}
void SetPortAllocationCVarFromProperty(UObject* This, FProperty* Property)
{
const FNumericProperty* EnumProperty = CastField<const FNumericProperty>(Property);
void* PropertyAddress = EnumProperty->ContainerPtrToValuePtr<void>(This);
EPortAllocatorFlags CurrentValue = static_cast<EPortAllocatorFlags>(EnumProperty->GetSignedIntPropertyValue(PropertyAddress));
FString CVarString = TEXT("");
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableUdp))
{
CVarString += "DISABLE_UDP,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableStun))
{
CVarString += "DISABLE_STUN,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableRelay))
{
CVarString += "DISABLE_RELAY,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableTcp))
{
CVarString += "DISABLE_TCP,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::EnableIPV6))
{
CVarString += "ENABLE_IPV6,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::EnableSharedSocket))
{
CVarString += "ENABLE_SHARED_SOCKET,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::EnableStunRetransmitAttribute))
{
CVarString += "ENABLE_STUN_RETRANSMIT_ATTRIBUTE,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableAdapterEnumeration))
{
CVarString += "DISABLE_ADAPTER_ENUMERATION,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableDefaultLocalCandidate))
{
CVarString += "DISABLE_DEFAULT_LOCAL_CANDIDATE,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableUdpRelay))
{
CVarString += "DISABLE_UDP_RELAY,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableCostlyNetworks))
{
CVarString += "DISABLE_COSTLY_NETWORKS,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::EnableIPV6OnWifi))
{
CVarString += "ENABLE_IPV6_ON_WIFI,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::EnableAnyAddressPort))
{
CVarString += "ENABLE_ANY_ADDRESS_PORTS,";
}
if (static_cast<bool>(CurrentValue & EPortAllocatorFlags::DisableLinkLocalNetworks))
{
CVarString += "DISABLE_LINK_LOCAL_NETWORKS,";
}
UPixelStreaming2PluginSettings::CVarWebRTCPortAllocatorFlags.AsVariable()->Set(*CVarString, ECVF_SetByProjectSetting);
}
void SetPortAllocationCVarAndPropertyFromValue(UObject* This, FProperty* Property, const FString& Value)
{
UPixelStreaming2PluginSettings::CVarWebRTCPortAllocatorFlags.AsVariable()->Set(*Value, ECVF_SetByCommandline);
const FNumericProperty* EnumProperty = CastField<const FNumericProperty>(Property);
int64* PropertyAddress = EnumProperty->ContainerPtrToValuePtr<int64>(This);
*PropertyAddress = static_cast<int64>(UPixelStreaming2PluginSettings::GetPortAllocationFlags());
}
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarWebRTCPortAllocatorFlags(
TEXT("PixelStreaming2.WebRTC.PortAllocatorFlags"),
TEXT(""),
TEXT("Sets the WebRTC port allocator flags. Format:\"DISABLE_UDP,DISABLE_STUN,...\""),
ECVF_Default);
TAutoConsoleVariable<int> UPixelStreaming2PluginSettings::CVarWebRTCMinPort(
TEXT("PixelStreaming2.WebRTC.MinPort"),
49152, // Default according to RFC5766
TEXT("Sets the minimum usable port for the WebRTC port allocator. Default: 49152"),
ECVF_Default);
TAutoConsoleVariable<int> UPixelStreaming2PluginSettings::CVarWebRTCMaxPort(
TEXT("PixelStreaming2.WebRTC.MaxPort"),
65535, // Default according to RFC5766
TEXT("Sets the maximum usable port for the WebRTC port allocator. Default: 65535"),
ECVF_Default);
TAutoConsoleVariable<FString> UPixelStreaming2PluginSettings::CVarWebRTCFieldTrials(
TEXT("PixelStreaming2.WebRTC.FieldTrials"),
TEXT(""),
TEXT("Sets the WebRTC field trials string. Format:\"TRIAL1/VALUE1/TRIAL2/VALUE2/\""),
ECVF_Default);
TAutoConsoleVariable<bool> UPixelStreaming2PluginSettings::CVarWebRTCDisableFrameDropper(
TEXT("PixelStreaming2.WebRTC.DisableFrameDropper"),
false,
TEXT("Disables the WebRTC internal frame dropper using the field trial WebRTC-FrameDropper/Disabled/"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarWebRTCVideoPacingMaxDelay(
TEXT("PixelStreaming2.WebRTC.VideoPacing.MaxDelay"),
-1.0f,
TEXT("Enables the WebRTC-Video-Pacing field trial and sets the max delay (ms) parameter. Default: -1.0f (values below zero are discarded.)"),
ECVF_Default);
TAutoConsoleVariable<float> UPixelStreaming2PluginSettings::CVarWebRTCVideoPacingFactor(
TEXT("PixelStreaming2.WebRTC.VideoPacing.Factor"),
-1.0f,
TEXT("Enables the WebRTC-Video-Pacing field trial and sets the video pacing factor parameter. Larger values are more lenient on larger bitrates. Default: -1.0f (values below zero are discarded.)"),
ECVF_Default);
UPixelStreaming2PluginSettings::FDelegates* UPixelStreaming2PluginSettings::DelegateSingleton = nullptr;
UPixelStreaming2PluginSettings::~UPixelStreaming2PluginSettings()
{
DelegateSingleton = nullptr;
}
FName UPixelStreaming2PluginSettings::GetCategoryName() const
{
return TEXT("Plugins");
}
#if WITH_EDITOR
FText UPixelStreaming2PluginSettings::GetSectionText() const
{
return NSLOCTEXT("PixelStreaming2Plugin", "PixelStreaming2SettingsSection", "PixelStreaming2");
}
void UPixelStreaming2PluginSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
FString PropertyName = PropertyChangedEvent.Property->GetNameCPP();
FString CVarName;
if (CVarName = FindCVarFromProperty(GetCmdArg, PropertyName); !CVarName.IsEmpty())
{
if (PropertyName == "WebRTCPortAllocatorFlags")
{
SetPortAllocationCVarFromProperty(this, PropertyChangedEvent.Property);
}
else if (PropertyName == "Codec" || PropertyName == "ScalabilityMode" || PropertyName == "EnableSimulcast")
{
VerifyVideoSettings();
}
else
{
SetCVarFromProperty(CVarName, PropertyChangedEvent.Property);
}
}
else if (CVarName = FindCVarFromProperty(GetMappedCmdArg, PropertyName); !CVarName.IsEmpty())
{
SetCVarFromProperty(CVarName, PropertyChangedEvent.Property);
}
}
void UPixelStreaming2PluginSettings::VerifyVideoSettings()
{
FProperty* SimulcastProperty = GetClass()->FindPropertyByName(TEXT("EnableSimulcast"));
FBoolProperty* SimulcastBoolProperty = CastField<FBoolProperty>(SimulcastProperty);
bool bSimulcastEnabled = SimulcastBoolProperty->GetPropertyValue_InContainer(this);
FProperty* CodecProperty = GetClass()->FindPropertyByName(TEXT("Codec"));
FStrProperty* CodecStrProperty = CastField<FStrProperty>(CodecProperty);
FString CodecString = CodecStrProperty->GetPropertyValue_InContainer(this);
FProperty* ScalabilityModeProperty = GetClass()->FindPropertyByName(TEXT("ScalabilityMode"));
FStrProperty* ScalabilityModeStrProperty = CastField<FStrProperty>(ScalabilityModeProperty);
FString ScalabilityModeString = ScalabilityModeStrProperty->GetPropertyValue_InContainer(this);
if (bSimulcastEnabled)
{
if (CodecString != TEXT("H264") && CodecString != TEXT("VP8"))
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Default codec ({0}) doesn't support simulcast! Resetting default codec to H.264", CodecString);
CodecStrProperty->SetPropertyValue_InContainer(this, TEXT("H264"));
}
}
CodecString = CodecStrProperty->GetPropertyValue_InContainer(this);
if ((CodecString == TEXT("H264") || CodecString == TEXT("VP8"))
&& (ScalabilityModeString != TEXT("L1T1") && ScalabilityModeString != TEXT("L1T2") && ScalabilityModeString != TEXT("L1T3")))
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Default codec ({0}) doesn't support the {1} scalability mode! Resetting scalability mode to L1T1", CodecString, ScalabilityModeString);
ScalabilityModeStrProperty->SetPropertyValue_InContainer(this, TEXT("L1T1"));
}
SetCVarFromProperty(FindCVarFromProperty(GetCmdArg, SimulcastProperty->GetNameCPP()), SimulcastProperty);
SetCVarFromProperty(FindCVarFromProperty(GetCmdArg, CodecProperty->GetNameCPP()), CodecProperty);
SetCVarFromProperty(FindCVarFromProperty(GetCmdArg, ScalabilityModeProperty->GetNameCPP()), ScalabilityModeProperty);
}
#endif
void UPixelStreaming2PluginSettings::SetCVarAndPropertyFromValue(const FString& CVarName, FProperty* Property, const FString& Value)
{
IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(*CVarName);
if (!CVar)
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Failed to find CVar: {0}", CVarName);
return;
}
if (FByteProperty* ByteProperty = CastField<FByteProperty>(Property); ByteProperty != NULL && ByteProperty->Enum != NULL)
{
CVar->Set(FCString::Atoi(*Value), ECVF_SetByCommandline);
ByteProperty->SetPropertyValue_InContainer(this, FCString::Atoi(*Value));
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [{2}] from command line", CVarName, Property->GetNameCPP(), FCString::Atoi(*Value));
}
else if (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
{
int64 EnumIndex = EnumProperty->GetEnum()->GetIndexByNameString(Value.Replace(TEXT("_"), TEXT("")));
if (EnumIndex != INDEX_NONE)
{
CVar->Set(*EnumProperty->GetEnum()->GetNameStringByIndex(EnumIndex), ECVF_SetByCommandline);
FNumericProperty* UnderlyingProp = EnumProperty->GetUnderlyingProperty();
int64* PropertyAddress = EnumProperty->ContainerPtrToValuePtr<int64>(this);
*PropertyAddress = EnumProperty->GetEnum()->GetValueByIndex(EnumIndex);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [{2}] from command line", CVarName, Property->GetNameCPP(), EnumProperty->GetEnum()->GetNameStringByIndex(EnumIndex));
}
else
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "{0} is not a valid enum value for {1}", Value, EnumProperty->GetEnum()->CppType);
}
}
else if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
{
bool bValue = false;
if (Value.Equals(FString(TEXT("true")), ESearchCase::IgnoreCase))
{
bValue = true;
}
else if (Value.Equals(FString(TEXT("false")), ESearchCase::IgnoreCase))
{
bValue = false;
}
CVar->Set(bValue, ECVF_SetByCommandline);
BoolProperty->SetPropertyValue_InContainer(this, bValue);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [{2}] from command line", CVarName, Property->GetNameCPP(), bValue);
}
else if (FIntProperty* IntProperty = CastField<FIntProperty>(Property))
{
CVar->Set(FCString::Atoi(*Value), ECVF_SetByCommandline);
IntProperty->SetPropertyValue_InContainer(this, FCString::Atoi(*Value));
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [{2}] from command line", CVarName, Property->GetNameCPP(), FCString::Atoi(*Value));
}
else if (FFloatProperty* FloatProperty = CastField<FFloatProperty>(Property))
{
CVar->Set(FCString::Atof(*Value), ECVF_SetByCommandline);
FloatProperty->SetPropertyValue_InContainer(this, FCString::Atof(*Value));
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [{2}] from command line", CVarName, Property->GetNameCPP(), FCString::Atof(*Value));
}
else if (FStrProperty* StringProperty = CastField<FStrProperty>(Property))
{
CVar->Set(*Value, ECVF_SetByCommandline);
StringProperty->SetPropertyValue_InContainer(this, *Value);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [\"{2}\"] from command line", CVarName, Property->GetNameCPP(), Value);
}
else if (FNameProperty* NameProperty = CastField<FNameProperty>(Property))
{
CVar->Set(*Value, ECVF_SetByCommandline);
NameProperty->SetPropertyValue_InContainer(this, FName(*Value));
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [\"{2}\"] from command line", CVarName, Property->GetNameCPP(), Value);
}
else if (FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
// TODO (william.belcher): Only FString array properties are currently supported
CVar->Set(*Value, ECVF_SetByCommandline);
TArray<FString> StringArray;
Value.ParseIntoArray(StringArray, TEXT(","), true);
TArray<FString>& Array = *ArrayProperty->ContainerPtrToValuePtr<TArray<FString>>(this);
Array = StringArray;
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] and Property [{1}] to [\"{2}\"] from command line", CVarName, Property->GetNameCPP(), Value);
}
}
void UPixelStreaming2PluginSettings::SetCVarFromProperty(const FString& CVarName, FProperty* Property)
{
IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(*CVarName);
if (!CVar)
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Failed to find CVar: {0}", CVarName);
return;
}
if (FByteProperty* ByteProperty = CastField<FByteProperty>(Property); ByteProperty != NULL && ByteProperty->Enum != NULL)
{
CVar->Set(ByteProperty->GetPropertyValue_InContainer(this), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [{1}] from Property [{2}]", CVarName, ByteProperty->GetPropertyValue_InContainer(this), Property->GetNameCPP());
}
else if (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
{
void* PropertyAddress = EnumProperty->ContainerPtrToValuePtr<void>(this);
int64 CurrentValue = EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(PropertyAddress);
CVar->Set(*EnumProperty->GetEnum()->GetNameStringByValue(CurrentValue), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [{1}] from Property [{2}]", CVarName, EnumProperty->GetEnum()->GetNameStringByValue(CurrentValue), Property->GetNameCPP());
}
else if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
{
CVar->Set(BoolProperty->GetPropertyValue_InContainer(this), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [{1}] from Property [{2}]", CVarName, BoolProperty->GetPropertyValue_InContainer(this), Property->GetNameCPP());
}
else if (FIntProperty* IntProperty = CastField<FIntProperty>(Property))
{
CVar->Set(IntProperty->GetPropertyValue_InContainer(this), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [{1}] from Property [{2}]", CVarName, IntProperty->GetPropertyValue_InContainer(this), Property->GetNameCPP());
}
else if (FFloatProperty* FloatProperty = CastField<FFloatProperty>(Property))
{
CVar->Set(FloatProperty->GetPropertyValue_InContainer(this), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [{1}] from Property [{2}]", CVarName, FloatProperty->GetPropertyValue_InContainer(this), Property->GetNameCPP());
}
else if (FStrProperty* StringProperty = CastField<FStrProperty>(Property))
{
CVar->Set(*StringProperty->GetPropertyValue_InContainer(this), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [\"{1}\"] from Property [{2}]", CVarName, StringProperty->GetPropertyValue_InContainer(this), Property->GetNameCPP());
}
else if (FNameProperty* NameProperty = CastField<FNameProperty>(Property))
{
CVar->Set(*NameProperty->GetPropertyValue_InContainer(this).ToString(), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [\"{1}\"] from Property [{2}]", CVarName, NameProperty->GetPropertyValue_InContainer(this), Property->GetNameCPP());
}
else if (FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
// TODO (william.belcher): Only FString array properties are currently supported
TArray<FString> Array = *ArrayProperty->ContainerPtrToValuePtr<TArray<FString>>(this);
CVar->Set(*FString::Join(Array, TEXT(",")), ECVF_SetByProjectSetting);
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Setting CVar [{0}] to [\"{1}\"] from Property [{2}]", CVarName, FString::Join(Array, TEXT(",")), Property->GetNameCPP());
}
}
void UPixelStreaming2PluginSettings::InitializeCVarsFromProperties()
{
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Initializing CVars from ini");
for (FProperty* Property = GetClass()->PropertyLink; Property; Property = Property->PropertyLinkNext)
{
if (!Property->HasAnyPropertyFlags(CPF_Config))
{
continue;
}
// Handle the majority of commandline argument
if (Property->GetNameCPP() == "WebRTCPortAllocatorFlags")
{
SetPortAllocationCVarFromProperty(this, Property);
continue;
}
FString CVarName;
if (CVarName = FindCVarFromProperty(GetCmdArg, Property->GetNameCPP()); !CVarName.IsEmpty())
{
SetCVarFromProperty(CVarName, Property);
continue;
}
else if (CVarName = FindCVarFromProperty(GetMappedCmdArg, Property->GetNameCPP()); !CVarName.IsEmpty())
{
SetCVarFromProperty(CVarName, Property);
continue;
}
}
}
void UPixelStreaming2PluginSettings::ValidateCommandLineArgs()
{
FString CommandLine = FCommandLine::Get();
TArray<FString> CommandArray;
CommandLine.ParseIntoArray(CommandArray, TEXT(" "), true);
for (FString Command : CommandArray)
{
Command.RemoveFromStart(TEXT("-"));
if (!Command.StartsWith("PixelStreaming"))
{
continue;
}
// Get the pure command line arg from an arg that contains an '=', eg PixelStreamingURL=
FString CurrentCommandLineArg = Command;
if (Command.Contains("="))
{
Command.Split(TEXT("="), &CurrentCommandLineArg, nullptr);
}
bool bValidArg = false;
for (const TPair<FString, FString>& Pair : GetCmdArg)
{
FString ValidCommandLineArg = ConsoleVariableToCommandArgParam(Pair.Key);
if (CurrentCommandLineArg == ValidCommandLineArg)
{
bValidArg = true;
break;
}
}
if (!bValidArg)
{
for (const TPair<FString, FString>& Pair : GetMappedCmdArg)
{
FString ValidCommandLineArg = ConsoleVariableToCommandArgParam(Pair.Key);
if (CurrentCommandLineArg == ValidCommandLineArg)
{
bValidArg = true;
break;
}
}
}
if (!bValidArg)
{
for (const TPair<FString, FString>& Pair : GetLegacyCmdArg)
{
FString ValidCommandLineArg = ConsoleVariableToCommandArgParam(Pair.Key);
if (CurrentCommandLineArg == ValidCommandLineArg)
{
bValidArg = true;
break;
}
}
}
if (!bValidArg)
{
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "Unknown PixelStreaming command line arg: {0}", CurrentCommandLineArg);
}
}
}
void UPixelStreaming2PluginSettings::ParseCommandlineArgs()
{
UE_LOGFMT(LogPixelStreaming2Settings, Verbose, "Updating CVars and properties with command line args");
for (const TPair<FString, FString>& Pair : GetCmdArg)
{
FString CVarString = Pair.Key;
FString PropertyName = Pair.Value;
FProperty* Property = GetClass()->FindPropertyByName(FName(*PropertyName));
if (!Property || !Property->HasAnyPropertyFlags(CPF_Config))
{
continue;
}
if (PropertyName == "WebRTCPortAllocatorFlags")
{
FString ConsoleString;
if (FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(CVarString), ConsoleString))
{
SetPortAllocationCVarAndPropertyFromValue(this, Property, ConsoleString);
}
continue;
}
// Handle a directly parsable commandline
FString ConsoleString;
if (FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(CVarString), ConsoleString))
{
SetCVarAndPropertyFromValue(CVarString, Property, ConsoleString);
}
else if (FParse::Param(FCommandLine::Get(), *ConsoleVariableToCommandArgParam(CVarString)))
{
SetCVarAndPropertyFromValue(CVarString, Property, TEXT("true"));
}
}
for (const TPair<FString, FString>& Pair : GetMappedCmdArg)
{
FString CVarString = Pair.Key;
FString PropertyName = Pair.Value;
FProperty* Property = GetClass()->FindPropertyByName(FName(*PropertyName));
if (!Property || !Property->HasAnyPropertyFlags(CPF_Config))
{
continue;
}
// Handle a directly parsable commandline
FString ConsoleString;
if (FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(CVarString), ConsoleString))
{
SetCVarAndPropertyFromValue(CVarString, Property, ConsoleString);
}
}
}
void UPixelStreaming2PluginSettings::ParseLegacyCommandlineArgs()
{
FString SignallingServerIP;
FString SignallingServerPort;
for (const TPair<FString, FString>& Pair : GetLegacyCmdArg)
{
FString LegacyCVarString = Pair.Key;
FString PropertyName = Pair.Value;
FProperty* Property = GetClass()->FindPropertyByName(FName(*PropertyName));
if (!Property || !Property->HasAnyPropertyFlags(CPF_Config))
{
continue;
}
FString NewCVarString;
if (FString CmdArgCVar = FindCVarFromProperty(GetCmdArg, PropertyName); !CmdArgCVar.IsEmpty())
{
NewCVarString = CmdArgCVar;
}
else if (FString MappedCmdArgCVar = FindCVarFromProperty(GetMappedCmdArg, PropertyName); !MappedCmdArgCVar.IsEmpty())
{
NewCVarString = MappedCmdArgCVar;
}
else
{
continue;
}
if (LegacyCVarString == "PixelStreaming2.Encoder.MinQp")
{
int32 MinQP;
if (FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(LegacyCVarString), MinQP))
{
int32 ScaledQuality = 100.0f * (1.0f - (FMath::Clamp<int32>(MinQP, 0, 51) / 51.0f));
SetCVarAndPropertyFromValue(NewCVarString, Property, FString::Printf(TEXT("%d"), ScaledQuality));
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "PixelStreamingEncoderMinQp is a legacy setting, converted to PixelStreamingEncoderMaxQuality={0}", CVarEncoderMaxQuality.GetValueOnAnyThread());
continue;
}
}
else if (LegacyCVarString == "PixelStreaming2.Encoder.MaxQp")
{
int32 MaxQP;
if (FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(LegacyCVarString), MaxQP))
{
int32 ScaledQuality = 100.0f * (1.0f - (FMath::Clamp<int32>(MaxQP, 0, 51) / 51.0f));
SetCVarAndPropertyFromValue(NewCVarString, Property, FString::Printf(TEXT("%d"), ScaledQuality));
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "PixelStreamingEncoderMaxQp is a legacy setting, converted to PixelStreamingEncoderMinQuality={0}", CVarEncoderMinQuality.GetValueOnAnyThread());
continue;
}
}
else if (LegacyCVarString == "PixelStreaming2.IP" || LegacyCVarString == "PixelStreaming2.Port")
{
if (LegacyCVarString == "PixelStreaming2.IP")
{
FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(LegacyCVarString), SignallingServerIP);
}
else if (LegacyCVarString == "PixelStreaming2.Port")
{
FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(LegacyCVarString), SignallingServerPort);
}
if (!SignallingServerIP.IsEmpty() && !SignallingServerPort.IsEmpty())
{
FString LegacyUrl = TEXT("ws://") + SignallingServerIP + TEXT(":") + SignallingServerPort;
SetCVarAndPropertyFromValue(NewCVarString, Property, LegacyUrl);
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "PixelStreamingIP and PixelStreamingPort are legacy settings converted to -PixelStreamingConnectionURL={0}", CVarConnectionURL.GetValueOnAnyThread());
}
continue;
}
FString ConsoleString;
if (FParse::Value(FCommandLine::Get(), *ConsoleVariableToCommandArgValue(LegacyCVarString), ConsoleString))
{
SetCVarAndPropertyFromValue(NewCVarString, Property, ConsoleString);
}
else if (FParse::Param(FCommandLine::Get(), *ConsoleVariableToCommandArgParam(LegacyCVarString)))
{
SetCVarAndPropertyFromValue(NewCVarString, Property, TEXT("true"));
}
else
{
continue;
}
UE_LOGFMT(LogPixelStreaming2Settings, Warning, "{0} is a legacy setting and has been converted to {1}", ConsoleVariableToCommandArgParam(LegacyCVarString), ConsoleVariableToCommandArgParam(NewCVarString));
}
ParseLegacyCommandLineOption(TEXT("PixelStreamingDebugDumpFrame"), CVarEncoderDebugDumpFrame);
// End legacy PixelStreaming command line args
}
void UPixelStreaming2PluginSettings::PostInitProperties()
{
Super::PostInitProperties();
UE_LOGFMT(LogPixelStreaming2Settings, Log, "Initialising Pixel Streaming settings.");
// Set all the CVars to reflect the state of the ini
InitializeCVarsFromProperties();
// Validate command line args to log if they're invalid
ValidateCommandLineArgs();
// Update CVars and properties based on command line args
ParseCommandlineArgs();
// Handle parsing of legacy command line args (such as -PixelStreamingUrl) after .ini and new commandline args.
ParseLegacyCommandlineArgs();
// These cvars don't have matching properties so need to be manually parsed
ParseLegacyCommandLineOption(*ConsoleVariableToCommandArgParam(TEXT("PixelStreaming2.Encoder.DumpDebugFrames")), CVarEncoderDebugDumpFrame);
ParseLegacyCommandLineOption(*ConsoleVariableToCommandArgParam(TEXT("PixelStreaming2.DumpDebugAudio")), CVarDebugDumpAudio);
}
UPixelStreaming2PluginSettings::FDelegates* UPixelStreaming2PluginSettings::Delegates()
{
if (DelegateSingleton == nullptr && !IsEngineExitRequested())
{
DelegateSingleton = new UPixelStreaming2PluginSettings::FDelegates();
return DelegateSingleton;
}
return DelegateSingleton;
}
TArray<FString> UPixelStreaming2PluginSettings::GetVideoCodecOptions() const
{
FProperty* Property = GetClass()->FindPropertyByName(TEXT("EnableSimulcast"));
FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property);
bool bSimulcastEnabled = BoolProperty->GetPropertyValue_InContainer(this);
if (bSimulcastEnabled)
{
return {
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::H264),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::VP8)
};
}
return {
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::AV1),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::H264),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::VP8),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::VP9)
};
}
TArray<FString> UPixelStreaming2PluginSettings::GetScalabilityModeOptions() const
{
FProperty* Property = GetClass()->FindPropertyByName(TEXT("Codec"));
FStrProperty* StrProperty = CastField<FStrProperty>(Property);
FString SelectedCodec = StrProperty->GetPropertyValue_InContainer(this);
// H.264 and VP8 only support temporal scalability
bool bRestrictModes = SelectedCodec == TEXT("H264") || SelectedCodec == TEXT("VP8");
if (bRestrictModes)
{
return {
UE::PixelStreaming2::GetCVarStringFromEnum(EScalabilityMode::L1T1),
UE::PixelStreaming2::GetCVarStringFromEnum(EScalabilityMode::L1T2),
UE::PixelStreaming2::GetCVarStringFromEnum(EScalabilityMode::L1T3),
};
}
TArray<FString> ScalabilityModes;
for (uint32 i = 0; i <= static_cast<uint32>(EScalabilityMode::None); i++)
{
ScalabilityModes.Add(UE::PixelStreaming2::GetCVarStringFromEnum(static_cast<EScalabilityMode>(i)));
}
return ScalabilityModes;
}
TArray<FString> UPixelStreaming2PluginSettings::GetWebRTCCodecPreferencesOptions() const
{
TSet<FString> PossibleCodecs = {
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::AV1),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::H264),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::VP9),
UE::PixelStreaming2::GetCVarStringFromEnum(EVideoCodec::VP8)
};
FProperty* Property = GetClass()->FindPropertyByName(TEXT("WebRTCCodecPreferences"));
FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property);
TArray<FString> CurrentCodecArray = *ArrayProperty->ContainerPtrToValuePtr<TArray<FString>>(this);
for (FString VideoCodec : CurrentCodecArray)
{
PossibleCodecs.Remove(VideoCodec);
}
return PossibleCodecs.Array();
}
TArray<FString> UPixelStreaming2PluginSettings::GetDefaultStreamerTypeOptions() const
{
return IPixelStreaming2StreamerFactory::GetAvailableFactoryTypes();
}