code
This commit is contained in:
@ -0,0 +1,151 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// The main IMediaPlayer implementation. This is a
|
||||
// shim which provides Unreal interfaces, and then
|
||||
// defers everything vulkan related to the main
|
||||
// AndroidVulkanVideoImpl class.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "IMediaCache.h"
|
||||
#include "IMediaControls.h"
|
||||
#include "IMediaEventSink.h"
|
||||
#include "IMediaPlayer.h"
|
||||
#include "IMediaTracks.h"
|
||||
#include "IMediaView.h"
|
||||
#include "MediaSamples.h"
|
||||
|
||||
// for vulkan types in the callback fn
|
||||
#include "IVulkanDynamicRHI.h"
|
||||
|
||||
#include "IAndroidVulkanVideoAVCallback.h"
|
||||
|
||||
#include "AndroidVulkanTextureSample.h"
|
||||
#include "VideoMediaSampleHolder.h"
|
||||
|
||||
class IAudioOut;
|
||||
class IVulkanImpl;
|
||||
|
||||
class FAndroidVulkanMediaPlayer : public IMediaPlayer,
|
||||
protected IMediaCache,
|
||||
protected IMediaControls,
|
||||
protected IMediaTracks,
|
||||
protected IMediaView,
|
||||
protected IAndroidVulkanVideoAVCallback
|
||||
{
|
||||
public:
|
||||
FAndroidVulkanMediaPlayer(IMediaEventSink &InEventSink);
|
||||
virtual ~FAndroidVulkanMediaPlayer();
|
||||
|
||||
// IMediaPlayer
|
||||
virtual void Close() override;
|
||||
virtual IMediaCache &GetCache() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual IMediaControls &GetControls() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual bool FlushOnSeekCompleted() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual FString GetInfo() const override;
|
||||
virtual FGuid GetPlayerPluginGUID() const override;
|
||||
virtual IMediaSamples &GetSamples() override;
|
||||
virtual FString GetStats() const override;
|
||||
virtual IMediaTracks &GetTracks() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual FString GetUrl() const override;
|
||||
virtual IMediaView &GetView() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual bool Open(const FString &Url, const IMediaOptions *Options) override;
|
||||
virtual bool Open(const TSharedRef<FArchive, ESPMode::ThreadSafe> &Archive,
|
||||
const FString &OriginalUrl, const IMediaOptions *Options) override;
|
||||
virtual void SetGuid(const FGuid &Guid) override;
|
||||
virtual void TickFetch(FTimespan DeltaTime, FTimespan Timecode) override;
|
||||
virtual void TickInput(FTimespan DeltaTime, FTimespan Timecode) override;
|
||||
virtual bool GetPlayerFeatureFlag(EFeatureFlag flag) const override;
|
||||
|
||||
// IMediaCache
|
||||
// don't override these - we don't provide caching (yet?)
|
||||
// virtual bool QueryCacheState(EMediaCacheState State, TRangeSet<FTimespan>& OutTimeRanges)
|
||||
// const virtual int32 GetSampleCount(EMediaCacheState State) const
|
||||
|
||||
// IMediaControl
|
||||
virtual bool CanControl(EMediaControl Control) const override;
|
||||
virtual FTimespan GetDuration() const override;
|
||||
virtual float GetRate() const override;
|
||||
virtual EMediaState GetState() const override;
|
||||
virtual EMediaStatus GetStatus() const override;
|
||||
virtual TRangeSet<float> GetSupportedRates(EMediaRateThinning Thinning) const override;
|
||||
virtual FTimespan GetTime() const override;
|
||||
virtual bool IsLooping() const override;
|
||||
virtual bool Seek(const FTimespan &Time) override;
|
||||
virtual bool SetLooping(bool Looping) override;
|
||||
virtual bool SetRate(float Rate) override;
|
||||
virtual bool SetNativeVolume(float Volume) override;
|
||||
|
||||
// IMediaTracks
|
||||
virtual bool GetAudioTrackFormat(int32 TrackIndex, int32 FormatIndex,
|
||||
FMediaAudioTrackFormat &OutFormat) const override;
|
||||
virtual int32 GetNumTracks(EMediaTrackType TrackType) const override;
|
||||
virtual int32 GetNumTrackFormats(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual int32 GetSelectedTrack(EMediaTrackType TrackType) const override;
|
||||
virtual FText GetTrackDisplayName(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual int32 GetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual FString GetTrackLanguage(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual FString GetTrackName(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual bool GetVideoTrackFormat(int32 TrackIndex, int32 FormatIndex,
|
||||
FMediaVideoTrackFormat &OutFormat) const override;
|
||||
virtual bool SelectTrack(EMediaTrackType TrackType, int32 TrackIndex) override;
|
||||
virtual bool SetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex,
|
||||
int32 FormatIndex) override;
|
||||
|
||||
// IMediaView
|
||||
// don't provide these features, so we don't override any of it
|
||||
|
||||
// IAndroidVulkanVideoAVCallback (impl callback)
|
||||
virtual void onVideoFrame(void *frameHwBuffer, int w, int h, int64_t presTimeNs) override;
|
||||
virtual void *getVkDeviceProcAddr(const char *name) override;
|
||||
virtual VkDevice getVkDevice() override;
|
||||
virtual const VkAllocationCallbacks *getVkAllocationCallbacks() override;
|
||||
virtual VkPhysicalDevice getNativePhysicalDevice() override;
|
||||
virtual void onPlaybackEnd(bool looping) override;
|
||||
|
||||
virtual void ProcessVideoSamples() override;
|
||||
|
||||
private:
|
||||
// delegates for foreground -> background transitions which should pause
|
||||
// and resume playback
|
||||
void OnEnterBackground();
|
||||
void OnEnterForeground();
|
||||
|
||||
FDelegateHandle DelegateEnterBackground;
|
||||
FDelegateHandle DelegateEnterForeground;
|
||||
|
||||
bool Seeking;
|
||||
int32 SeekIndex;
|
||||
|
||||
// our output samples
|
||||
TSharedPtr<FVideoMediaSampleHolder, ESPMode::ThreadSafe> SampleQueue;
|
||||
IMediaEventSink &EventSink;
|
||||
IVulkanImpl *impl;
|
||||
IAudioOut *AudioOut;
|
||||
FString CurInfo;
|
||||
FString VideoURL;
|
||||
FGuid PlayerGUID;
|
||||
EMediaState PlayState;
|
||||
bool Looping;
|
||||
FAndroidVulkanTextureSamplePool VideoSamplePool;
|
||||
bool HasVideoThisFrame;
|
||||
bool SentBlankFrame;
|
||||
};
|
@ -0,0 +1,190 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Vulkan texture sample - holds an opaque pointer
|
||||
// to an Android AHardwareBuffer, and provides the
|
||||
// IMediaTextureSample and
|
||||
// IMediaTextureSampleConverter interfaces to allow
|
||||
// Unreal's media player to show the frame.
|
||||
// ------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IMediaTextureSample.h"
|
||||
#include "IMediaTextureSampleConverter.h"
|
||||
#include "MediaObjectPool.h"
|
||||
|
||||
#include "IVulkanVertexData.h"
|
||||
|
||||
#define TMP_REPLACE 5
|
||||
|
||||
class IVulkanImpl;
|
||||
|
||||
class AndroidVulkanTextureSample : public IMediaTextureSample,
|
||||
public IMediaTextureSampleConverter,
|
||||
public IMediaPoolable
|
||||
{
|
||||
friend class FAndroidVulkanTextureSamplePool;
|
||||
|
||||
public:
|
||||
AndroidVulkanTextureSample();
|
||||
|
||||
void Init(IVulkanImpl *impl, void *hwImage, int w, int h, FMediaTimeStamp sampleTime);
|
||||
void InitNoVideo();
|
||||
|
||||
~AndroidVulkanTextureSample();
|
||||
|
||||
virtual const void *GetBuffer() override
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual FIntPoint GetDim() const override;
|
||||
virtual FTimespan GetDuration() const override
|
||||
{
|
||||
return FTimespan::Zero();
|
||||
}
|
||||
virtual EMediaTextureSampleFormat GetFormat() const override
|
||||
{
|
||||
return VideoTextureFormat;
|
||||
}
|
||||
|
||||
virtual FIntPoint GetOutputDim() const override;
|
||||
virtual uint32 GetStride() const override;
|
||||
virtual FRHITexture *GetTexture() const override;
|
||||
virtual IMediaTextureSampleConverter *GetMediaTextureSampleConverter() override;
|
||||
|
||||
virtual FMediaTimeStamp GetTime() const;
|
||||
|
||||
virtual bool IsCacheable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool IsOutputSrgb() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// IMediaTextureSampleConverter interface (changed in 5.5 to add command list)
|
||||
|
||||
virtual bool Convert(FTexture2DRHIRef &InDstTexture, const FConversionHints &Hints);
|
||||
|
||||
bool ConvertInternal(FRHICommandListImmediate &RHICmdList, FTexture2DRHIRef &InDstTexture,
|
||||
const FConversionHints &Hints);
|
||||
|
||||
// called inside render pass
|
||||
bool RenderToMesh(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList, void *MeshID);
|
||||
|
||||
// called outside a render pass
|
||||
// n.b. don't make matrices etc. const references
|
||||
// because then we get dangling references and
|
||||
// BAD THINGS happen
|
||||
bool UpdateViewMatrices(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList,
|
||||
ShaderViewMatrices Matrices, void *MeshID);
|
||||
bool InitFrameForMeshRendering(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList,
|
||||
void *MeshID);
|
||||
bool UpdateMesh(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList,
|
||||
TArray<VertexData> PositionAndUV, TArray<uint32> Indices,
|
||||
ShaderModelMatrix Matrix, void *MeshID);
|
||||
|
||||
void InitializePoolable() override;
|
||||
virtual bool IsReadyForReuse() override;
|
||||
virtual void ShutdownPoolable() override;
|
||||
|
||||
void Clear();
|
||||
void ClearIfShown();
|
||||
|
||||
static void SetVideoFormat(EMediaTextureSampleFormat Format)
|
||||
{
|
||||
VideoTextureFormat = Format;
|
||||
}
|
||||
|
||||
void ImplDeleted();
|
||||
|
||||
private:
|
||||
void FRenderOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture);
|
||||
void FRenderMeshOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture, void *MeshID,
|
||||
int Samples);
|
||||
void FUpdateMeshOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture,
|
||||
const TArray<VertexData> &PositionAndUV,
|
||||
const TArray<uint32> &Indices,
|
||||
const ShaderModelMatrix &Matrix, void *MeshID);
|
||||
void FUpdateMatricesOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture,
|
||||
const ShaderViewMatrices &Matrices, void *MeshID);
|
||||
void FInitMeshRenderingCommand(const FTexture2DRHIRef &DstTexture, void *MeshID, int Samples);
|
||||
bool IsEmptyTexture;
|
||||
FGPUFenceRHIRef Fence;
|
||||
|
||||
FIntPoint Dimension;
|
||||
void *FrameHWImage;
|
||||
|
||||
/** Duration for which the sample is valid. */
|
||||
// FTimespan TimeDuration;
|
||||
|
||||
/** Sample time. */
|
||||
FMediaTimeStamp SampleTime;
|
||||
|
||||
int NumSamples; // number of samples used for MSAA on rendering meshes directly
|
||||
|
||||
IVulkanImpl *Impl;
|
||||
bool Shown;
|
||||
|
||||
FCriticalSection AccessLock;
|
||||
|
||||
static EMediaTextureSampleFormat VideoTextureFormat;
|
||||
};
|
||||
|
||||
class FAndroidVulkanTextureSamplePool : public TMediaObjectPool<AndroidVulkanTextureSample>
|
||||
{
|
||||
public:
|
||||
void Tick()
|
||||
{
|
||||
TMediaObjectPool<AndroidVulkanTextureSample>::Tick();
|
||||
for (auto wp : liveObjects)
|
||||
{
|
||||
auto pin = wp.Pin();
|
||||
if (pin.IsValid())
|
||||
{
|
||||
pin->ClearIfShown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<AndroidVulkanTextureSample, ESPMode::ThreadSafe> AcquireShared()
|
||||
{
|
||||
auto ret = TMediaObjectPool<AndroidVulkanTextureSample>::AcquireShared();
|
||||
TWeakPtr<AndroidVulkanTextureSample, ESPMode::ThreadSafe> wp = ret;
|
||||
if (!liveObjects.Find(wp))
|
||||
{
|
||||
liveObjects.Add(wp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ReleaseEverything()
|
||||
{
|
||||
for (auto wp : liveObjects)
|
||||
{
|
||||
auto pin = wp.Pin();
|
||||
if (pin.IsValid())
|
||||
{
|
||||
pin->ImplDeleted();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wp : liveObjects)
|
||||
{
|
||||
auto pin = wp.Pin();
|
||||
if (pin.IsValid())
|
||||
{
|
||||
pin->ShutdownPoolable();
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
int bob[TMP_REPLACE];
|
||||
// for anything that has already been drawn, we want to release textures ASAP,
|
||||
// rather than whenever Unreal gets round to it.
|
||||
TArray<TWeakPtr<AndroidVulkanTextureSample, ESPMode::ThreadSafe>> liveObjects;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// AndroidVulkanVideo module implementation
|
||||
// ------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#include "IMediaEventSink.h"
|
||||
#include "IMediaPlayer.h"
|
||||
|
||||
class IAndroidVulkanVideoModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual TSharedPtr<IMediaPlayer, ESPMode::ThreadSafe> CreatePlayer(
|
||||
IMediaEventSink &EventSink) = 0;
|
||||
|
||||
private:
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Callback used by implementation class to send
|
||||
// buffers to media player.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
class IAndroidVulkanVideoAVCallback
|
||||
{
|
||||
public:
|
||||
// called with video frame hardware buffers
|
||||
virtual void onVideoFrame(void *frameHwBuffer, int w, int h, int64_t presTimeNs) = 0;
|
||||
// we need to get vulkan functions and device etc. via Unreal Engine for consistency
|
||||
virtual void *getVkDeviceProcAddr(const char *name) = 0;
|
||||
virtual VkDevice getVkDevice() = 0;
|
||||
virtual const VkAllocationCallbacks *getVkAllocationCallbacks() = 0;
|
||||
virtual VkPhysicalDevice getNativePhysicalDevice() = 0;
|
||||
|
||||
virtual void onPlaybackEnd(bool looping) = 0;
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
struct VertexData
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float _ignored1;
|
||||
float u;
|
||||
float v;
|
||||
float _ignored2;
|
||||
float _ignored3;
|
||||
};
|
||||
|
||||
struct ShaderModelMatrix
|
||||
{
|
||||
ShaderModelMatrix()
|
||||
{
|
||||
memset(this, 0, sizeof(ShaderModelMatrix));
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
m44[c + c * 4] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
float m44[16];
|
||||
};
|
||||
|
||||
struct ShaderViewMatrices
|
||||
{
|
||||
ShaderViewMatrices()
|
||||
{
|
||||
memset(this, 0, sizeof(ShaderViewMatrices));
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
vp44[c + c * 4] = 1;
|
||||
vp44_2[c + c * 4] = 1;
|
||||
}
|
||||
}
|
||||
float vp44[16];
|
||||
float vp44_2[16];
|
||||
float pre_view_translation[4];
|
||||
float pre_view_translation_2[4];
|
||||
};
|
Reference in New Issue
Block a user