Files
UnrealEngine/Engine/Plugins/Media/ElectraPlayer/Source/ElectraPlayerRuntime/Private/ElectraPlayer.h
2025-05-18 13:04:45 +08:00

820 lines
35 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/UnrealString.h"
#include "Containers/Queue.h"
#include "Containers/SortedMap.h"
#include "Misc/Guid.h"
#include "Templates/SharedPointer.h"
#include "Templates/Greater.h"
#include "IAnalyticsProviderET.h"
#include "IElectraPlayerInterface.h"
#include "Player/AdaptiveStreamingPlayer.h"
#include "MediaStreamMetadata.h"
#include "PlayerRuntimeGlobal.h"
class FVideoDecoderOutput;
using FVideoDecoderOutputPtr = TSharedPtr<FVideoDecoderOutput, ESPMode::ThreadSafe>;
class IAudioDecoderOutput;
using IAudioDecoderOutputPtr = TSharedPtr<IAudioDecoderOutput, ESPMode::ThreadSafe>;
class IMetaDataDecoderOutput;
using IMetaDataDecoderOutputPtr = TSharedPtr<IMetaDataDecoderOutput, ESPMode::ThreadSafe>;
class ISubtitleDecoderOutput;
using ISubtitleDecoderOutputPtr = TSharedPtr<ISubtitleDecoderOutput, ESPMode::ThreadSafe>;
namespace Electra
{
class IVideoDecoderResourceDelegate;
}
class FElectraRendererVideo;
class FElectraRendererAudio;
using namespace Electra;
DECLARE_MULTICAST_DELEGATE_TwoParams(FElectraPlayerSendAnalyticMetricsDelegate, const TSharedPtr<IAnalyticsProviderET>& /*AnalyticsProvider*/, const FGuid& /*PlayerGuid*/);
DECLARE_MULTICAST_DELEGATE_OneParam(FElectraPlayerSendAnalyticMetricsPerMinuteDelegate, const TSharedPtr<IAnalyticsProviderET>& /*AnalyticsProvider*/);
DECLARE_MULTICAST_DELEGATE_TwoParams(FElectraPlayerReportVideoStreamingErrorDelegate, const FGuid& /*PlayerGuid*/, const FString& /*LastError*/);
DECLARE_MULTICAST_DELEGATE_FourParams(FElectraPlayerReportSubtitlesMetricsDelegate, const FGuid& /*PlayerGuid*/, const FString& /*URL*/, double /*ResponseTime*/, const FString& /*LastError*/);
class FElectraPlayer
: public IElectraPlayerInterface
, public IAdaptiveStreamingPlayerMetrics
{
public:
FElectraPlayer(const TSharedPtr<IElectraPlayerAdapterDelegate, ESPMode::ThreadSafe>& AdapterDelegate,
FElectraPlayerSendAnalyticMetricsDelegate& InSendAnalyticMetricsDelegate,
FElectraPlayerSendAnalyticMetricsPerMinuteDelegate& InSendAnalyticMetricsPerMinuteDelegate,
FElectraPlayerReportVideoStreamingErrorDelegate& InReportVideoStreamingErrorDelegate,
FElectraPlayerReportSubtitlesMetricsDelegate& InReportSubtitlesFileMetricsDelegate);
~FElectraPlayer();
void OnVideoDecoded(const FVideoDecoderOutputPtr& DecoderOutput, bool bDoNotRender);
void OnVideoFlush();
void OnAudioDecoded(const IAudioDecoderOutputPtr& DecoderOutput);
void OnAudioFlush();
void OnSubtitleDecoded(ISubtitleDecoderOutputPtr DecoderOutput);
void OnSubtitleFlush();
void SendAnalyticMetrics(const TSharedPtr<IAnalyticsProviderET>& AnalyticsProvider, const FGuid& InPlayerGuid);
void SendAnalyticMetricsPerMinute(const TSharedPtr<IAnalyticsProviderET>& AnalyticsProvider);
void SendPendingAnalyticMetrics(const TSharedPtr<IAnalyticsProviderET>& AnalyticsProvider);
void ReportVideoStreamingError(const FGuid& InPlayerGuid, const FString& LastError);
void ReportSubtitlesMetrics(const FGuid& InPlayerGuid, const FString& URL, double ResponseTime, const FString& LastError);
void DropOldFramesFromPresentationQueue();
bool CanPresentVideoFrames(uint64 NumFrames);
bool CanPresentAudioFrames(uint64 NumFrames);
FString GetUrl() const override
{
return MediaUrl;
}
void SetGuid(const FGuid& Guid) override
{
PlayerGuid = Guid;
}
void SetAsyncResourceReleaseNotification(IAsyncResourceReleaseNotifyContainer* AsyncResourceReleaseNotification) override;
// -------- PlayerAdapter (Plugin/Native) API
bool OpenInternal(const FString& Url, const FParamDict& PlayerOptions, const FPlaystartOptions& InPlaystartOptions, EOpenType InOpenType) override;
void CloseInternal(bool bKillAfterClose) override;
void Tick(FTimespan DeltaTime, FTimespan Timecode) override;
bool IsKillAfterCloseAllowed() const override { return bAllowKillAfterCloseEvent; }
EPlayerState GetState() const override;
EPlayerStatus GetStatus() const override;
bool IsLooping() const override;
bool SetLooping(bool bLooping) override;
int32 GetLoopCount() const override;
FTimespan GetTime() const override;
FTimespan GetDuration() const override;
bool IsLive() const override;
FTimespan GetSeekableDuration() const override;
void SetPlaybackRange(const FPlaybackRange& InPlaybackRange) override;
void GetPlaybackRange(FPlaybackRange& OutPlaybackRange) const override;
TRange<FTimespan> GetPlaybackRange(ETimeRangeType InRangeToGet) const override;
TRangeSet<float> GetSupportedRates(EPlayRateType InPlayRateType) const override;
float GetRate() const override;
bool SetRate(float Rate) override;
bool Seek(const FTimespan& Time, const FSeekParam& Param) override;
void SetFrameAccurateSeekMode(bool bEnableFrameAccuracy) override;
bool GetAudioTrackFormat(int32 TrackIndex, int32 FormatIndex, FAudioTrackFormat& OutFormat) const override;
bool GetVideoTrackFormat(int32 TrackIndex, int32 FormatIndex, FVideoTrackFormat& OutFormat) const override;
int32 GetNumTracks(EPlayerTrackType TrackType) const override;
int32 GetNumTrackFormats(EPlayerTrackType TrackType, int32 TrackIndex) const override;
int32 GetSelectedTrack(EPlayerTrackType TrackType) const override;
FText GetTrackDisplayName(EPlayerTrackType TrackType, int32 TrackIndex) const override;
int32 GetTrackFormat(EPlayerTrackType TrackType, int32 TrackIndex) const override;
FString GetTrackLanguage(EPlayerTrackType TrackType, int32 TrackIndex) const override;
FString GetTrackName(EPlayerTrackType TrackType, int32 TrackIndex) const override;
bool SelectTrack(EPlayerTrackType TrackType, int32 TrackIndex) override;
int32 GetNumVideoStreams(int32 TrackIndex) const override;
bool GetVideoStreamFormat(FVideoStreamFormat& OutFormat, int32 InTrackIndex, int32 InStreamIndex) const override;
bool GetActiveVideoStreamFormat(FVideoStreamFormat& OutFormat) const override;
Electra::FVariantValue GetMediaInfo(FName InInfoName) const override;
TSharedPtr<TMap<FString, TArray<TSharedPtr<Electra::IMediaStreamMetadata::IItem, ESPMode::ThreadSafe>>>, ESPMode::ThreadSafe> GetMediaMetadata() const override;
bool GetStreamBufferInformation(FStreamBufferInfo& OutBufferInformation, EPlayerTrackType InTrackType) const override;
void SuspendOrResumeDecoders(bool bSuspend, const Electra::FParamDict& InOptions) override;
private:
DECLARE_DELEGATE_TwoParams(FOnMediaPlayerEventReceivedDelegate, TSharedPtrTS<IAdaptiveStreamingPlayerAEMSEvent> /*InEvent*/, IAdaptiveStreamingPlayerAEMSReceiver::EDispatchMode /*InDispatchMode*/);
class FAEMSEventReceiver : public IAdaptiveStreamingPlayerAEMSReceiver
{
public:
virtual ~FAEMSEventReceiver() = default;
FOnMediaPlayerEventReceivedDelegate& GetEventReceivedDelegate()
{ return EventReceivedDelegate; }
private:
virtual void OnMediaPlayerEventReceived(TSharedPtrTS<IAdaptiveStreamingPlayerAEMSEvent> InEvent, IAdaptiveStreamingPlayerAEMSReceiver::EDispatchMode InDispatchMode) override
{ EventReceivedDelegate.ExecuteIfBound(InEvent, InDispatchMode); }
FOnMediaPlayerEventReceivedDelegate EventReceivedDelegate;
};
DECLARE_DELEGATE_OneParam(FOnMediaPlayerSubtitleReceivedDelegate, ISubtitleDecoderOutputPtr);
DECLARE_DELEGATE(FOnMediaPlayerSubtitleFlushDelegate);
class FSubtitleEventReceiver : public IAdaptiveStreamingPlayerSubtitleReceiver
{
public:
virtual ~FSubtitleEventReceiver() = default;
FOnMediaPlayerSubtitleReceivedDelegate& GetSubtitleReceivedDelegate()
{ return SubtitleReceivedDelegate; }
FOnMediaPlayerSubtitleFlushDelegate& GetSubtitleFlushDelegate()
{ return SubtitleFlushDelegate; }
private:
virtual void OnMediaPlayerSubtitleReceived(ISubtitleDecoderOutputPtr Subtitle) override
{ SubtitleReceivedDelegate.ExecuteIfBound(Subtitle); }
virtual void OnMediaPlayerFlushSubtitles() override
{ SubtitleFlushDelegate.ExecuteIfBound(); }
FOnMediaPlayerSubtitleReceivedDelegate SubtitleReceivedDelegate;
FOnMediaPlayerSubtitleFlushDelegate SubtitleFlushDelegate;
};
struct FPlayerMetricEventBase
{
enum class EType
{
OpenSource,
ReceivedMainPlaylist,
ReceivedPlaylists,
TracksChanged,
PlaylistDownload,
CleanStart,
BufferingStart,
BufferingEnd,
Bandwidth,
BufferUtilization,
SegmentDownload,
LicenseKey,
DataAvailabilityChange,
VideoQualityChange,
AudioQualityChange,
CodecFormatChange,
PrerollStart,
PrerollEnd,
PlaybackStart,
PlaybackPaused,
PlaybackResumed,
PlaybackEnded,
JumpInPlayPosition,
PlaybackStopped,
SeekCompleted,
MediaMetadataChanged,
Error,
LogMessage,
DroppedVideoFrame,
DroppedAudioFrame
};
FPlayerMetricEventBase(EType InType) : Type(InType) {}
virtual ~FPlayerMetricEventBase() = default;
EType Type;
};
struct FPlayerMetricEvent_OpenSource : public FPlayerMetricEventBase
{
FPlayerMetricEvent_OpenSource(const FString& InURL) : FPlayerMetricEventBase(EType::OpenSource), URL(InURL) {}
FString URL;
};
struct FPlayerMetricEvent_ReceivedMainPlaylist : public FPlayerMetricEventBase
{
FPlayerMetricEvent_ReceivedMainPlaylist(const FString& InEffectiveURL) : FPlayerMetricEventBase(EType::ReceivedMainPlaylist), EffectiveURL(InEffectiveURL) {}
FString EffectiveURL;
};
struct FPlayerMetricEvent_PlaylistDownload : public FPlayerMetricEventBase
{
FPlayerMetricEvent_PlaylistDownload(const Metrics::FPlaylistDownloadStats& InPlaylistDownloadStats) : FPlayerMetricEventBase(EType::PlaylistDownload), PlaylistDownloadStats(InPlaylistDownloadStats) {}
Metrics::FPlaylistDownloadStats PlaylistDownloadStats;
};
struct FPlayerMetricEvent_BufferingStart : public FPlayerMetricEventBase
{
FPlayerMetricEvent_BufferingStart(Metrics::EBufferingReason InBufferingReason) : FPlayerMetricEventBase(EType::BufferingStart), BufferingReason(InBufferingReason) {}
Metrics::EBufferingReason BufferingReason;
};
struct FPlayerMetricEvent_BufferingEnd : public FPlayerMetricEventBase
{
FPlayerMetricEvent_BufferingEnd(Metrics::EBufferingReason InBufferingReason) : FPlayerMetricEventBase(EType::BufferingEnd), BufferingReason(InBufferingReason) {}
Metrics::EBufferingReason BufferingReason;
};
struct FPlayerMetricEvent_Bandwidth : public FPlayerMetricEventBase
{
FPlayerMetricEvent_Bandwidth(int64 InEffectiveBps, int64 InThroughputBps, double InLatencyInSeconds) : FPlayerMetricEventBase(EType::Bandwidth), EffectiveBps(InEffectiveBps), ThroughputBps(InThroughputBps), LatencyInSeconds(InLatencyInSeconds) {}
int64 EffectiveBps;
int64 ThroughputBps;
double LatencyInSeconds;
};
struct FPlayerMetricEvent_BufferUtilization : public FPlayerMetricEventBase
{
FPlayerMetricEvent_BufferUtilization(const Metrics::FBufferStats& InBufferStats) : FPlayerMetricEventBase(EType::BufferUtilization), BufferStats(InBufferStats) {}
Metrics::FBufferStats BufferStats;
};
struct FPlayerMetricEvent_SegmentDownload : public FPlayerMetricEventBase
{
FPlayerMetricEvent_SegmentDownload(const Metrics::FSegmentDownloadStats& InSegmentDownloadStats) : FPlayerMetricEventBase(EType::SegmentDownload), SegmentDownloadStats(InSegmentDownloadStats) {}
Metrics::FSegmentDownloadStats SegmentDownloadStats;
};
struct FPlayerMetricEvent_LicenseKey : public FPlayerMetricEventBase
{
FPlayerMetricEvent_LicenseKey(const Metrics::FLicenseKeyStats& InLicenseKeyStats) : FPlayerMetricEventBase(EType::LicenseKey), LicenseKeyStats(InLicenseKeyStats) {}
Metrics::FLicenseKeyStats LicenseKeyStats;
};
struct FPlayerMetricEvent_DataAvailabilityChange : public FPlayerMetricEventBase
{
FPlayerMetricEvent_DataAvailabilityChange(const Metrics::FDataAvailabilityChange& InDataAvailability) : FPlayerMetricEventBase(EType::DataAvailabilityChange), DataAvailability(InDataAvailability) {}
Metrics::FDataAvailabilityChange DataAvailability;
};
struct FPlayerMetricEvent_VideoQualityChange : public FPlayerMetricEventBase
{
FPlayerMetricEvent_VideoQualityChange(int32 InNewBitrate, int32 InPreviousBitrate, bool bInIsDrasticDownswitch) : FPlayerMetricEventBase(EType::VideoQualityChange), NewBitrate(InNewBitrate), PreviousBitrate(InPreviousBitrate), bIsDrasticDownswitch(bInIsDrasticDownswitch) {}
int32 NewBitrate;
int32 PreviousBitrate;
bool bIsDrasticDownswitch;
};
struct FPlayerMetricEvent_AudioQualityChange : public FPlayerMetricEventBase
{
FPlayerMetricEvent_AudioQualityChange(int32 InNewBitrate, int32 InPreviousBitrate, bool bInIsDrasticDownswitch) : FPlayerMetricEventBase(EType::AudioQualityChange), NewBitrate(InNewBitrate), PreviousBitrate(InPreviousBitrate), bIsDrasticDownswitch(bInIsDrasticDownswitch) {}
int32 NewBitrate;
int32 PreviousBitrate;
bool bIsDrasticDownswitch;
};
struct FPlayerMetricEvent_CodecFormatChange : public FPlayerMetricEventBase
{
FPlayerMetricEvent_CodecFormatChange(const FStreamCodecInformation& InNewDecodingFormat) : FPlayerMetricEventBase(EType::CodecFormatChange), NewDecodingFormat(InNewDecodingFormat) {}
FStreamCodecInformation NewDecodingFormat;
};
struct FPlayerMetricEvent_JumpInPlayPosition : public FPlayerMetricEventBase
{
FPlayerMetricEvent_JumpInPlayPosition(const FTimeValue& InToNewTime, const FTimeValue& InFromTime, Metrics::ETimeJumpReason InTimejumpReason) : FPlayerMetricEventBase(EType::JumpInPlayPosition), ToNewTime(InToNewTime), FromTime(InFromTime), TimejumpReason(InTimejumpReason) {}
FTimeValue ToNewTime;
FTimeValue FromTime;
Metrics::ETimeJumpReason TimejumpReason;
};
struct FPlayerMetricEvent_MediaMetadataChange : public FPlayerMetricEventBase
{
FPlayerMetricEvent_MediaMetadataChange(const TSharedPtrTS<Electra::UtilsMP4::FMetadataParser>& InMetadata) : FPlayerMetricEventBase(EType::MediaMetadataChanged), NewMetadata(InMetadata) {}
TSharedPtrTS<Electra::UtilsMP4::FMetadataParser> NewMetadata;
};
struct FPlayerMetricEvent_Error : public FPlayerMetricEventBase
{
FPlayerMetricEvent_Error(const FString& InErrorReason) : FPlayerMetricEventBase(EType::Error), ErrorReason(InErrorReason) {}
FString ErrorReason;
};
struct FPlayerMetricEvent_LogMessage : public FPlayerMetricEventBase
{
FPlayerMetricEvent_LogMessage(IInfoLog::ELevel InLogLevel, const FString& InLogMessage, int64 InPlayerWallclockMilliseconds) : FPlayerMetricEventBase(EType::LogMessage), LogLevel(InLogLevel), LogMessage(InLogMessage), PlayerWallclockMilliseconds(InPlayerWallclockMilliseconds) {}
IInfoLog::ELevel LogLevel;
FString LogMessage;
int64 PlayerWallclockMilliseconds;
};
void CalculateTargetSeekTime(FTimespan& OutTargetTime, const FTimespan& InTime);
bool PresentVideoFrame(const FVideoDecoderOutputPtr& InVideoFrame);
bool PresentAudioFrame(const IAudioDecoderOutputPtr& DecoderOutput);
bool PresentSubtitle(const ISubtitleDecoderOutputPtr& DecoderOutput);
void PlatformSuspendOrResumeDecoders(bool bSuspend, const Electra::FParamDict& InOptions);
void OnMediaPlayerEventReceived(TSharedPtrTS<IAdaptiveStreamingPlayerAEMSEvent> InEvent, IAdaptiveStreamingPlayerAEMSReceiver::EDispatchMode InDispatchMode);
// Methods from IAdaptiveStreamingPlayerMetrics
virtual void ReportOpenSource(const FString& URL) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_OpenSource>(URL)); }
virtual void ReportReceivedMainPlaylist(const FString& EffectiveURL) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_ReceivedMainPlaylist>(EffectiveURL)); }
virtual void ReportReceivedPlaylists() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::ReceivedPlaylists)); }
virtual void ReportTracksChanged() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::TracksChanged)); }
virtual void ReportPlaylistDownload(const Metrics::FPlaylistDownloadStats& PlaylistDownloadStats) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_PlaylistDownload>(PlaylistDownloadStats)); }
virtual void ReportCleanStart() override
{ /*DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::CleanStart));*/
bDiscardOutputUntilCleanStart = false;
}
virtual void ReportBufferingStart(Metrics::EBufferingReason BufferingReason) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_BufferingStart>(BufferingReason)); }
virtual void ReportBufferingEnd(Metrics::EBufferingReason BufferingReason) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_BufferingEnd>(BufferingReason)); }
virtual void ReportBandwidth(int64 EffectiveBps, int64 ThroughputBps, double LatencyInSeconds) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_Bandwidth>(EffectiveBps, ThroughputBps, LatencyInSeconds)); }
virtual void ReportBufferUtilization(const Metrics::FBufferStats& BufferStats) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_BufferUtilization>(BufferStats)); }
virtual void ReportSegmentDownload(const Metrics::FSegmentDownloadStats& SegmentDownloadStats) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_SegmentDownload>(SegmentDownloadStats)); }
virtual void ReportLicenseKey(const Metrics::FLicenseKeyStats& LicenseKeyStats) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_LicenseKey>(LicenseKeyStats)); }
virtual void ReportDataAvailabilityChange(const Metrics::FDataAvailabilityChange& DataAvailability) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_DataAvailabilityChange>(DataAvailability)); }
virtual void ReportVideoQualityChange(int32 NewBitrate, int32 PreviousBitrate, bool bIsDrasticDownswitch) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_VideoQualityChange>(NewBitrate, PreviousBitrate, bIsDrasticDownswitch)); }
virtual void ReportAudioQualityChange(int32 NewBitrate, int32 PreviousBitrate, bool bIsDrasticDownswitch) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_AudioQualityChange>(NewBitrate, PreviousBitrate, bIsDrasticDownswitch)); }
virtual void ReportDecodingFormatChange(const FStreamCodecInformation& NewDecodingFormat) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_CodecFormatChange>(NewDecodingFormat)); }
virtual void ReportPrerollStart() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PrerollStart)); }
virtual void ReportPrerollEnd() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PrerollEnd)); }
virtual void ReportPlaybackStart() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PlaybackStart)); }
virtual void ReportPlaybackPaused() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PlaybackPaused)); }
virtual void ReportPlaybackResumed() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PlaybackResumed)); }
virtual void ReportPlaybackEnded() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PlaybackEnded)); }
virtual void ReportJumpInPlayPosition(const FTimeValue& ToNewTime, const FTimeValue& FromTime, Metrics::ETimeJumpReason TimejumpReason) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_JumpInPlayPosition>(ToNewTime, FromTime, TimejumpReason)); }
virtual void ReportPlaybackStopped() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::PlaybackStopped)); }
virtual void ReportSeekCompleted() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::SeekCompleted)); }
virtual void ReportMediaMetadataChanged(TSharedPtrTS<Electra::UtilsMP4::FMetadataParser> Metadata) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_MediaMetadataChange>(Metadata)); }
virtual void ReportError(const FString& ErrorReason) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_Error>(ErrorReason)); }
virtual void ReportLogMessage(IInfoLog::ELevel InLogLevel, const FString& InLogMessage, int64 InPlayerWallclockMilliseconds) override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEvent_LogMessage>(InLogLevel, InLogMessage, InPlayerWallclockMilliseconds)); }
virtual void ReportDroppedVideoFrame() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::DroppedVideoFrame)); }
virtual void ReportDroppedAudioFrame() override
{ DeferredPlayerEvents.Enqueue(MakeSharedTS<FPlayerMetricEventBase>(FPlayerMetricEventBase::EType::DroppedAudioFrame)); }
void LogPresentationFramesQueues(FTimespan DeltaTime);
void ClearToDefaultState();
void MediaStateOnPreparingFinished();
bool MediaStateOnPlay();
bool MediaStateOnPause();
void MediaStateOnEndReached();
void MediaStateOnSeekFinished();
void TriggerFirstSeekIfNecessary();
TSharedPtr<FTrackMetadata, ESPMode::ThreadSafe> GetTrackStreamMetadata(EPlayerTrackType TrackType, int32 TrackIndex) const;
// Delegate to talk back to adapter host
TWeakPtr<IElectraPlayerAdapterDelegate, ESPMode::ThreadSafe> AdapterDelegate;
// Contains number of audio tracks available to expose it later.
int32 NumTracksAudio;
int32 NumTracksVideo;
int32 NumTracksSubtitle;
int32 SelectedQuality;
mutable int32 SelectedVideoTrackIndex;
mutable int32 SelectedAudioTrackIndex;
mutable int32 SelectedSubtitleTrackIndex;
mutable bool bVideoTrackIndexDirty;
mutable bool bAudioTrackIndexDirty;
mutable bool bSubtitleTrackIndexDirty;
bool bInitialSeekPerformed;
bool bDiscardOutputUntilCleanStart;
bool bIsFirstBuffering;
FPlaybackRange CurrentPlaybackRange;
TOptional<bool> bFrameAccurateSeeking;
TOptional<bool> bEnableLooping;
TSharedPtr<TMap<FString, TArray<TSharedPtr<Electra::IMediaStreamMetadata::IItem, ESPMode::ThreadSafe>>>, ESPMode::ThreadSafe> CurrentStreamMetadata;
TOptional<FVideoStreamFormat> CurrentlyActiveVideoStreamFormat;
FIntPoint LastPresentedFrameDimension;
struct FPlayerState
{
TOptional<float> IntendedPlayRate;
float CurrentPlayRate = 0.0f;
TAtomic<EPlayerState> State;
TAtomic<EPlayerStatus> Status;
bool bUseInternal = false;
void Reset()
{
IntendedPlayRate.Reset();
CurrentPlayRate = 0.0f;
State = EPlayerState::Closed;
Status = EPlayerStatus::None;
}
float GetRate() const;
EPlayerState GetState() const;
EPlayerStatus GetStatus() const;
void SetIntendedPlayRate(float InIntendedRate);
void SetPlayRateFromPlayer(float InCurrentPlayerPlayRate);
};
/** Media player Guid */
FGuid PlayerGuid;
/** Metric delegates */
FElectraPlayerSendAnalyticMetricsDelegate& SendAnalyticMetricsDelegate;
FElectraPlayerSendAnalyticMetricsPerMinuteDelegate& SendAnalyticMetricsPerMinuteDelegate;
FElectraPlayerReportVideoStreamingErrorDelegate& ReportVideoStreamingErrorDelegate;
FElectraPlayerReportSubtitlesMetricsDelegate& ReportSubtitlesMetricsDelegate;
/** Option interface **/
FPlaystartOptions PlaystartOptions;
FPlayerState PlayerState;
TAtomic<bool> bPlayerHasClosed;
TAtomic<bool> bHasPendingError;
bool bAllowKillAfterCloseEvent;
/** Queued events */
TQueue<IElectraPlayerAdapterDelegate::EPlayerEvent> DeferredEvents;
TQueue<TSharedPtrTS<FPlayerMetricEventBase>> DeferredPlayerEvents;
TSharedPtrTS<FAEMSEventReceiver> MediaPlayerEventReceiver;
TSharedPtrTS<FSubtitleEventReceiver> MediaPlayerSubtitleReceiver;
/** The URL of the currently opened media. */
FString MediaUrl;
class FInternalPlayerImpl
{
public:
/** The media player itself **/
TSharedPtr<IAdaptiveStreamingPlayer, ESPMode::ThreadSafe> AdaptivePlayer;
/** Renderers to use **/
TSharedPtr<FElectraRendererVideo, ESPMode::ThreadSafe> RendererVideo;
TSharedPtr<FElectraRendererAudio, ESPMode::ThreadSafe> RendererAudio;
/** */
static void DoCloseAsync(TSharedPtr<FInternalPlayerImpl, ESPMode::ThreadSafe>&& Player, uint32 PlayerID, TSharedPtr<IAsyncResourceReleaseNotifyContainer, ESPMode::ThreadSafe> AsyncDestructNotification);
};
mutable FCriticalSection PlayerLock;
TSharedPtr<FInternalPlayerImpl, ESPMode::ThreadSafe> CurrentPlayer;
FEvent* WaitForPlayerDestroyedEvent;
TSharedPtr<Electra::FApplicationTerminationHandler, ESPMode::ThreadSafe> AppTerminationHandler;
TSharedPtr<IAsyncResourceReleaseNotifyContainer, ESPMode::ThreadSafe> AsyncResourceReleaseNotification;
class FAdaptiveStreamingPlayerResourceProvider : public IAdaptiveStreamingPlayerResourceProvider
{
public:
FAdaptiveStreamingPlayerResourceProvider(const TWeakPtr<IElectraPlayerAdapterDelegate, ESPMode::ThreadSafe>& AdapterDelegate);
virtual ~FAdaptiveStreamingPlayerResourceProvider();
virtual void ProvideStaticPlaybackDataForURL(TSharedPtr<IAdaptiveStreamingPlayerResourceRequest, ESPMode::ThreadSafe> InOutRequest) override;
void SetExternalDataReader(TWeakPtr<IElectraPlayerExternalDataReader, ESPMode::ThreadSafe> InExternalDataReader);
void ProcessPendingStaticResourceRequests();
void ClearPendingRequests();
private:
/** Requests for static resource fetches we want to perform on the main thread **/
TQueue<TSharedPtr<IAdaptiveStreamingPlayerResourceRequest, ESPMode::ThreadSafe>, EQueueMode::Mpsc> PendingStaticResourceRequests;
// Player adapter delegate
TWeakPtr<IElectraPlayerAdapterDelegate, ESPMode::ThreadSafe> AdapterDelegate;
// External data reader
TWeakPtr<IElectraPlayerExternalDataReader, ESPMode::ThreadSafe> ExternalDataReader;
IElectraPlayerExternalDataReader::FElectraPlayerExternalDataReaderOnRequestCompleted ExternalDataCompletedDelegate;
static void OnExternalDataReadCompleted(IElectraPlayerExternalDataReader::FResponseDataPtr InResponseData, int64 InTotalFileSize, const IElectraPlayerExternalDataReader::FReadParam& InFromRequestParams);
};
TSharedPtr<FAdaptiveStreamingPlayerResourceProvider, ESPMode::ThreadSafe> StaticResourceProvider;
TSharedPtr<IVideoDecoderResourceDelegate, ESPMode::ThreadSafe> VideoDecoderResourceDelegate;
class FBlobRequest
{
public:
FBlobRequest() : Request(MakeShared<Electra::FHTTPResourceRequest, ESPMode::ThreadSafe>())
{ }
void OnBlobRequestComplete(TSharedPtrTS<Electra::FHTTPResourceRequest> InRequest)
{
bIsComplete = true;
}
TSharedPtr<Electra::FHTTPResourceRequest, ESPMode::ThreadSafe> Request;
bool bIsComplete = false;
bool bDispatched = false;
};
TSharedPtr<FBlobRequest, ESPMode::ThreadSafe> PendingBlobRequest;
class FAverageValue
{
public:
FAverageValue()
: Samples(nullptr)
, NumSamples(0)
, MaxSamples(0)
{
}
~FAverageValue()
{
delete[] Samples;
}
void SetNumSamples(int32 InMaxSamples)
{
check(InMaxSamples > 0);
delete[] Samples;
NumSamples = 0;
MaxSamples = InMaxSamples;
Samples = new double[MaxSamples];
}
void AddValue(double Value)
{
Samples[NumSamples % MaxSamples] = Value;
++NumSamples;
}
void Reset()
{
NumSamples = 0;
}
double GetAverage() const
{
double Avg = 0.0;
if (NumSamples > 0)
{
double Sum = 0.0;
int32 Last = NumSamples <= MaxSamples ? NumSamples : MaxSamples;
for (int32 i = 0; i < Last; ++i)
{
Sum += Samples[i];
}
Avg = Sum / Last;
}
return Avg;
}
private:
double* Samples;
int32 NumSamples;
int32 MaxSamples;
};
struct FStatistics
{
struct FBandwidth
{
FBandwidth()
{
Bandwidth.SetNumSamples(3);
Latency.SetNumSamples(3);
Reset();
}
void Reset()
{
Bandwidth.Reset();
Latency.Reset();
}
void AddSample(double InBytesPerSecond, double InLatency)
{
Bandwidth.AddValue(InBytesPerSecond);
Latency.AddValue(InLatency);
}
double GetAverageBandwidth() const
{
return Bandwidth.GetAverage();
}
double GetAverageLatency() const
{
return Latency.GetAverage();
}
FAverageValue Bandwidth;
FAverageValue Latency;
};
struct FHistoryEntry
{
double TimeSinceStart = 0.0;
FString Message;
};
FStatistics()
{
Reset();
}
void Reset()
{
InitialURL.Empty();
CurrentlyActivePlaylistURL.Empty();
LastError.Empty();
LastState = "Empty";
TimeAtOpen = -1.0;
TimeToLoadMainPlaylist = -1.0;
TimeToLoadStreamPlaylists = -1.0;
InitialBufferingDuration = -1.0;
InitialVideoStreamBitrate = 0;
InitialAudioStreamBitrate = 0;
TimeAtPrerollBegin = -1.0;
TimeForInitialPreroll = -1.0;
NumTimesRebuffered = 0;
NumTimesForwarded = 0;
NumTimesRewound = 0;
NumTimesLooped = 0;
TimeAtBufferingBegin = 0.0;
TotalRebufferingDuration = 0.0;
LongestRebufferingDuration = 0.0;
PlayPosAtStart = -1.0;
PlayPosAtEnd = -1.0;
NumVideoQualityUpswitches = 0;
NumVideoQualityDownswitches = 0;
NumVideoQualityDrasticDownswitches = 0;
NumAudioQualityUpswitches = 0;
NumAudioQualityDownswitches = 0;
NumAudioQualityDrasticDownswitches = 0;
NumVideoDatabytesStreamed = 0;
NumAudioDatabytesStreamed = 0;
NumSegmentDownloadsAborted = 0;
CurrentlyActiveResolutionWidth = 0;
CurrentlyActiveResolutionHeight = 0;
VideoSegmentBitratesStreamed.Empty();
AudioSegmentBitratesStreamed.Empty();
VideoQualityPercentages.Empty();
AudioQualityPercentages.Empty();
NumVideoSegmentsStreamed = 0;
NumAudioSegmentsStreamed = 0;
InitialBufferingBandwidth.Reset();
bIsInitiallyDownloading = false;
bDidPlaybackEnd = false;
MediaTimelineAtStart.Reset();
MediaTimelineAtEnd.Reset();
MediaDuration = 0.0;
MessageHistoryBuffer.Empty();
NumErr404 = 0;
NumErr4xx = 0;
NumErr5xx = 0;
NumErrTimeouts = 0;
NumErrConnDrops = 0;
NumErrOther = 0;
}
void AddMessageToHistory(FString InMessage);
// n elements by descending bitrate holding a percentage of this bitrate being used
using FQualityPercentages = TSortedMap<int32, int32, FDefaultAllocator, TGreater<int32>>;
FString InitialURL;
FString CurrentlyActivePlaylistURL;
FString LastError;
FString LastState; // "Empty", "Opening", "Preparing", "Buffering", "Idle", "Ready", "Playing", "Paused", "Seeking", "Rebuffering", "Ended"
double TimeAtOpen;
double TimeToLoadMainPlaylist;
double TimeToLoadStreamPlaylists;
double InitialBufferingDuration;
int32 InitialVideoStreamBitrate;
int32 InitialAudioStreamBitrate;
double TimeAtPrerollBegin;
double TimeForInitialPreroll;
int32 NumTimesRebuffered;
int32 NumTimesForwarded;
int32 NumTimesRewound;
int32 NumTimesLooped;
double TimeAtBufferingBegin;
double TotalRebufferingDuration;
double LongestRebufferingDuration;
double PlayPosAtStart;
double PlayPosAtEnd;
int32 NumVideoQualityUpswitches;
int32 NumVideoQualityDownswitches;
int32 NumVideoQualityDrasticDownswitches;
int32 NumAudioQualityUpswitches;
int32 NumAudioQualityDownswitches;
int32 NumAudioQualityDrasticDownswitches;
int64 NumVideoDatabytesStreamed;
int64 NumAudioDatabytesStreamed;
int32 NumSegmentDownloadsAborted;
int32 CurrentlyActiveResolutionWidth;
int32 CurrentlyActiveResolutionHeight;
TMap<int32, uint32> VideoSegmentBitratesStreamed; // key=video stream bitrate, value=number of segments loaded at this rate
TMap<int32, uint32> AudioSegmentBitratesStreamed; // key=audio stream bitrate, value=number of segments loaded at this rate
FQualityPercentages VideoQualityPercentages;
FQualityPercentages AudioQualityPercentages;
uint32 NumVideoSegmentsStreamed;
uint32 NumAudioSegmentsStreamed;
FBandwidth InitialBufferingBandwidth;
bool bIsInitiallyDownloading;
bool bDidPlaybackEnd;
FTimeRange MediaTimelineAtStart;
FTimeRange MediaTimelineAtEnd;
double MediaDuration;
TArray<FHistoryEntry> MessageHistoryBuffer;
uint32 NumErr404;
uint32 NumErr4xx;
uint32 NumErr5xx;
uint32 NumErrTimeouts;
uint32 NumErrConnDrops;
uint32 NumErrOther;
};
struct FAnalyticsEvent
{
FString EventName;
TArray<FAnalyticsEventAttribute> ParamArray;
};
void UpdatePlayEndStatistics();
void LogStatistics();
void AddCommonAnalyticsAttributes(TArray<FAnalyticsEventAttribute>& InOutParamArray);
TSharedPtr<FAnalyticsEvent> CreateAnalyticsEvent(FString InEventName);
void EnqueueAnalyticsEvent(TSharedPtr<FAnalyticsEvent> InAnalyticEvent);
void UpdateAnalyticsCustomValues();
FCriticalSection StatisticsLock;
FStatistics Statistics;
TQueue<TSharedPtr<FAnalyticsEvent>> QueuedAnalyticEvents;
int32 NumQueuedAnalyticEvents = 0;
FString AnalyticsOSVersion;
FString AnalyticsGPUType;
/** Unique player instance GUID sent with each analytics event. This allows finding all events of a particular playback session. **/
FString AnalyticsInstanceGuid;
/** Sequential analytics event number. Helps sorting events. **/
uint32 AnalyticsInstanceEventCount;
/** Custom analytics constants. **/
FString AnalyticsCustomValues[8];
/** Unique instance ID */
uint32 InstanceID = 0;
void HandleBlobDownload();
void HandleDeferredPlayerEvents();
void HandlePlayerEventOpenSource(const FString& URL);
void HandlePlayerEventReceivedMainPlaylist(const FString& EffectiveURL);
void HandlePlayerEventReceivedPlaylists();
void HandlePlayerEventTracksChanged();
void HandlePlayerEventPlaylistDownload(const Metrics::FPlaylistDownloadStats& PlaylistDownloadStats);
void HandlePlayerEventBufferingStart(Metrics::EBufferingReason BufferingReason);
void HandlePlayerEventBufferingEnd(Metrics::EBufferingReason BufferingReason);
void HandlePlayerEventBandwidth(int64 EffectiveBps, int64 ThroughputBps, double LatencyInSeconds);
void HandlePlayerEventBufferUtilization(const Metrics::FBufferStats& BufferStats);
void HandlePlayerEventSegmentDownload(const Metrics::FSegmentDownloadStats& SegmentDownloadStats);
void HandlePlayerEventLicenseKey(const Metrics::FLicenseKeyStats& LicenseKeyStats);
void HandlePlayerEventDataAvailabilityChange(const Metrics::FDataAvailabilityChange& DataAvailability);
void HandlePlayerEventVideoQualityChange(int32 NewBitrate, int32 PreviousBitrate, bool bIsDrasticDownswitch);
void HandlePlayerEventAudioQualityChange(int32 NewBitrate, int32 PreviousBitrate, bool bIsDrasticDownswitch);
void HandlePlayerEventCodecFormatChange(const Electra::FStreamCodecInformation& NewDecodingFormat);
void HandlePlayerEventPrerollStart();
void HandlePlayerEventPrerollEnd();
void HandlePlayerEventPlaybackStart();
void HandlePlayerEventPlaybackPaused();
void HandlePlayerEventPlaybackResumed();
void HandlePlayerEventPlaybackEnded();
void HandlePlayerEventJumpInPlayPosition(const FTimeValue& ToNewTime, const FTimeValue& FromTime, Metrics::ETimeJumpReason TimejumpReason);
void HandlePlayerEventPlaybackStopped();
void HandlePlayerEventSeekCompleted();
void HandlePlayerMediaMetadataChanged(const TSharedPtrTS<Electra::UtilsMP4::FMetadataParser>& InMetadata);
void HandlePlayerEventError(const FString& ErrorReason);
void HandlePlayerEventLogMessage(IInfoLog::ELevel InLogLevel, const FString& InLogMessage, int64 InPlayerWallclockMilliseconds);
void HandlePlayerEventDroppedVideoFrame();
void HandlePlayerEventDroppedAudioFrame();
};
ENUM_CLASS_FLAGS(FElectraPlayer::EPlayerStatus);