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

1126 lines
44 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ElectraPlayerPlugin.h"
#include "Misc/Optional.h"
#include "IMediaEventSink.h"
#include "IMediaOptions.h"
#include "IMediaMetadataItem.h"
#include "MediaSamples.h"
#include "MediaPlayerOptions.h"
#include "IElectraPlayerRuntimeModule.h"
#include "IElectraPlayerPluginModule.h"
#include "IElectraMetadataSample.h"
#include "IElectraSubtitleSample.h"
#include "MediaSubtitleDecoderOutput.h"
#include "MediaMetaDataDecoderOutput.h"
using namespace Electra;
//-----------------------------------------------------------------------------
std::atomic<uint32> FElectraPlayerPlugin::NextPlayerUniqueID { 0 };
//-----------------------------------------------------------------------------
namespace ElectraMediaOptions
{
static const FName GetSafeMediaOptions(TEXT("GetSafeMediaOptions"));
static const FName ElectraNoPreloading(TEXT("ElectraNoPreloading"));
static const FName PlaylistProperties(TEXT("playlist_properties"));
static const FName ElectraInitialBitrate(TEXT("ElectraInitialBitrate"));
static const FName MaxElectraVerticalResolution(TEXT("MaxElectraVerticalResolution"));
static const FName MaxElectraVerticalResolutionOf60fpsVideos(TEXT("MaxElectraVerticalResolutionOf60fpsVideos"));
static const FName ElectraLivePresentationOffset(TEXT("ElectraLivePresentationOffset"));
static const FName ElectraLiveAudioPresentationOffset(TEXT("ElectraLiveAudioPresentationOffset"));
static const FName ElectraLiveUseConservativePresentationOffset(TEXT("ElectraLiveUseConservativePresentationOffset"));
static const FName ElectraThrowErrorWhenRebuffering(TEXT("ElectraThrowErrorWhenRebuffering"));
static const FName ElectraGetDenyStreamCode(TEXT("ElectraGetDenyStreamCode"));
static const FName MaxResolutionForMediaStreaming(TEXT("MaxResolutionForMediaStreaming"));
static const FName ElectraMaxStreamingBandwidth(TEXT("ElectraMaxStreamingBandwidth"));
static const FName ElectraPlayerDataCache(TEXT("ElectraPlayerDataCache"));
static const FName Mimetype(TEXT("mimetype"));
static const FName CodecOptions[] = { TEXT("excluded_codecs_video"), TEXT("excluded_codecs_audio"), TEXT("excluded_codecs_subtitles"), TEXT("preferred_codecs_video"), TEXT("preferred_codecs_audio"),TEXT("preferred_codecs_subtitles") };
static const FName ElectraGetPlaylistData(TEXT("ElectraGetPlaylistData"));
static const FName ElectraGetLicenseKeyData(TEXT("ElectraGetLicenseKeyData"));
static const FName ElectraGetPlaystartPosFromSeekPositions(TEXT("ElectraGetPlaystartPosFromSeekPositions"));
static const FName KeyUniquePlayerID(TEXT("UniquePlayerID"));
static const FName OptionKeyParseTimecodeInfo(TEXT("parse_timecode_info"));
}
//-----------------------------------------------------------------------------
FElectraPlayerPlugin::FElectraPlayerPlugin()
{
// Make sure a few assumptions are correct...
static_assert((int32)EMediaEvent::MediaBuffering == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MediaBuffering, "check alignment of both enums");
static_assert((int32)EMediaEvent::MediaBufferingComplete == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MediaBufferingComplete, "check alignment of both enums");
static_assert((int32)EMediaEvent::MediaClosed == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MediaClosed, "check alignment of both enums");
static_assert((int32)EMediaEvent::MediaConnecting == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MediaConnecting, "check alignment of both enums");
static_assert((int32)EMediaEvent::MediaOpened == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MediaOpened, "check alignment of both enums");
static_assert((int32)EMediaEvent::MediaOpenFailed == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MediaOpenFailed, "check alignment of both enums");
static_assert((int32)EMediaEvent::PlaybackEndReached == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::PlaybackEndReached, "check alignment of both enums");
static_assert((int32)EMediaEvent::PlaybackResumed == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::PlaybackResumed, "check alignment of both enums");
static_assert((int32)EMediaEvent::PlaybackSuspended == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::PlaybackSuspended, "check alignment of both enums");
static_assert((int32)EMediaEvent::SeekCompleted == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::SeekCompleted, "check alignment of both enums");
static_assert((int32)EMediaEvent::TracksChanged == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::TracksChanged, "check alignment of both enums");
static_assert((int32)EMediaEvent::MetadataChanged == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::MetadataChanged, "check alignment of both enums");
static_assert((int32)EMediaEvent::Internal_PurgeVideoSamplesHint == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::Internal_PurgeVideoSamplesHint, "check alignment of both enums");
static_assert((int32)EMediaEvent::Internal_VideoSamplesAvailable == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::Internal_VideoSamplesAvailable, "check alignment of both enums");
static_assert((int32)EMediaEvent::Internal_VideoSamplesUnavailable == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::Internal_VideoSamplesUnavailable, "check alignment of both enums");
static_assert((int32)EMediaEvent::Internal_AudioSamplesAvailable == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::Internal_AudioSamplesAvailable, "check alignment of both enums");
static_assert((int32)EMediaEvent::Internal_AudioSamplesUnavailable == (int32)IElectraPlayerAdapterDelegate::EPlayerEvent::Internal_AudioSamplesUnavailable, "check alignment of both enums");
static_assert((int32)EMediaState::Closed == (int32)IElectraPlayerInterface::EPlayerState::Closed, "check alignment of both enums");
static_assert((int32)EMediaState::Error == (int32)IElectraPlayerInterface::EPlayerState::Error, "check alignment of both enums");
static_assert((int32)EMediaState::Paused == (int32)IElectraPlayerInterface::EPlayerState::Paused, "check alignment of both enums");
static_assert((int32)EMediaState::Playing == (int32)IElectraPlayerInterface::EPlayerState::Playing, "check alignment of both enums");
static_assert((int32)EMediaState::Preparing == (int32)IElectraPlayerInterface::EPlayerState::Preparing, "check alignment of both enums");
static_assert((int32)EMediaState::Stopped == (int32)IElectraPlayerInterface::EPlayerState::Stopped, "check alignment of both enums");
static_assert((int32)EMediaStatus::None == (int32)IElectraPlayerInterface::EPlayerStatus::None, "check alignment of both enums");
static_assert((int32)EMediaStatus::Buffering == (int32)IElectraPlayerInterface::EPlayerStatus::Buffering, "check alignment of both enums");
static_assert((int32)EMediaStatus::Connecting == (int32)IElectraPlayerInterface::EPlayerStatus::Connecting, "check alignment of both enums");
static_assert((int32)EMediaTrackType::Audio == (int32)IElectraPlayerInterface::EPlayerTrackType::Audio, "check alignment of both enums");
static_assert((int32)EMediaTrackType::Video == (int32)IElectraPlayerInterface::EPlayerTrackType::Video, "check alignment of both enums");
static_assert((int32)EMediaRateThinning::Unthinned == (int32)IElectraPlayerInterface::EPlayRateType::Unthinned, "check alignment of both enums");
static_assert((int32)EMediaRateThinning::Thinned == (int32)IElectraPlayerInterface::EPlayRateType::Thinned, "check alignment of both enums");
static_assert((int32)EMediaTimeRangeType::Absolute == (int32)IElectraPlayerInterface::ETimeRangeType::Absolute, "check alignment of both enums");
static_assert((int32)EMediaTimeRangeType::Current == (int32)IElectraPlayerInterface::ETimeRangeType::Current, "check alignment of both enums");
static_assert(IMediaPlayerLifecycleManagerDelegate::ResourceFlags_Decoder == IElectraPlayerInterface::ResourceFlags_Decoder, "check alignment of both enums");
static_assert(IMediaPlayerLifecycleManagerDelegate::ResourceFlags_OutputBuffers == IElectraPlayerInterface::ResourceFlags_OutputBuffers, "check alignment of both enums");
static_assert(IMediaPlayerLifecycleManagerDelegate::ResourceFlags_Any == IElectraPlayerInterface::ResourceFlags_Any, "check alignment of both enums");
static_assert(IMediaPlayerLifecycleManagerDelegate::ResourceFlags_All == IElectraPlayerInterface::ResourceFlags_All, "check alignment of both enums");
}
bool FElectraPlayerPlugin::Initialize(IMediaEventSink& InEventSink,
FElectraPlayerSendAnalyticMetricsDelegate& InSendAnalyticMetricsDelegate,
FElectraPlayerSendAnalyticMetricsPerMinuteDelegate& InSendAnalyticMetricsPerMinuteDelegate,
FElectraPlayerReportVideoStreamingErrorDelegate& InReportVideoStreamingErrorDelegate,
FElectraPlayerReportSubtitlesMetricsDelegate& InReportSubtitlesFileMetricsDelegate)
{
CallbackPointerLock.Lock();
EventSink = &InEventSink;
CallbackPointerLock.Unlock();
OutputTexturePool = MakeShareable(new FElectraTextureSamplePool);
MediaSamples.Reset(new FMediaSamples);
PlayerResourceDelegate = MakeShareable(PlatformCreatePlayerResourceDelegate());
Player = MakeShareable(FElectraPlayerRuntimeFactory::CreatePlayer(SharedThis(this), InSendAnalyticMetricsDelegate, InSendAnalyticMetricsPerMinuteDelegate, InReportVideoStreamingErrorDelegate, InReportSubtitlesFileMetricsDelegate));
bMetadataChanged = false;
CurrentMetadata.Reset();
return true;
}
FElectraPlayerPlugin::~FElectraPlayerPlugin()
{
CallbackPointerLock.Lock();
EventSink = nullptr;
OptionInterface.Reset();
CallbackPointerLock.Unlock();
if (Player.IsValid())
{
Player->CloseInternal(true);
Player.Reset();
}
PlayerResourceDelegate.Reset();
MediaSamples.Reset();
}
//-----------------------------------------------------------------------------
class FElectraBinarySample : public IElectraBinarySample
{
public:
virtual ~FElectraBinarySample() = default;
virtual const void* GetData() override { return Metadata->GetData(); }
virtual uint32 GetSize() const override { return Metadata->GetSize(); }
virtual FGuid GetGUID() const override { return IElectraBinarySample::GetSampleTypeGUID(); }
virtual const FString& GetSchemeIdUri() const override { return Metadata->GetSchemeIdUri(); }
virtual const FString& GetValue() const override { return Metadata->GetValue(); }
virtual const FString& GetID() const override { return Metadata->GetID(); }
virtual EDispatchedMode GetDispatchedMode() const override
{
switch(Metadata->GetDispatchedMode())
{
default:
case IMetaDataDecoderOutput::EDispatchedMode::OnReceive:
{
return FElectraBinarySample::EDispatchedMode::OnReceive;
}
case IMetaDataDecoderOutput::EDispatchedMode::OnStart:
{
return FElectraBinarySample::EDispatchedMode::OnStart;
}
}
}
virtual EOrigin GetOrigin() const override
{
switch(Metadata->GetOrigin())
{
default:
case IMetaDataDecoderOutput::EOrigin::TimedMetadata:
{
return FElectraBinarySample::EOrigin::TimedMetadata;
}
case IMetaDataDecoderOutput::EOrigin::EventStream:
{
return FElectraBinarySample::EOrigin::EventStream;
}
case IMetaDataDecoderOutput::EOrigin::InbandEventStream:
{
return FElectraBinarySample::EOrigin::InbandEventStream;
}
}
}
virtual FMediaTimeStamp GetTime() const override
{
FDecoderTimeStamp ts = Metadata->GetTime();
return FMediaTimeStamp(ts.Time, ts.SequenceIndex);
}
virtual FTimespan GetDuration() const override
{
FTimespan Duration = Metadata->GetDuration();
// A zero duration might cause the metadata sample fall through the cracks later
// so set it to a short 1ms instead.
if (Duration.IsZero())
{
Duration = FTimespan::FromMilliseconds(1);
}
return Duration;
}
virtual TOptional<FMediaTimeStamp> GetTrackBaseTime() const override
{
TOptional<FMediaTimeStamp> ms;
TOptional<FDecoderTimeStamp> ts = Metadata->GetTime();
if (ts.IsSet())
{
ms = FMediaTimeStamp(ts.GetValue().Time, ts.GetValue().SequenceIndex);
}
return ms;
}
IMetaDataDecoderOutputPtr Metadata;
};
//-----------------------------------------------------------------------------
class FElectraSubtitleSample : public IElectraSubtitleSample
{
public:
virtual FGuid GetGUID() const override
{
return IElectraSubtitleSample::GetSampleTypeGUID();
}
virtual FMediaTimeStamp GetTime() const override
{
FDecoderTimeStamp ts = Subtitle->GetTime();
return FMediaTimeStamp(ts.Time, ts.SequenceIndex);
}
virtual FTimespan GetDuration() const override
{
return Subtitle->GetDuration();
}
virtual TOptional<FVector2D> GetPosition() const override
{
return TOptional<FVector2D>();
}
virtual FText GetText() const override
{
FUTF8ToTCHAR cnv((const ANSICHAR*)Subtitle->GetData().GetData(), Subtitle->GetData().Num());
FString UTF8Text = FString::ConstructFromPtrSize(cnv.Get(), cnv.Length());
return FText::FromString(UTF8Text);
}
virtual EMediaOverlaySampleType GetType() const override
{
return EMediaOverlaySampleType::Subtitle;
}
ISubtitleDecoderOutputPtr Subtitle;
};
//-----------------------------------------------------------------------------
class FStreamMetadataItem : public IMediaMetadataItem
{
public:
FStreamMetadataItem(const TSharedPtr<Electra::IMediaStreamMetadata::IItem, ESPMode::ThreadSafe>& InItem) : Item(InItem.ToSharedRef())
{ }
virtual ~FStreamMetadataItem()
{ }
const FString& GetLanguageCode() const override
{ return Item->GetLanguageCode(); }
const FString& GetMimeType() const override
{ return Item->GetMimeType(); }
const FVariant& GetValue() const override
{ return Item->GetValue(); }
private:
TSharedRef<Electra::IMediaStreamMetadata::IItem, ESPMode::ThreadSafe> Item;
};
//-----------------------------------------------------------------------------
void FElectraPlayerPlugin::BlobReceived(const TSharedPtr<TArray<uint8>, ESPMode::ThreadSafe>& InBlobData, IElectraPlayerAdapterDelegate::EBlobResultType InResultType, int32 InResultCode, const Electra::FParamDict* InExtraInfo)
{
}
Electra::FVariantValue FElectraPlayerPlugin::QueryOptions(EOptionType Type, const Electra::FVariantValue& Param)
{
CallbackPointerLock.Lock();
TSharedPtr<IElectraSafeMediaOptionInterface, ESPMode::ThreadSafe> SafeOptionInterface = OptionInterface.Pin();
CallbackPointerLock.Unlock();
if (SafeOptionInterface.IsValid())
{
IElectraSafeMediaOptionInterface::FScopedLock SafeLock(SafeOptionInterface);
IMediaOptions *SafeOptions = SafeOptionInterface->GetMediaOptionInterface();
if (SafeOptions)
{
switch(Type)
{
case EOptionType::MaxVerticalStreamResolution:
{
return FVariantValue((int64)SafeOptions->GetMediaOption(ElectraMediaOptions::MaxResolutionForMediaStreaming, (int64)0));
}
case EOptionType::MaxBandwidthForStreaming:
{
return FVariantValue((int64)SafeOptions->GetMediaOption(ElectraMediaOptions::ElectraMaxStreamingBandwidth, (int64)0));
}
case EOptionType::PlayListData:
{
if (SafeOptions->HasMediaOption(ElectraMediaOptions::ElectraGetPlaylistData))
{
check(Param.IsType(FVariantValue::EDataType::TypeFString));
return FVariantValue(SafeOptions->GetMediaOption(ElectraMediaOptions::ElectraGetPlaylistData, Param.GetFString()));
}
break;
}
case EOptionType::LicenseKeyData:
{
if (SafeOptions->HasMediaOption(ElectraMediaOptions::ElectraGetLicenseKeyData))
{
check(Param.IsType(FVariantValue::EDataType::TypeFString));
return FVariantValue(SafeOptions->GetMediaOption(ElectraMediaOptions::ElectraGetLicenseKeyData, Param.GetFString()));
}
break;
}
case EOptionType::CustomAnalyticsMetric:
{
check(Param.IsType(FVariantValue::EDataType::TypeFString));
if (Param.IsType(FVariantValue::EDataType::TypeFString))
{
FName OptionKey(*Param.GetFString());
if (SafeOptions->HasMediaOption(OptionKey))
{
return FVariantValue(SafeOptions->GetMediaOption(OptionKey, FString()));
}
}
break;
}
case EOptionType::PlaystartPosFromSeekPositions:
{
if (SafeOptions->HasMediaOption(ElectraMediaOptions::ElectraGetPlaystartPosFromSeekPositions))
{
check(Param.IsType(FVariantValue::EDataType::TypeSharedPointer));
TSharedPtr<TArray<FTimespan>, ESPMode::ThreadSafe> PosArray = Param.GetSharedPointer<TArray<FTimespan>>();
if (PosArray.IsValid())
{
TSharedPtr<FElectraSeekablePositions, ESPMode::ThreadSafe> Res = StaticCastSharedPtr<FElectraSeekablePositions, IMediaOptions::FDataContainer, ESPMode::ThreadSafe>(SafeOptions->GetMediaOption(ElectraMediaOptions::ElectraGetPlaystartPosFromSeekPositions, MakeShared<FElectraSeekablePositions, ESPMode::ThreadSafe>(*PosArray)));
if (Res.IsValid() && Res->Data.Num())
{
return FVariantValue(int64(Res->Data[0].GetTicks())); // return HNS
}
}
return FVariantValue();
}
break;
}
default:
{
break;
}
}
}
}
return FVariantValue();
}
void FElectraPlayerPlugin::SendMediaEvent(EPlayerEvent Event)
{
if (Event == EPlayerEvent::MetadataChanged)
{
SetMetadataChanged();
}
FScopeLock lock(&CallbackPointerLock);
if (EventSink)
{
EventSink->ReceiveMediaEvent((EMediaEvent)Event);
}
}
void FElectraPlayerPlugin::OnVideoFlush()
{
TRange<FTimespan> AllTime(FTimespan::MinValue(), FTimespan::MaxValue());
TSharedPtr<IMediaTextureSample, ESPMode::ThreadSafe> FlushSample;
while (GetSamples().FetchVideo(AllTime, FlushSample))
{ }
}
void FElectraPlayerPlugin::OnAudioFlush()
{
TRange<FTimespan> AllTime(FTimespan::MinValue(), FTimespan::MaxValue());
TSharedPtr<IMediaAudioSample, ESPMode::ThreadSafe> FlushSample;
while (GetSamples().FetchAudio(AllTime, FlushSample))
{ }
}
void FElectraPlayerPlugin::OnSubtitleFlush()
{
TRange<FTimespan> AllTime(FTimespan::MinValue(), FTimespan::MaxValue());
TSharedPtr<IMediaOverlaySample, ESPMode::ThreadSafe> FlushSample;
while (GetSamples().FetchSubtitle(AllTime, FlushSample))
{ }
}
void FElectraPlayerPlugin::PresentVideoFrame(const FVideoDecoderOutputPtr& InVideoFrame)
{
FScopeLock SampleLock(&MediaSamplesLock);
FVideoDecoderOutputPtr VideoFrame = InVideoFrame;
TSharedPtr<FElectraTextureSamplePool, ESPMode::ThreadSafe> TexturePool = OutputTexturePool;
if (VideoFrame.IsValid() && TexturePool.IsValid())
{
FElectraTextureSampleRef TextureSample = TexturePool->AcquireShared();
TextureSample->Initialize(VideoFrame.Get());
MediaSamples->AddVideo(TextureSample);
}
}
void FElectraPlayerPlugin::PresentAudioFrame(const IAudioDecoderOutputPtr& InAudioFrame)
{
FScopeLock SampleLock(&MediaSamplesLock);
IAudioDecoderOutputPtr AudioFrame = InAudioFrame;
if (AudioFrame.IsValid())
{
TSharedRef<FElectraPlayerAudioSample, ESPMode::ThreadSafe> AudioSample = OutputAudioPool.AcquireShared();
AudioSample->Initialize(InAudioFrame);
MediaSamples->AddAudio(AudioSample);
}
}
void FElectraPlayerPlugin::PresentSubtitleSample(const ISubtitleDecoderOutputPtr& InSubtitleSample)
{
FScopeLock SampleLock(&MediaSamplesLock);
ISubtitleDecoderOutputPtr Subtitle = InSubtitleSample;
if (Subtitle.IsValid())
{
TSharedRef<FElectraSubtitleSample, ESPMode::ThreadSafe> SubtitleSample = MakeShared<FElectraSubtitleSample, ESPMode::ThreadSafe>();
SubtitleSample->Subtitle = InSubtitleSample;
MediaSamples->AddSubtitle(SubtitleSample);
}
}
void FElectraPlayerPlugin::PresentMetadataSample(const IMetaDataDecoderOutputPtr& InMetadataFrame)
{
FScopeLock SampleLock(&MediaSamplesLock);
IMetaDataDecoderOutputPtr MetadataFrame = InMetadataFrame;
if (MetadataFrame.IsValid())
{
TSharedRef<FElectraBinarySample, ESPMode::ThreadSafe> MetaDataSample = MakeShared<FElectraBinarySample, ESPMode::ThreadSafe>();
MetaDataSample->Metadata = InMetadataFrame;
MediaSamples->AddMetadata(MetaDataSample);
}
}
bool FElectraPlayerPlugin::CanReceiveVideoSamples(int32 NumFrames)
{
FScopeLock SampleLock(&MediaSamplesLock);
return MediaSamples->CanReceiveVideoSamples(NumFrames);
}
bool FElectraPlayerPlugin::CanReceiveAudioSamples(int32 NumFrames)
{
FScopeLock SampleLock(&MediaSamplesLock);
return MediaSamples->CanReceiveAudioSamples(NumFrames);
}
FString FElectraPlayerPlugin::GetVideoAdapterName() const
{
return GRHIAdapterName;
}
TSharedPtr<IElectraPlayerResourceDelegate, ESPMode::ThreadSafe> FElectraPlayerPlugin::GetResourceDelegate() const
{
return PlayerResourceDelegate;
}
//-----------------------------------------------------------------------------
// IMediaPlayer interface
FGuid FElectraPlayerPlugin::GetPlayerPluginGUID() const
{
static FGuid PlayerPluginGUID(0x94ee3f80, 0x8e604292, 0xb4d24dd5, 0xfdade1c2);
return PlayerPluginGUID;
}
FString FElectraPlayerPlugin::GetInfo() const
{
return TEXT("No information available");
}
IMediaSamples& FElectraPlayerPlugin::GetSamples()
{
FScopeLock SampleLock(&MediaSamplesLock);
return *MediaSamples;
}
FString FElectraPlayerPlugin::GetStats() const
{
return TEXT("ElectraPlayer: GetStats: <empty>?");
}
IMediaTracks& FElectraPlayerPlugin::GetTracks()
{
return *this;
}
bool FElectraPlayerPlugin::Open(const FString& Url, const IMediaOptions* Options)
{
return Open(Url, Options, nullptr);
}
bool FElectraPlayerPlugin::Open(const FString& Url, const IMediaOptions* Options, const FMediaPlayerOptions* InPlayerOptions)
{
PlayerUniqueID = ++NextPlayerUniqueID;
if (Options == nullptr)
{
UE_LOG(LogElectraPlayerPlugin, Error, TEXT("[%u] IMediaPlayer::Open: Options == nullptr"), PlayerUniqueID);
FScopeLock lock(&CallbackPointerLock);
if (EventSink)
{
EventSink->ReceiveMediaEvent(EMediaEvent::MediaOpenFailed);
}
return false;
}
// Get the safe option interface to poll for changes during playback.
CallbackPointerLock.Lock();
OptionInterface = StaticCastSharedPtr<IElectraSafeMediaOptionInterface>(Options->GetMediaOption(ElectraMediaOptions::GetSafeMediaOptions, TSharedPtr<IElectraSafeMediaOptionInterface, ESPMode::ThreadSafe>()));
CallbackPointerLock.Unlock();
UE_LOG(LogElectraPlayerPlugin, Log, TEXT("[%u] IMediaPlayer::Open"), PlayerUniqueID);
IElectraPlayerInterface::FPlaystartOptions LocalPlaystartOptions;
// Get playstart options from passed options, if they exist.
FName Environment;
if (InPlayerOptions)
{
if (InPlayerOptions->SeekTimeType != EMediaPlayerOptionSeekTimeType::Ignored)
{
LocalPlaystartOptions.TimeOffset = InPlayerOptions->SeekTime;
}
if (InPlayerOptions->TrackSelection == EMediaPlayerOptionTrackSelectMode::UseLanguageCodes)
{
if (InPlayerOptions->TracksByLanguage.Video.Len())
{
LocalPlaystartOptions.InitialVideoTrackAttributes.Language_RFC4647 = InPlayerOptions->TracksByLanguage.Video;
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Asking for initial video language \"%s\""), PlayerUniqueID, *InPlayerOptions->TracksByLanguage.Video);
}
if (InPlayerOptions->TracksByLanguage.Audio.Len())
{
LocalPlaystartOptions.InitialAudioTrackAttributes.Language_RFC4647 = InPlayerOptions->TracksByLanguage.Audio;
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Asking for initial audio language \"%s\""), PlayerUniqueID, *InPlayerOptions->TracksByLanguage.Audio);
}
if (InPlayerOptions->TracksByLanguage.Subtitle.Len())
{
LocalPlaystartOptions.InitialSubtitleTrackAttributes.Language_RFC4647 = InPlayerOptions->TracksByLanguage.Subtitle;
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Asking for initial subtitle language \"%s\""), PlayerUniqueID, *InPlayerOptions->TracksByLanguage.Subtitle);
}
}
else if (InPlayerOptions->TrackSelection == EMediaPlayerOptionTrackSelectMode::UseTrackOptionIndices)
{
LocalPlaystartOptions.InitialAudioTrackAttributes.TrackIndexOverride = InPlayerOptions->Tracks.Audio;
LocalPlaystartOptions.InitialSubtitleTrackAttributes.TrackIndexOverride = InPlayerOptions->Tracks.Subtitle;
}
const FVariant* Env = InPlayerOptions->InternalCustomOptions.Find(MediaPlayerOptionValues::Environment());
Environment = Env ? Env->GetValue<FName>() : Environment;
}
bool bNoPreloading = Options->GetMediaOption(ElectraMediaOptions::ElectraNoPreloading, (bool)false);
if (bNoPreloading)
{
LocalPlaystartOptions.bDoNotPreload = true;
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: No preloading after opening media"), PlayerUniqueID);
}
// Set up options to initialize the internal player with.
Electra::FParamDict PlayerOptions;
PlayerOptions.Set(ElectraMediaOptions::KeyUniquePlayerID, Electra::FVariantValue((int64)PlayerUniqueID));
for(auto &CodecOption : ElectraMediaOptions::CodecOptions)
{
FString Value = Options->GetMediaOption(CodecOption, FString());
if (Value.Len())
{
PlayerOptions.Set(CodecOption, Electra::FVariantValue(Value));
}
}
// Required playlist properties?
FString PlaylistProperties = Options->GetMediaOption(ElectraMediaOptions::PlaylistProperties, FString());
if (PlaylistProperties.Len())
{
PlayerOptions.Set(ElectraMediaOptions::PlaylistProperties, Electra::FVariantValue(PlaylistProperties));
}
if (InPlayerOptions && InPlayerOptions->InternalCustomOptions.Find(MediaPlayerOptionValues::ParseTimecodeInfo()))
{
PlayerOptions.Set(ElectraMediaOptions::OptionKeyParseTimecodeInfo, FVariantValue());
}
// Check for one-time initialization options that can't be changed during playback.
int64 InitialStreamBitrate = Options->GetMediaOption(ElectraMediaOptions::ElectraInitialBitrate, (int64)-1);
if (InitialStreamBitrate > 0)
{
PlayerOptions.Set(TEXT("initial_bitrate"), Electra::FVariantValue(InitialStreamBitrate));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Using initial bitrate of %d bits/second"), PlayerUniqueID, (int32)InitialStreamBitrate);
}
FString MediaMimeType = Options->GetMediaOption(ElectraMediaOptions::Mimetype, FString());
if (MediaMimeType.Len())
{
PlayerOptions.Set(TEXT("mime_type"), Electra::FVariantValue(MediaMimeType));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Setting media mime type to \"%s\""), PlayerUniqueID, *MediaMimeType);
}
int64 MaxVerticalHeight = Options->GetMediaOption(ElectraMediaOptions::MaxElectraVerticalResolution, (int64)-1);
if (MaxVerticalHeight > 0)
{
PlayerOptions.Set(TEXT("max_resoY"), Electra::FVariantValue(MaxVerticalHeight));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Limiting vertical resolution to %d for all streams"), PlayerUniqueID, (int32)MaxVerticalHeight);
}
int64 MaxVerticalHeightAt60 = Options->GetMediaOption(ElectraMediaOptions::MaxElectraVerticalResolutionOf60fpsVideos, (int64)-1);
if (MaxVerticalHeightAt60 > 0)
{
PlayerOptions.Set(TEXT("max_resoY_above_30fps"), Electra::FVariantValue(MaxVerticalHeightAt60));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Limiting vertical resolution to %d for streams >30fps"), PlayerUniqueID, (int32)MaxVerticalHeightAt60);
}
double LiveEdgeDistanceForNormalPresentation = Options->GetMediaOption(ElectraMediaOptions::ElectraLivePresentationOffset, (double)-1.0);
if (LiveEdgeDistanceForNormalPresentation > 0.0)
{
PlayerOptions.Set(TEXT("seekable_range_live_end_offset"), Electra::FVariantValue(Electra::FTimeValue().SetFromSeconds(LiveEdgeDistanceForNormalPresentation)));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Setting distance to live edge for normal presentations to %.3f seconds"), PlayerUniqueID, LiveEdgeDistanceForNormalPresentation);
}
double LiveEdgeDistanceForAudioOnlyPresentation = Options->GetMediaOption(ElectraMediaOptions::ElectraLiveAudioPresentationOffset, (double)-1.0);
if (LiveEdgeDistanceForAudioOnlyPresentation > 0.0)
{
PlayerOptions.Set(TEXT("seekable_range_live_end_offset_audioonly"), Electra::FVariantValue(Electra::FTimeValue().SetFromSeconds(LiveEdgeDistanceForAudioOnlyPresentation)));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Setting distance to live edge for audio-only presentation to %.3f seconds"), PlayerUniqueID, LiveEdgeDistanceForAudioOnlyPresentation);
}
bool bUseConservativeLiveEdgeDistance = Options->GetMediaOption(ElectraMediaOptions::ElectraLiveUseConservativePresentationOffset, (bool)false);
if (bUseConservativeLiveEdgeDistance)
{
PlayerOptions.Set(TEXT("seekable_range_live_end_offset_conservative"), Electra::FVariantValue(bUseConservativeLiveEdgeDistance));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Using conservative live edge for distance calculation"), PlayerUniqueID);
}
bool bThrowErrorWhenRebuffering = Options->GetMediaOption(ElectraMediaOptions::ElectraThrowErrorWhenRebuffering, (bool)false);
if (bThrowErrorWhenRebuffering)
{
PlayerOptions.Set(TEXT("throw_error_when_rebuffering"), Electra::FVariantValue(bThrowErrorWhenRebuffering));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: Throw playback error when rebuffering"), PlayerUniqueID);
}
FString CDNHTTPStatusDenyStream = Options->GetMediaOption(ElectraMediaOptions::ElectraGetDenyStreamCode, FString());
if (CDNHTTPStatusDenyStream.Len())
{
int32 HTTPStatus = -1;
LexFromString(HTTPStatus, *CDNHTTPStatusDenyStream);
if (HTTPStatus > 0 && HTTPStatus < 1000)
{
PlayerOptions.Set(TEXT("abr:cdn_deny_httpstatus"), Electra::FVariantValue((int64)HTTPStatus));
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaPlayer::Open: CDN HTTP status %d will deny a stream permanently"), PlayerUniqueID, HTTPStatus);
}
}
// Check if there is an environment specified in which this player is used.
// Certain optimization settings apply for dedicated environments.
if (Environment == MediaPlayerOptionValues::Environment_Preview() || Environment == MediaPlayerOptionValues::Environment_Sequencer())
{
PlayerOptions.Set(TEXT("worker_threads"), Electra::FVariantValue(FString(TEXT("worker"))));
}
// Check for options that can be changed during playback and apply them at startup already.
// If a media source supports the MaxResolutionForMediaStreaming option then we can override the max resolution.
int64 DefaultValue = 0;
int64 MaxVerticalStreamResolution = Options->GetMediaOption(ElectraMediaOptions::MaxResolutionForMediaStreaming, DefaultValue);
if (MaxVerticalStreamResolution != 0)
{
UE_LOG(LogElectraPlayerPlugin, Log, TEXT("[%u] IMediaPlayer::Open: Limiting max resolution to %d"), PlayerUniqueID, (int32)MaxVerticalStreamResolution);
LocalPlaystartOptions.MaxVerticalStreamResolution = (int32)MaxVerticalStreamResolution;
}
int64 MaxBandwidthForStreaming = Options->GetMediaOption(ElectraMediaOptions::ElectraMaxStreamingBandwidth, (int64)0);
if (MaxBandwidthForStreaming > 0)
{
UE_LOG(LogElectraPlayerPlugin, Log, TEXT("[%u] Limiting max streaming bandwidth to %d bps"), PlayerUniqueID, (int32)MaxBandwidthForStreaming);
LocalPlaystartOptions.MaxBandwidthForStreaming = (int32)MaxBandwidthForStreaming;
}
bMetadataChanged = false;
CurrentMetadata.Reset();
// Check if we can get a segment cache interface for this playback request...
TSharedPtr<FElectraPlayerDataCacheContainer, ESPMode::ThreadSafe> ElectraPlayerDataCacheContainer;
TSharedPtr<IMediaOptions::FDataContainer, ESPMode::ThreadSafe> ElectraPlayerDataCacheDefaultValue;
TSharedPtr<IMediaOptions::FDataContainer, ESPMode::ThreadSafe> DataContainer = Options->GetMediaOption(ElectraMediaOptions::ElectraPlayerDataCache, ElectraPlayerDataCacheDefaultValue);
if (DataContainer.IsValid())
{
ElectraPlayerDataCacheContainer = StaticCastSharedPtr<FElectraPlayerDataCacheContainer, IMediaOptions::FDataContainer, ESPMode::ThreadSafe>(DataContainer);
if (ElectraPlayerDataCacheContainer.IsValid())
{
LocalPlaystartOptions.ExternalDataCache = ElectraPlayerDataCacheContainer->Data;
}
}
return Player->OpenInternal(Url, PlayerOptions, LocalPlaystartOptions, IElectraPlayerInterface::EOpenType::Media);
}
//-----------------------------------------------------------------------------
/**
*
*/
bool FElectraPlayerPlugin::Open(const TSharedRef<FArchive, ESPMode::ThreadSafe>& Archive, const FString& OriginalUrl, const IMediaOptions* /*Options*/)
{
// we support playback only from an external file, not from a "resource" (e.g. a packaged asset)
UE_LOG(LogElectraPlayerPlugin, Error, TEXT("[%u] IMediaPlayer::Archive"), PlayerUniqueID);
unimplemented();
return false;
}
//-----------------------------------------------------------------------------
/**
* Internal Close / Shutdown player
*/
void FElectraPlayerPlugin::Close()
{
CallbackPointerLock.Lock();
OptionInterface.Reset();
CallbackPointerLock.Unlock();
Player->CloseInternal(true);
}
//-----------------------------------------------------------------------------
/**
* Tick the player from the game thread
*/
void FElectraPlayerPlugin::TickInput(FTimespan DeltaTime, FTimespan Timecode)
{
OutputTexturePool->Tick();
Player->Tick(DeltaTime, Timecode);
}
FVariant FElectraPlayerPlugin::GetMediaInfo(FName InInfoName) const
{
return Player.IsValid() ? Player->GetMediaInfo(InInfoName).ToFVariant() : FVariant();
}
//-----------------------------------------------------------------------------
/**
Returns the current metadata, if any.
*/
TSharedPtr<TMap<FString, TArray<TUniquePtr<IMediaMetadataItem>>>, ESPMode::ThreadSafe> FElectraPlayerPlugin::GetMediaMetadata() const
{
if (bMetadataChanged && Player.IsValid())
{
TSharedPtr<TMap<FString, TArray<TSharedPtr<Electra::IMediaStreamMetadata::IItem, ESPMode::ThreadSafe>>>, ESPMode::ThreadSafe> PlayerMeta = Player->GetMediaMetadata();
if (PlayerMeta.IsValid())
{
TSharedPtr<TMap<FString, TArray<TUniquePtr<IMediaMetadataItem>>>, ESPMode::ThreadSafe> NewMeta(new TMap<FString, TArray<TUniquePtr<IMediaMetadataItem>>>);
for(auto& PlayerMetaItem : *PlayerMeta)
{
TArray<TUniquePtr<IMediaMetadataItem>>& NewItemList = NewMeta->Emplace(PlayerMetaItem.Key);
for(auto& PlayerMetaListItem : PlayerMetaItem.Value)
{
if (PlayerMetaListItem.IsValid())
{
NewItemList.Emplace(MakeUnique<FStreamMetadataItem>(PlayerMetaListItem));
}
}
}
bMetadataChanged = false;
CurrentMetadata = MoveTemp(NewMeta);
}
}
return CurrentMetadata;
}
void FElectraPlayerPlugin::SetMetadataChanged()
{
bMetadataChanged = true;
}
//-----------------------------------------------------------------------------
/**
Get special feature flags states
*/
bool FElectraPlayerPlugin::GetPlayerFeatureFlag(EFeatureFlag flag) const
{
switch(flag)
{
case EFeatureFlag::AllowShutdownOnClose:
return Player->IsKillAfterCloseAllowed();
case EFeatureFlag::UsePlaybackTimingV2:
return true;
case EFeatureFlag::PlayerUsesInternalFlushOnSeek:
return true;
case EFeatureFlag::IsTrackSwitchSeamless:
return true;
case EFeatureFlag::PlayerSelectsDefaultTracks:
return true;
default:
break;
}
return IMediaPlayer::GetPlayerFeatureFlag(flag);
}
//-----------------------------------------------------------------------------
/**
Set a notification to be signaled once any async tear down of the instance is done
*/
bool FElectraPlayerPlugin::SetAsyncResourceReleaseNotification(IAsyncResourceReleaseNotificationRef AsyncResourceReleaseNotification)
{
class FAsyncResourceReleaseNotifyContainer : public IElectraPlayerInterface::IAsyncResourceReleaseNotifyContainer
{
public:
FAsyncResourceReleaseNotifyContainer(IAsyncResourceReleaseNotificationRef InAsyncResourceReleaseNotification) : AsyncResourceReleaseNotification(InAsyncResourceReleaseNotification) {}
virtual void Signal(uint32 ResourceFlags) override { AsyncResourceReleaseNotification->Signal(ResourceFlags); }
private:
IAsyncResourceReleaseNotificationRef AsyncResourceReleaseNotification;
};
Player->SetAsyncResourceReleaseNotification(new FAsyncResourceReleaseNotifyContainer(AsyncResourceReleaseNotification));
return true;
}
uint32 FElectraPlayerPlugin::GetNewResourcesOnOpen() const
{
// Electra will recreate all decoder related resources on each open call
// (a simplification: we also recreate the texture pool should it change sizes on SOME platforms - but we reoprt the release only per instance, so thisb matches that)
return IMediaPlayerLifecycleManagerDelegate::ResourceFlags_Decoder;
}
//////////////////////////////////////////////////////////////////////////
// IMediaControl impl
//-----------------------------------------------------------------------------
/**
* Currently, we cannot do anything.. well, at least we can play!
*/
bool FElectraPlayerPlugin::CanControl(EMediaControl Control) const
{
EMediaState CurrentState = GetState();
if (Control == EMediaControl::BlockOnFetch)
{
return CurrentState == EMediaState::Playing || CurrentState == EMediaState::Paused;
}
else if (Control == EMediaControl::Pause)
{
return CurrentState == EMediaState::Playing;
}
else if (Control == EMediaControl::Resume)
{
return CurrentState == EMediaState::Paused || CurrentState == EMediaState::Stopped;
}
else if (Control == EMediaControl::Seek || Control == EMediaControl::Scrub)
{
return CurrentState == EMediaState::Playing || CurrentState == EMediaState::Paused || CurrentState == EMediaState::Stopped;
}
else if (Control == EMediaControl::PlaybackRange)
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------
/**
* Rate is only real-time
*/
float FElectraPlayerPlugin::GetRate() const
{
return Player->GetRate();
}
//-----------------------------------------------------------------------------
/**
* Expose player state
*/
EMediaState FElectraPlayerPlugin::GetState() const
{
return (EMediaState)Player->GetState();
}
//-----------------------------------------------------------------------------
/**
* Expose player status
*/
EMediaStatus FElectraPlayerPlugin::GetStatus() const
{
return (EMediaStatus)Player->GetStatus();
}
bool FElectraPlayerPlugin::IsLooping() const
{
return Player->IsLooping();
}
bool FElectraPlayerPlugin::SetLooping(bool bLooping)
{
return Player->SetLooping(bLooping);
}
//-----------------------------------------------------------------------------
/**
* Only return real-time playback for the moment..
*/
TRangeSet<float> FElectraPlayerPlugin::GetSupportedRates(EMediaRateThinning Thinning) const
{
return Player->GetSupportedRates(Thinning == EMediaRateThinning::Thinned ? IElectraPlayerInterface::EPlayRateType::Thinned : IElectraPlayerInterface::EPlayRateType::Unthinned);
}
FTimespan FElectraPlayerPlugin::GetTime() const
{
return Player->GetTime();
}
FTimespan FElectraPlayerPlugin::GetDuration() const
{
return Player->GetDuration();
}
bool FElectraPlayerPlugin::SetRate(float Rate)
{
UE_LOG(LogElectraPlayerPlugin, Log, TEXT("[%u] IMediaControls::SetRate(%f)"), PlayerUniqueID, Rate);
CSV_EVENT(ElectraPlayer, TEXT("Setting Rate"));
return Player->SetRate(Rate);
}
bool FElectraPlayerPlugin::Seek(const FTimespan& Time, const FMediaSeekParams& InAdditionalParams)
{
UE_LOG(LogElectraPlayerPlugin, Verbose, TEXT("[%u] IMediaControls::Seek() to %s"), PlayerUniqueID, *Time.ToString(TEXT("%h:%m:%s.%f")));
CSV_EVENT(ElectraPlayer, TEXT("Seeking"));
IElectraPlayerInterface::FSeekParam sp;
sp.SequenceIndex = InAdditionalParams.NewSequenceIndex;
return Player->Seek(Time, sp);
}
TRange<FTimespan> FElectraPlayerPlugin::GetPlaybackTimeRange(EMediaTimeRangeType InRangeToGet) const
{
return Player->GetPlaybackRange(static_cast<IElectraPlayerInterface::ETimeRangeType>(InRangeToGet));
}
bool FElectraPlayerPlugin::SetPlaybackTimeRange(const TRange<FTimespan>& InTimeRange)
{
IElectraPlayerInterface::FPlaybackRange Range;
Range.Start = InTimeRange.GetLowerBoundValue();
Range.End = InTimeRange.GetUpperBoundValue();
Player->SetPlaybackRange(Range);
return true;
}
bool FElectraPlayerPlugin::QueryCacheState(EMediaCacheState State, TRangeSet<FTimespan>& OutTimeRanges) const
{
// Note: The data of time ranges returned here will not actually get "cached" as
// it is always only transient. We thus report the ranges only for `Loaded` and `Loading`,
// but never for `Cached`!
switch(State)
{
case EMediaCacheState::Loaded:
case EMediaCacheState::Loading:
case EMediaCacheState::Pending:
{
// When asked to provide what's already loaded we look at what we have in the sample queue
// and add that to the result. These samples have already left the player but are ready
// for use.
if (State == EMediaCacheState::Loaded)
{
TArray<TRange<FMediaTimeStamp>> QueuedRange;
FScopeLock SampleLock(&MediaSamplesLock);
if (MediaSamples.IsValid() && MediaSamples->PeekVideoSampleTimeRanges(QueuedRange) && QueuedRange.Num())
{
OutTimeRanges.Add(TRange<FTimespan>(QueuedRange[0].GetLowerBoundValue().Time, QueuedRange.Last().GetUpperBoundValue().Time));
}
}
// Get the data time range from the player. It returns both current and future data in one call, so we
// separate the result here based on what is being asked for.
IElectraPlayerInterface::FStreamBufferInfo vidBuf, audBuf;
bool bHaveVid = Player->GetStreamBufferInformation(vidBuf, IElectraPlayerInterface::EPlayerTrackType::Video);
bool bHaveAud = !bHaveVid ? Player->GetStreamBufferInformation(audBuf, IElectraPlayerInterface::EPlayerTrackType::Audio) : false;
const IElectraPlayerInterface::FStreamBufferInfo* Buffer = bHaveVid ? &vidBuf : bHaveAud ? &audBuf : nullptr;
const TArray<IElectraPlayerInterface::FStreamBufferInfo::FTimeRange>* tr = nullptr;
if (Buffer)
{
switch(State)
{
case EMediaCacheState::Loaded:
tr = &Buffer->TimeEnqueued;
break;
case EMediaCacheState::Loading:
tr = &Buffer->TimeAvailable;
break;
case EMediaCacheState::Pending:
tr = &Buffer->TimeRequested;
break;
}
}
for(int32 i=0,iMax=tr?tr->Num():0; i<iMax; ++i)
{
OutTimeRanges.Add(TRange<FTimespan>(tr->operator[](i).Start.Time, tr->operator[](i).End.Time));
}
return true;
}
}
return false;
}
bool FElectraPlayerPlugin::GetAudioTrackFormat(int32 TrackIndex, int32 FormatIndex, FMediaAudioTrackFormat& OutFormat) const
{
IElectraPlayerInterface::FAudioTrackFormat Format;
if (!Player->GetAudioTrackFormat(TrackIndex, FormatIndex, Format))
{
return false;
}
OutFormat.BitsPerSample = Format.BitsPerSample;
OutFormat.NumChannels = Format.NumChannels;
OutFormat.SampleRate = Format.SampleRate;
OutFormat.TypeName = Format.TypeName;
return true;
}
bool FElectraPlayerPlugin::GetVideoTrackFormat(int32 TrackIndex, int32 FormatIndex, FMediaVideoTrackFormat& OutFormat) const
{
IElectraPlayerInterface::FVideoTrackFormat Format;
if (!Player->GetVideoTrackFormat(TrackIndex, FormatIndex, Format))
{
return false;
}
OutFormat.Dim = Format.Dim;
OutFormat.FrameRate = Format.FrameRate;
OutFormat.FrameRates = Format.FrameRates;
OutFormat.TypeName = Format.TypeName;
return true;
}
int32 FElectraPlayerPlugin::GetNumTracks(EMediaTrackType TrackType) const
{
return Player->GetNumTracks((IElectraPlayerInterface::EPlayerTrackType)TrackType);
}
int32 FElectraPlayerPlugin::GetNumTrackFormats(EMediaTrackType TrackType, int32 TrackIndex) const
{
return Player->GetNumTrackFormats((IElectraPlayerInterface::EPlayerTrackType)TrackType, TrackIndex);
}
int32 FElectraPlayerPlugin::GetSelectedTrack(EMediaTrackType TrackType) const
{
return Player->GetSelectedTrack((IElectraPlayerInterface::EPlayerTrackType)TrackType);
}
FText FElectraPlayerPlugin::GetTrackDisplayName(EMediaTrackType TrackType, int32 TrackIndex) const
{
return Player->GetTrackDisplayName((IElectraPlayerInterface::EPlayerTrackType)TrackType, TrackIndex);
}
int32 FElectraPlayerPlugin::GetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex) const
{
return Player->GetTrackFormat((IElectraPlayerInterface::EPlayerTrackType)TrackType, TrackIndex);
}
FString FElectraPlayerPlugin::GetTrackLanguage(EMediaTrackType TrackType, int32 TrackIndex) const
{
return Player->GetTrackLanguage((IElectraPlayerInterface::EPlayerTrackType)TrackType, TrackIndex);
}
FString FElectraPlayerPlugin::GetTrackName(EMediaTrackType TrackType, int32 TrackIndex) const
{
return Player->GetTrackName((IElectraPlayerInterface::EPlayerTrackType)TrackType, TrackIndex);
}
bool FElectraPlayerPlugin::SelectTrack(EMediaTrackType TrackType, int32 TrackIndex)
{
return Player->SelectTrack((IElectraPlayerInterface::EPlayerTrackType)TrackType, TrackIndex);
}
bool FElectraPlayerPlugin::SetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex, int32 FormatIndex)
{
return false;
}
bool FElectraPlayerPlugin::SetVideoTrackFrameRate(int32 TrackIndex, int32 FormatIndex, float FrameRate)
{
return false;
}