This commit is contained in:
2025-07-14 22:24:27 +08:00
parent daacc18ecf
commit 4af19ef574
7722 changed files with 72086 additions and 0 deletions

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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:
};

View File

@ -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;
};

View File

@ -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];
};