// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Map.h" #include "Containers/UnrealString.h" #include "Misc/Optional.h" #include "Misc/TVariant.h" #include "Templates/SharedPointer.h" #include "UObject/NameTypes.h" #include "epic_rtc/core/stats.h" namespace UE::PixelStreaming2 { class FStats; namespace RTCStatCategories { const FString LocalVideoTrack = TEXT("video-track-outbound"); const FString LocalAudioTrack = TEXT("audio-track-outbound"); const FString VideoSource = TEXT("video-source"); const FString AudioSource = TEXT("audio-source"); const FString VideoCodec = TEXT("video-codec"); const FString AudioCodec = TEXT("audio-codec"); const FString DataChannel = TEXT("data-channel"); const FString RemoteVideoTrack = TEXT("video-track-inbound"); const FString RemoteAudioTrack = TEXT("audio-track-inbound"); const FString CandidatePair = TEXT("candidate-pair"); } // namespace RTCStatCategories enum PIXELSTREAMING2RTC_API EDisplayFlags : uint8 { HIDDEN = 0, TEXT = 1 << 0, GRAPH = 1 << 1, }; struct PIXELSTREAMING2RTC_API FStatConfig { FName Name; TOptional Alias; EDisplayFlags DisplayFlags = EDisplayFlags::TEXT; }; using FStatVariant = TVariant; class PIXELSTREAMING2RTC_API FStat { public: FStat() = default; FStat(FStatConfig Config, double InitialValue, int NDecimalPlacesToPrint = 0, bool bSmooth = false); FStat(FStatConfig Config, FString InitialValue); FStat(FStatConfig Config, bool bInitialValue); FStat(const FStat& Other); bool IsNumeric() const; bool IsTextual() const; bool IsBoolean() const; FString ToString(); bool SetValue(FStatVariant ValueVariant); template T GetValue() { checkf(false, TEXT("Trying to get value with incompatible container. Please use FString, double or bool!")); } template <> FString GetValue(); template <> double GetValue(); template <> bool GetValue(); template T GetPrevValue() { checkf(false, TEXT("Trying to get value with incompatible container. Please use FString, double or bool!")); } template <> FString GetPrevValue(); template <> double GetPrevValue(); template <> bool GetPrevValue(); bool operator==(const FStat& Other) const; bool IsHidden(); bool ShouldGraph(); bool ShouldDisplayText(); FName GetName() const; FName GetDisplayName() const; private: double CalcMA(double InPrevAvg, int InNumSamples, double InValue); double CalcEMA(double InPrevAvg, int InNumSamples, double InValue); protected: FName Name; EDisplayFlags DisplayFlags = EDisplayFlags::TEXT; TOptional Alias; // variable used for calculating and displaying numeric stat data int NDecimalPlacesToPrint = 0; bool bSmooth = false; int NumSamples = 0; FStatVariant StatVariant; FStatVariant PrevStatVariant; }; FORCEINLINE uint32 GetTypeHash(const FStat& Obj) { // From UnrealString.h return GetTypeHash(Obj.GetName()); } class PIXELSTREAMING2RTC_API FRTCStatsCollector { public: static TSharedPtr Create(const FString& PlayerId); private: FRTCStatsCollector(); explicit FRTCStatsCollector(const FString& PlayerId); void OnWebRTCDisableStatsChanged(IConsoleVariable* Var); public: void Process(const EpicRtcConnectionStats& InStats); private: class FStatsSink { public: FStatsSink(FName InCategory); virtual ~FStatsSink() = default; virtual FStat* Get(const FName& StatName) { return Stats.Find(StatName); } virtual void PostProcess(FStats* PSStats, const FString& PeerId, double SecondsDelta); protected: // Stats that are stored as is. TMap Stats; // Stats we calculate based on the stats map above. This calculation is done in FStatsSink::PostProcess by the `Calculators` below. TArray(FStatsSink&, double)>> Calculators; protected: FName Category; }; class FRTPLocalVideoTrackStatsSink : public FStatsSink { public: FRTPLocalVideoTrackStatsSink(FName Category); virtual ~FRTPLocalVideoTrackStatsSink() = default; void Process(const EpicRtcLocalTrackRtpStats& InStats, const FString& PeerId, double SecondsDelta); }; class FRTPLocalAudioTrackStatsSink : public FStatsSink { public: FRTPLocalAudioTrackStatsSink(FName Category); virtual ~FRTPLocalAudioTrackStatsSink() = default; void Process(const EpicRtcLocalTrackRtpStats& InStats, const FString& PeerId, double SecondsDelta); }; class FRTPRemoteTrackStatsSink : public FStatsSink { public: FRTPRemoteTrackStatsSink(FName Category); virtual ~FRTPRemoteTrackStatsSink() = default; void Process(const EpicRtcRemoteTrackRtpStats& InStats, const FString& PeerId, double SecondsDelta); }; class FVideoSourceStatsSink : public FStatsSink { public: FVideoSourceStatsSink(FName Category); virtual ~FVideoSourceStatsSink() = default; void Process(const EpicRtcVideoSourceStats& InStats, const FString& PeerId, double SecondsDelta); }; class FVideoCodecStatsSink : public FStatsSink { public: FVideoCodecStatsSink(FName Category); virtual ~FVideoCodecStatsSink() = default; void Process(const EpicRtcCodecStats& InStats, const FString& PeerId, double SecondsDelta); }; class FAudioSourceStatsSink : public FStatsSink { public: FAudioSourceStatsSink(FName Category); virtual ~FAudioSourceStatsSink() = default; void Process(const EpicRtcAudioSourceStats& InStats, const FString& PeerId, double SecondsDelta); }; class FAudioCodecStatsSink : public FStatsSink { public: FAudioCodecStatsSink(FName Category); virtual ~FAudioCodecStatsSink() = default; void Process(const EpicRtcCodecStats& InStats, const FString& PeerId, double SecondsDelta); }; /** * ---------- FDataChannelSink ------------------- */ class FDataTrackStatsSink : public FStatsSink { public: FDataTrackStatsSink(FName Category); virtual ~FDataTrackStatsSink() = default; void Process(const EpicRtcDataTrackStats& InStats, const FString& PeerId, double SecondsDelta); }; class FCandidatePairStatsSink : public FStatsSink { public: FCandidatePairStatsSink(FName Category); virtual ~FCandidatePairStatsSink() = default; void Process(const EpicRtcIceCandidatePairStats& InStats, const FString& PeerId, double SecondsDelta); }; private: FString AssociatedPlayerId; uint64 LastCalculationCycles; bool bIsEnabled; // index, ssrc sink TMap>> LocalVideoTrackSinks; TMap>> LocalAudioTrackSinks; TMap>> RemoteVideoTrackSinks; TMap>> RemoteAudioTrackSinks; TMap> VideoSourceSinks; TMap> VideoCodecSinks; TMap> AudioSourceSinks; TMap> AudioCodecSinks; TMap> DataTrackSinks; TUniquePtr CandidatePairStatsSink; }; } // namespace UE::PixelStreaming2