You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This resolves bitrate issues in WebRTC caused by inconsistent frame rates. These changes are intended for 5.0 and licencees who are using source builds of the engine to get the updates before 5.0 is released. #JIRA UCS-2077 #rb Aidan.Possemiers [FYI] Luke.Bermingham, Nick.Pace, Matthew.Cotton, Marco.Anastasi #lockdown Mitchell.Wilson #ROBOMERGE-AUTHOR: aidan.possemiers #ROBOMERGE-SOURCE: CL 18267227 in //UE4/Main/... via CL 18267244 via CL 18267255 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v895-18170469) #ROBOMERGE[STARSHIP]: UE5-Main [CL 18267257 by aidan possemiers in ue5-release-engine-test branch]
1075 lines
32 KiB
C++
1075 lines
32 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VideoEncoderInput.h"
|
|
#include "VideoEncoderInputImpl.h"
|
|
#include "VideoEncoderCommon.h"
|
|
#include "VideoEncoderFactory.h"
|
|
#include "AVEncoderDebug.h"
|
|
#include "Misc/Paths.h"
|
|
#include "GenericPlatform/GenericPlatformMath.h"
|
|
|
|
#include "Misc/Guid.h"
|
|
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
#include "VulkanRHIBridge.h"
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
#include "MicrosoftCommon.h"
|
|
#endif
|
|
|
|
FString GetGUID()
|
|
{
|
|
static FGuid id;
|
|
if (!id.IsValid())
|
|
{
|
|
id = FGuid::NewGuid();
|
|
}
|
|
|
|
return id.ToString();
|
|
}
|
|
|
|
namespace AVEncoder
|
|
{
|
|
// *** FVideoEncoderInput *************************************************************************
|
|
|
|
// --- construct video encoder input based on expected input frame format -------------------------
|
|
|
|
TSharedPtr<FVideoEncoderInput> FVideoEncoderInput::CreateDummy(uint32 InWidth, uint32 InHeight, bool bIsResizable)
|
|
{
|
|
TSharedPtr<FVideoEncoderInputImpl> Input = MakeShared<FVideoEncoderInputImpl>();
|
|
Input->bIsResizable = bIsResizable;
|
|
|
|
if (!Input->SetupForDummy(InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
return Input;
|
|
}
|
|
|
|
TSharedPtr<FVideoEncoderInput> FVideoEncoderInput::CreateForYUV420P(uint32 InWidth, uint32 InHeight, bool bIsResizable)
|
|
{
|
|
TSharedPtr<FVideoEncoderInputImpl> Input = MakeShared<FVideoEncoderInputImpl>();
|
|
Input->bIsResizable = bIsResizable;
|
|
|
|
if (!Input->SetupForYUV420P(InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
return Input;
|
|
}
|
|
|
|
TSharedPtr<FVideoEncoderInput> FVideoEncoderInput::CreateForD3D11(void* InApplicationD3DDevice, uint32 InWidth, uint32 InHeight, bool bIsResizable, bool IsShared)
|
|
{
|
|
TSharedPtr<FVideoEncoderInputImpl> Input = MakeShared<FVideoEncoderInputImpl>();
|
|
|
|
#if PLATFORM_WINDOWS
|
|
Input->bIsResizable = bIsResizable;
|
|
|
|
if (IsShared)
|
|
{
|
|
if (!Input->SetupForD3D11Shared(static_cast<ID3D11Device*>(InApplicationD3DDevice), InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Input->SetupForD3D11(static_cast<ID3D11Device*>(InApplicationD3DDevice), InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return Input;
|
|
}
|
|
|
|
|
|
TSharedPtr<FVideoEncoderInput> FVideoEncoderInput::CreateForD3D12(void* InApplicationD3DDevice, uint32 InWidth, uint32 InHeight, bool bIsResizable, bool IsShared)
|
|
{
|
|
TSharedPtr<FVideoEncoderInputImpl> Input = MakeShared<FVideoEncoderInputImpl>();
|
|
|
|
#if PLATFORM_WINDOWS
|
|
Input->bIsResizable = bIsResizable;
|
|
|
|
if (IsShared)
|
|
{
|
|
if (!Input->SetupForD3D12Shared(static_cast<ID3D12Device*>(InApplicationD3DDevice), InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Input->SetupForD3D12(static_cast<ID3D12Device*>(InApplicationD3DDevice), InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return Input;
|
|
}
|
|
|
|
TSharedPtr<FVideoEncoderInput> FVideoEncoderInput::CreateForCUDA(void* InApplicationContext, uint32 InWidth, uint32 InHeight, bool bIsResizable)
|
|
{
|
|
TSharedPtr<FVideoEncoderInputImpl> Input = MakeShared<FVideoEncoderInputImpl>();
|
|
Input->bIsResizable = bIsResizable;
|
|
|
|
if (!Input->SetupForCUDA(reinterpret_cast<CUcontext>(InApplicationContext), InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
return Input;
|
|
}
|
|
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
TSharedPtr<FVideoEncoderInput> FVideoEncoderInput::CreateForVulkan(void* InApplicationVulkanData, uint32 InWidth, uint32 InHeight, bool bIsResizable)
|
|
{
|
|
TSharedPtr<FVideoEncoderInputImpl> Input = MakeShared<FVideoEncoderInputImpl>();
|
|
Input->bIsResizable = bIsResizable;
|
|
|
|
FVulkanDataStruct* VulkanData = static_cast<FVulkanDataStruct*>(InApplicationVulkanData);
|
|
|
|
if (!Input->SetupForVulkan(VulkanData->VulkanInstance, VulkanData->VulkanPhysicalDevice, VulkanData->VulkanDevice, InWidth, InHeight))
|
|
{
|
|
Input.Reset();
|
|
}
|
|
|
|
return Input;
|
|
}
|
|
#endif
|
|
|
|
void FVideoEncoderInput::SetResolution(uint32 InWidth, uint32 InHeight)
|
|
{
|
|
Width = InWidth;
|
|
Height = InHeight;
|
|
}
|
|
|
|
void FVideoEncoderInput::SetMaxNumBuffers(uint32 InMaxNumBuffers)
|
|
{
|
|
MaxNumBuffers = InMaxNumBuffers;
|
|
}
|
|
|
|
// --- encoder input frames -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
// *** FVideoEncoderInputImpl *********************************************************************
|
|
|
|
FVideoEncoderInputImpl::~FVideoEncoderInputImpl()
|
|
{
|
|
{
|
|
FScopeLock Guard(&ProtectFrames);
|
|
if (ActiveFrames.Num() > 0)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("There are still %d active input frames."), ActiveFrames.Num());
|
|
}
|
|
|
|
check(ActiveFrames.Num() == 0);
|
|
|
|
while (!AvailableFrames.IsEmpty())
|
|
{
|
|
FVideoEncoderInputFrameImpl* Frame = nullptr;
|
|
AvailableFrames.Dequeue(Frame);
|
|
delete Frame;
|
|
}
|
|
}
|
|
#if PLATFORM_WINDOWS
|
|
// DEBUG_D3D11_REPORT_LIVE_DEVICE_OBJECT(FrameInfoD3D.EncoderDeviceD3D11);
|
|
#endif
|
|
}
|
|
|
|
bool FVideoEncoderInputImpl::SetupForDummy(uint32 InWidth, uint32 InHeight)
|
|
{
|
|
FrameFormat = EVideoFrameFormat::Undefined;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
return true;
|
|
}
|
|
|
|
bool FVideoEncoderInputImpl::SetupForYUV420P(uint32 InWidth, uint32 InHeight)
|
|
{
|
|
FrameFormat = EVideoFrameFormat::YUV420P;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
FrameInfoYUV420P.StrideY = InWidth;
|
|
FrameInfoYUV420P.StrideU = (InWidth + 1) / 2;
|
|
FrameInfoYUV420P.StrideV = (InWidth + 1) / 2;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
}
|
|
|
|
bool FVideoEncoderInputImpl::SetupForD3D11(void* InApplicationD3DDevice, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
TRefCountPtr<IDXGIDevice> DXGIDevice;
|
|
TRefCountPtr<IDXGIAdapter> Adapter;
|
|
|
|
HRESULT Result = static_cast<ID3D11Device*>(InApplicationD3DDevice)->QueryInterface(__uuidof(IDXGIDevice), (void**)DXGIDevice.GetInitReference());
|
|
if (Result != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::QueryInterface() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
else if ((Result = DXGIDevice->GetAdapter(Adapter.GetInitReference())) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("DXGIDevice::GetAdapter() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
|
|
uint32 DeviceFlags = 0;
|
|
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_0;
|
|
D3D_FEATURE_LEVEL ActualFeatureLevel;
|
|
|
|
if ((Result = D3D11CreateDevice(
|
|
Adapter,
|
|
D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL,
|
|
DeviceFlags,
|
|
&FeatureLevel,
|
|
1,
|
|
D3D11_SDK_VERSION,
|
|
FrameInfoD3D.EncoderDeviceD3D11.GetInitReference(),
|
|
&ActualFeatureLevel,
|
|
FrameInfoD3D.EncoderDeviceContextD3D11.GetInitReference())) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("D3D11CreateDevice() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
DEBUG_SET_D3D11_OBJECT_NAME(FrameInfoD3D.EncoderDeviceD3D11, "FVideoEncoderInputImpl");
|
|
DEBUG_SET_D3D11_OBJECT_NAME(FrameInfoD3D.EncoderDeviceContextD3D11, "FVideoEncoderInputImpl");
|
|
|
|
FrameFormat = EVideoFrameFormat::D3D11_R8G8B8A8_UNORM;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool FVideoEncoderInputImpl::SetupForD3D11Shared(void* InApplicationD3DDevice, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
|
|
TRefCountPtr<IDXGIDevice> DXGIDevice;
|
|
TRefCountPtr<IDXGIAdapter> Adapter;
|
|
|
|
HRESULT Result = static_cast<ID3D11Device*>(InApplicationD3DDevice)->QueryInterface(__uuidof(IDXGIDevice), (void**)DXGIDevice.GetInitReference());
|
|
if (Result != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::QueryInterface() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
else if ((Result = DXGIDevice->GetAdapter(Adapter.GetInitReference())) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("DXGIDevice::GetAdapter() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
|
|
uint32 DeviceFlags = 0;
|
|
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_1;
|
|
D3D_FEATURE_LEVEL ActualFeatureLevel;
|
|
|
|
if ((Result = D3D11CreateDevice(
|
|
Adapter,
|
|
D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL,
|
|
DeviceFlags,
|
|
&FeatureLevel,
|
|
1,
|
|
D3D11_SDK_VERSION,
|
|
FrameInfoD3D.EncoderDeviceD3D11.GetInitReference(),
|
|
&ActualFeatureLevel,
|
|
FrameInfoD3D.EncoderDeviceContextD3D11.GetInitReference())) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("D3D11CreateDevice() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
DEBUG_SET_D3D11_OBJECT_NAME(FrameInfoD3D.EncoderDeviceD3D11, "FVideoEncoderInputImpl");
|
|
DEBUG_SET_D3D11_OBJECT_NAME(FrameInfoD3D.EncoderDeviceContextD3D11, "FVideoEncoderInputImpl");
|
|
|
|
FrameFormat = EVideoFrameFormat::D3D11_R8G8B8A8_UNORM;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool FVideoEncoderInputImpl::SetupForD3D12(void* InApplicationD3DDevice, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
|
|
LUID AdapterLuid = static_cast<ID3D12Device*>(InApplicationD3DDevice)->GetAdapterLuid();
|
|
TRefCountPtr<IDXGIFactory4> DXGIFactory;
|
|
HRESULT Result;
|
|
if ((Result = CreateDXGIFactory(IID_PPV_ARGS(DXGIFactory.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("CreateDXGIFactory() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
// get the adapter game uses to render
|
|
TRefCountPtr<IDXGIAdapter> Adapter;
|
|
if ((Result = DXGIFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(Adapter.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("DXGIFactory::EnumAdapterByLuid() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
|
|
uint32 DeviceFlags = 0;
|
|
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_12_0; // TODO get this from the adaptor support
|
|
if ((Result = D3D12CreateDevice(Adapter, FeatureLevel, IID_PPV_ARGS(FrameInfoD3D.EncoderDeviceD3D12.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("D3D11CreateDevice() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
|
|
FrameFormat = EVideoFrameFormat::D3D12_R8G8B8A8_UNORM;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FVideoEncoderInputImpl::SetupForD3D12Shared(void* InApplicationD3DDevice, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
|
|
LUID AdapterLuid = static_cast<ID3D12Device*>(InApplicationD3DDevice)->GetAdapterLuid();
|
|
TRefCountPtr<IDXGIFactory4> DXGIFactory;
|
|
HRESULT Result;
|
|
if ((Result = CreateDXGIFactory(IID_PPV_ARGS(DXGIFactory.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("CreateDXGIFactory() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
// get the adapter game uses to render
|
|
TRefCountPtr<IDXGIAdapter> Adapter;
|
|
if ((Result = DXGIFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(Adapter.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("DXGIFactory::EnumAdapterByLuid() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
|
|
uint32 DeviceFlags = 0;
|
|
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_1;
|
|
D3D_FEATURE_LEVEL ActualFeatureLevel;
|
|
if ((Result = D3D11CreateDevice(
|
|
Adapter,
|
|
D3D_DRIVER_TYPE_UNKNOWN,
|
|
NULL,
|
|
DeviceFlags,
|
|
&FeatureLevel,
|
|
1,
|
|
D3D11_SDK_VERSION,
|
|
FrameInfoD3D.EncoderDeviceD3D11.GetInitReference(),
|
|
&ActualFeatureLevel,
|
|
FrameInfoD3D.EncoderDeviceContextD3D11.GetInitReference())) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("D3D11CreateDevice() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
return false;
|
|
}
|
|
|
|
if (ActualFeatureLevel != D3D_FEATURE_LEVEL_11_1)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("D3D11CreateDevice() - failed to create device w/ feature level 11.1 - needed to encode textures from D3D12."));
|
|
FrameInfoD3D.EncoderDeviceD3D11.SafeRelease();
|
|
FrameInfoD3D.EncoderDeviceContextD3D11.SafeRelease();
|
|
return false;
|
|
}
|
|
|
|
DEBUG_SET_D3D11_OBJECT_NAME(FrameInfoD3D.EncoderDeviceD3D11, "FVideoEncoderInputImpl");
|
|
DEBUG_SET_D3D11_OBJECT_NAME(FrameInfoD3D.EncoderDeviceContextD3D11, "FVideoEncoderInputImpl");
|
|
|
|
FrameFormat = EVideoFrameFormat::D3D11_R8G8B8A8_UNORM;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool FVideoEncoderInputImpl::SetupForCUDA(void* InApplicationContext, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
FrameInfoCUDA.EncoderContextCUDA = static_cast<CUcontext>(InApplicationContext);
|
|
|
|
FrameFormat = EVideoFrameFormat::CUDA_R8G8B8A8_UNORM;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
}
|
|
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
bool FVideoEncoderInputImpl::SetupForVulkan(VkInstance InApplicationVulkanInstance, VkPhysicalDevice InApplicationVulkanPhysicalDevice, VkDevice InApplicationVulkanDevice, uint32 InWidth, uint32 InHeight)
|
|
{
|
|
FrameInfoVulkan.VulkanInstance = InApplicationVulkanInstance;
|
|
FrameInfoVulkan.VulkanPhysicalDevice = InApplicationVulkanPhysicalDevice;
|
|
FrameInfoVulkan.VulkanDevice = InApplicationVulkanDevice;
|
|
|
|
FrameFormat = EVideoFrameFormat::VULKAN_R8G8B8A8_UNORM;
|
|
this->Width = InWidth;
|
|
this->Height = InHeight;
|
|
|
|
CollectAvailableEncoders();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// --- available encoders -------------------------------------------------------------------------
|
|
|
|
void FVideoEncoderInputImpl::CollectAvailableEncoders()
|
|
{
|
|
AvailableEncoders.Empty();
|
|
for (const FVideoEncoderInfo& Info : FVideoEncoderFactory::Get().GetAvailable())
|
|
{
|
|
if (Info.SupportedInputFormats.Contains(FrameFormat))
|
|
{
|
|
AvailableEncoders.Push(Info);
|
|
}
|
|
}
|
|
}
|
|
|
|
const TArray<FVideoEncoderInfo>& FVideoEncoderInputImpl::GetAvailableEncoders()
|
|
{
|
|
return AvailableEncoders;
|
|
}
|
|
|
|
// --- encoder input frames -----------------------------------------------------------------------
|
|
|
|
bool FVideoEncoderInputImpl::IsUserManagedFrame(const FVideoEncoderInputFrame* InBuffer) const
|
|
{
|
|
const FVideoEncoderInputFrameImpl* Frame = static_cast<const FVideoEncoderInputFrameImpl*>(InBuffer);
|
|
FScopeLock Guard(&ProtectFrames);
|
|
for (int32 Index = UserManagedFrames.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
if (UserManagedFrames[Index].Key == Frame)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// create a user managed buffer
|
|
FVideoEncoderInputFrame* FVideoEncoderInputImpl::CreateBuffer(OnFrameReleasedCallback InOnFrameReleased)
|
|
{
|
|
FVideoEncoderInputFrameImpl* Frame = CreateFrame();
|
|
if (Frame)
|
|
{
|
|
FScopeLock Guard(&ProtectFrames);
|
|
UserManagedFrames.Emplace(Frame, MoveTemp(InOnFrameReleased));
|
|
}
|
|
return Frame;
|
|
}
|
|
|
|
// destroy user managed buffer
|
|
void FVideoEncoderInputImpl::DestroyBuffer(FVideoEncoderInputFrame* InBuffer)
|
|
{
|
|
FVideoEncoderInputFrameImpl* Frame = static_cast<FVideoEncoderInputFrameImpl*>(InBuffer);
|
|
FScopeLock Guard(&ProtectFrames);
|
|
bool bAnythingRemoved = false;
|
|
for (int32 Index = UserManagedFrames.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
if (UserManagedFrames[Index].Key == Frame)
|
|
{
|
|
UserManagedFrames.RemoveAt(Index);
|
|
bAnythingRemoved = true;
|
|
}
|
|
}
|
|
if (bAnythingRemoved)
|
|
{
|
|
delete Frame;
|
|
}
|
|
}
|
|
|
|
// --- encoder input frames -----------------------------------------------------------------------
|
|
|
|
FVideoEncoderInputFrame* FVideoEncoderInputImpl::ObtainInputFrame()
|
|
{
|
|
FVideoEncoderInputFrameImpl* Frame = nullptr;
|
|
FScopeLock Guard(&ProtectFrames);
|
|
|
|
if (!AvailableFrames.IsEmpty())
|
|
{
|
|
|
|
AvailableFrames.Dequeue(Frame);
|
|
|
|
}
|
|
else
|
|
{
|
|
Frame = CreateFrame();
|
|
UE_LOG(LogVideoEncoder, Verbose, TEXT("Created new frame total frames: %d"), NumBuffers);
|
|
}
|
|
|
|
ActiveFrames.Push(Frame);
|
|
|
|
Frame->SetFrameID(NextFrameID++);
|
|
|
|
if (NextFrameID == 0)
|
|
{
|
|
++NextFrameID; // skip 0 id
|
|
}
|
|
|
|
return const_cast<FVideoEncoderInputFrame*>(Frame->Obtain());
|
|
}
|
|
|
|
FVideoEncoderInputFrameImpl* FVideoEncoderInputImpl::CreateFrame()
|
|
{
|
|
FVideoEncoderInputFrameImpl* Frame = new FVideoEncoderInputFrameImpl(this);
|
|
NumBuffers++;
|
|
switch (FrameFormat)
|
|
{
|
|
case EVideoFrameFormat::Undefined:
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("Got undefined frame format!"));
|
|
break;
|
|
case EVideoFrameFormat::YUV420P:
|
|
SetupFrameYUV420P(Frame);
|
|
break;
|
|
case EVideoFrameFormat::D3D11_R8G8B8A8_UNORM:
|
|
SetupFrameD3D11(Frame);
|
|
break;
|
|
case EVideoFrameFormat::D3D12_R8G8B8A8_UNORM:
|
|
SetupFrameD3D12(Frame);
|
|
break;
|
|
case EVideoFrameFormat::VULKAN_R8G8B8A8_UNORM:
|
|
SetupFrameVulkan(Frame);
|
|
break;
|
|
case EVideoFrameFormat::CUDA_R8G8B8A8_UNORM:
|
|
SetupFrameCUDA(Frame);
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
return Frame;
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::ReleaseInputFrame(FVideoEncoderInputFrame* InFrame)
|
|
{
|
|
FVideoEncoderInputFrameImpl* InFrameImpl = static_cast<FVideoEncoderInputFrameImpl*>(InFrame);
|
|
|
|
FScopeLock Guard(&ProtectFrames);
|
|
// check user managed buffers first
|
|
for (const UserManagedFrame& Frame : UserManagedFrames)
|
|
{
|
|
if (Frame.Key == InFrameImpl)
|
|
{
|
|
Frame.Value(InFrameImpl);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int32 NumRemoved = ActiveFrames.Remove(InFrameImpl);
|
|
check(NumRemoved == 1);
|
|
if (NumRemoved > 0)
|
|
{
|
|
// drop frame if format changed
|
|
if (InFrame->GetFormat() != FrameFormat)
|
|
{
|
|
ProtectFrames.Unlock();
|
|
delete InFrameImpl;
|
|
NumBuffers--;
|
|
UE_LOG(LogVideoEncoder, Verbose, TEXT("Deleted buffer (format mismatch) total remaining: %d"), NumBuffers);
|
|
return;
|
|
}
|
|
|
|
// drop frame if resolution changed
|
|
if (bIsResizable && (InFrame->GetWidth() != this->Width || InFrame->GetHeight() != this->Height))
|
|
{
|
|
ProtectFrames.Unlock();
|
|
delete InFrameImpl;
|
|
NumBuffers--;
|
|
UE_LOG(LogVideoEncoder, Verbose, TEXT("Deleted buffer (size mismatch) total remaining: %d"), NumBuffers);
|
|
return;
|
|
}
|
|
|
|
if (!AvailableFrames.IsEmpty() && NumBuffers > MaxNumBuffers)
|
|
{
|
|
ProtectFrames.Unlock();
|
|
delete InFrameImpl;
|
|
NumBuffers--;
|
|
UE_LOG(LogVideoEncoder, Verbose, TEXT("Deleted buffer (too many) total frames: %d"), NumBuffers);
|
|
return;
|
|
}
|
|
|
|
AvailableFrames.Enqueue(InFrameImpl);
|
|
}
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::Flush()
|
|
{
|
|
ProtectFrames.Lock();
|
|
while (!AvailableFrames.IsEmpty())
|
|
{
|
|
FVideoEncoderInputFrameImpl* Frame = nullptr;
|
|
AvailableFrames.Dequeue(Frame);
|
|
ProtectFrames.Unlock();
|
|
delete Frame;
|
|
NumBuffers--;
|
|
ProtectFrames.Lock();
|
|
}
|
|
ProtectFrames.Unlock();
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::SetupFrameYUV420P(FVideoEncoderInputFrameImpl* Frame)
|
|
{
|
|
Frame->SetFormat(EVideoFrameFormat::YUV420P);
|
|
Frame->SetWidth(this->Width);
|
|
Frame->SetHeight(this->Height);
|
|
FVideoEncoderInputFrame::FYUV420P& YUV420P = Frame->GetYUV420P();
|
|
YUV420P.StrideY = FrameInfoYUV420P.StrideY;
|
|
YUV420P.StrideU = FrameInfoYUV420P.StrideU;
|
|
YUV420P.StrideV = FrameInfoYUV420P.StrideV;
|
|
YUV420P.Data[0] = YUV420P.Data[1] = YUV420P.Data[2] = nullptr;
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::SetupFrameD3D11(FVideoEncoderInputFrameImpl* Frame)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
Frame->SetFormat(FrameFormat);
|
|
Frame->SetWidth(this->Width);
|
|
Frame->SetHeight(this->Height);
|
|
|
|
FVideoEncoderInputFrame::FD3D11& Data = Frame->GetD3D11();
|
|
Data.EncoderDevice = FrameInfoD3D.EncoderDeviceD3D11;
|
|
#endif
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::SetupFrameD3D12(FVideoEncoderInputFrameImpl* Frame)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
Frame->SetFormat(FrameFormat);
|
|
Frame->SetWidth(this->Width);
|
|
Frame->SetHeight(this->Height);
|
|
|
|
if (FrameFormat == EVideoFrameFormat::D3D11_R8G8B8A8_UNORM)
|
|
{
|
|
FVideoEncoderInputFrame::FD3D11& Data = Frame->GetD3D11();
|
|
Data.EncoderDevice = FrameInfoD3D.EncoderDeviceD3D11;
|
|
}
|
|
else
|
|
{
|
|
FVideoEncoderInputFrame::FD3D12& Data = Frame->GetD3D12();
|
|
Data.EncoderDevice = FrameInfoD3D.EncoderDeviceD3D12;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::SetupFrameVulkan(FVideoEncoderInputFrameImpl* Frame)
|
|
{
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
Frame->SetFormat(FrameFormat);
|
|
Frame->SetWidth(this->Width);
|
|
Frame->SetHeight(this->Height);
|
|
|
|
FVideoEncoderInputFrame::FVulkan& Data = Frame->GetVulkan();
|
|
Data.EncoderDevice = FrameInfoVulkan.VulkanDevice;
|
|
#endif
|
|
}
|
|
|
|
void FVideoEncoderInputImpl::SetupFrameCUDA(FVideoEncoderInputFrameImpl* Frame)
|
|
{
|
|
Frame->SetFormat(FrameFormat);
|
|
Frame->SetWidth(this->Width);
|
|
Frame->SetHeight(this->Height);
|
|
FVideoEncoderInputFrame::FCUDA& Data = Frame->GetCUDA();
|
|
Data.EncoderDevice = FrameInfoCUDA.EncoderContextCUDA;
|
|
}
|
|
|
|
// ---
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TRefCountPtr<ID3D11Device> FVideoEncoderInputImpl::GetD3D11EncoderDevice() const
|
|
{
|
|
return FrameInfoD3D.EncoderDeviceD3D11;
|
|
}
|
|
|
|
TRefCountPtr<ID3D12Device> FVideoEncoderInputImpl::GetD3D12EncoderDevice() const
|
|
{
|
|
return FrameInfoD3D.EncoderDeviceD3D12;
|
|
}
|
|
#endif
|
|
|
|
CUcontext FVideoEncoderInputImpl::GetCUDAEncoderContext() const
|
|
{
|
|
return FrameInfoCUDA.EncoderContextCUDA;
|
|
}
|
|
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
void* FVideoEncoderInputImpl::GetVulkanEncoderDevice() const
|
|
{
|
|
return (void*)&FrameInfoVulkan;
|
|
}
|
|
#endif
|
|
|
|
// *** FVideoEncoderInputFrame ********************************************************************
|
|
|
|
FVideoEncoderInputFrame::FVideoEncoderInputFrame()
|
|
: FrameID(0)
|
|
, TimestampUs(0)
|
|
, TimestampRTP(0)
|
|
, NumReferences(0)
|
|
, Format(EVideoFrameFormat::Undefined)
|
|
, Width(0)
|
|
, Height(0)
|
|
, bFreeYUV420PData(false)
|
|
{
|
|
}
|
|
|
|
FVideoEncoderInputFrame::FVideoEncoderInputFrame(const FVideoEncoderInputFrame& CloneFrom)
|
|
: FrameID(CloneFrom.FrameID)
|
|
, TimestampUs(CloneFrom.TimestampUs)
|
|
, TimestampRTP(CloneFrom.TimestampRTP)
|
|
, NumReferences(0)
|
|
, Format(CloneFrom.Format)
|
|
, Width(CloneFrom.Width)
|
|
, Height(CloneFrom.Height)
|
|
, bFreeYUV420PData(false)
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
D3D11.EncoderDevice = CloneFrom.D3D11.EncoderDevice;
|
|
D3D11.Texture = CloneFrom.D3D11.Texture;
|
|
if ((D3D11.EncoderTexture = CloneFrom.D3D11.EncoderTexture) != nullptr)
|
|
{
|
|
D3D11.EncoderTexture->AddRef();
|
|
}
|
|
|
|
D3D12.EncoderDevice = CloneFrom.D3D12.EncoderDevice;
|
|
D3D12.Texture = CloneFrom.D3D12.Texture;
|
|
if ((D3D12.EncoderTexture = CloneFrom.D3D12.EncoderTexture) != nullptr)
|
|
{
|
|
D3D12.EncoderTexture->AddRef();
|
|
}
|
|
#endif
|
|
|
|
CUDA.EncoderDevice = CloneFrom.CUDA.EncoderDevice;
|
|
CUDA.EncoderTexture = CloneFrom.CUDA.EncoderTexture;
|
|
}
|
|
|
|
FVideoEncoderInputFrame::~FVideoEncoderInputFrame()
|
|
{
|
|
if (bFreeYUV420PData)
|
|
{
|
|
delete[] YUV420P.Data[0];
|
|
delete[] YUV420P.Data[1];
|
|
delete[] YUV420P.Data[2];
|
|
YUV420P.Data[0] = YUV420P.Data[1] = YUV420P.Data[2] = nullptr;
|
|
bFreeYUV420PData = false;
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
if (D3D11.EncoderTexture)
|
|
{
|
|
// check to make sure this frame holds the last reference
|
|
auto NumRef = D3D11.EncoderTexture->AddRef();
|
|
if (NumRef > 2)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Warning, TEXT("VideoEncoderFame - D3D11 input texture still holds %d references."), NumRef);
|
|
}
|
|
D3D11.EncoderTexture->Release();
|
|
D3D11.EncoderTexture->Release();
|
|
D3D11.EncoderTexture = nullptr;
|
|
}
|
|
if (D3D11.SharedHandle)
|
|
{
|
|
CloseHandle(D3D11.SharedHandle);
|
|
D3D11.SharedHandle = nullptr;
|
|
}
|
|
if (D3D11.Texture && OnReleaseD3D11Texture)
|
|
{
|
|
OnReleaseD3D11Texture(D3D11.Texture);
|
|
}
|
|
if (D3D12.EncoderTexture)
|
|
{
|
|
// check to make sure this frame holds the last reference
|
|
auto NumRef = D3D12.EncoderTexture->AddRef();
|
|
if (NumRef > 2)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Warning, TEXT("VideoEncoderFame - D3D12 input texture still holds %d references."), NumRef);
|
|
}
|
|
D3D12.EncoderTexture->Release();
|
|
D3D12.EncoderTexture->Release();
|
|
D3D12.EncoderTexture = nullptr;
|
|
}
|
|
if (D3D12.Texture && OnReleaseD3D12Texture)
|
|
{
|
|
OnReleaseD3D12Texture(D3D12.Texture);
|
|
D3D12.Texture = nullptr;
|
|
}
|
|
#endif
|
|
|
|
if (CUDA.EncoderTexture)
|
|
{
|
|
OnReleaseCUDATexture(CUDA.EncoderTexture);
|
|
CUDA.EncoderTexture = nullptr;
|
|
}
|
|
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
if (Vulkan.EncoderTexture != VK_NULL_HANDLE)
|
|
{
|
|
OnReleaseVulkanTexture(Vulkan.EncoderTexture);
|
|
}
|
|
|
|
if (Vulkan.EncoderSurface)
|
|
{
|
|
OnReleaseVulkanSurface(Vulkan.EncoderSurface);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FVideoEncoderInputFrame::AllocateYUV420P()
|
|
{
|
|
if (!bFreeYUV420PData)
|
|
{
|
|
YUV420P.StrideY = Width;
|
|
YUV420P.StrideU = (Width + 1) / 2;
|
|
YUV420P.StrideV = (Width + 1) / 2;;
|
|
YUV420P.Data[0] = new uint8[Height * YUV420P.StrideY];
|
|
YUV420P.Data[1] = new uint8[(Height + 1) / 2 * YUV420P.StrideU];
|
|
YUV420P.Data[2] = new uint8[(Height + 1) / 2 * YUV420P.StrideV];
|
|
bFreeYUV420PData = true;
|
|
}
|
|
}
|
|
|
|
void FVideoEncoderInputFrame::SetYUV420P(const uint8* InDataY, const uint8* InDataU, const uint8* InDataV, uint32 InStrideY, uint32 InStrideU, uint32 InStrideV)
|
|
{
|
|
if (Format == EVideoFrameFormat::YUV420P)
|
|
{
|
|
if (bFreeYUV420PData)
|
|
{
|
|
delete[] YUV420P.Data[0];
|
|
delete[] YUV420P.Data[1];
|
|
delete[] YUV420P.Data[2];
|
|
bFreeYUV420PData = false;
|
|
}
|
|
YUV420P.Data[0] = InDataY;
|
|
YUV420P.Data[1] = InDataU;
|
|
YUV420P.Data[2] = InDataV;
|
|
YUV420P.StrideY = InStrideY;
|
|
YUV420P.StrideU = InStrideU;
|
|
YUV420P.StrideV = InStrideV;
|
|
}
|
|
}
|
|
|
|
static FThreadSafeCounter _VideoEncoderInputFrameCnt{ 0 };
|
|
|
|
#if PLATFORM_WINDOWS
|
|
void FVideoEncoderInputFrame::SetTexture(ID3D11Texture2D* InTexture, FReleaseD3D11TextureCallback InOnReleaseTexture)
|
|
{
|
|
if (Format == EVideoFrameFormat::D3D11_R8G8B8A8_UNORM)
|
|
{
|
|
check(D3D11.Texture == nullptr);
|
|
if (!D3D11.Texture)
|
|
{
|
|
TRefCountPtr<IDXGIResource> DXGIResource;
|
|
HANDLE SharedHandle;
|
|
HRESULT Result = InTexture->QueryInterface(IID_PPV_ARGS(DXGIResource.GetInitReference()));
|
|
if (FAILED(Result))
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::QueryInterface() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
//
|
|
// NOTE : The HANDLE IDXGIResource::GetSharedHandle gives us is NOT an NT Handle, and therefre we should not call CloseHandle on it
|
|
//
|
|
else if ((Result = DXGIResource->GetSharedHandle(&SharedHandle)) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("IDXGIResource::GetSharedHandle() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
else if (SharedHandle == nullptr)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("IDXGIResource::GetSharedHandle() failed to return a shared texture resource no created as shared? (D3D11_RESOURCE_MISC_SHARED)."));
|
|
}
|
|
else if ((Result = D3D11.EncoderDevice->OpenSharedResource(SharedHandle, __uuidof(ID3D11Texture2D), (LPVOID*)&D3D11.EncoderTexture)) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::OpenSharedResource() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_SET_D3D11_OBJECT_NAME(D3D11.EncoderTexture, "FVideoEncoderInputFrame::SetTexture()");
|
|
D3D11.Texture = InTexture;
|
|
OnReleaseD3D11Texture = InOnReleaseTexture;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FVideoEncoderInputFrame::SetTexture(ID3D12Resource* InTexture, FReleaseD3D12TextureCallback InOnReleaseTexture)
|
|
{
|
|
if (Format == EVideoFrameFormat::D3D11_R8G8B8A8_UNORM)
|
|
{
|
|
check(D3D12.Texture == nullptr)
|
|
check(D3D11.EncoderTexture == nullptr)
|
|
|
|
TRefCountPtr<ID3D12Device> OwnerDevice;
|
|
HRESULT Result;
|
|
if ((Result = InTexture->GetDevice(IID_PPV_ARGS(OwnerDevice.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::QueryInterface() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
//
|
|
// NOTE: ID3D12Device::CreateSharedHandle gives as an NT Handle, and so we need to call CloseHandle on it
|
|
//
|
|
else if ((Result = OwnerDevice->CreateSharedHandle(InTexture, NULL, GENERIC_ALL, *FString::Printf(TEXT("%s_FVideoEncoderInputFrame_%d"), *GetGUID(), _VideoEncoderInputFrameCnt.Increment()), &D3D11.SharedHandle)) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D12Device::CreateSharedHandle() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
else if (D3D11.SharedHandle == nullptr)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D12Device::CreateSharedHandle() failed to return a shared texture resource no created as shared? (D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)."));
|
|
}
|
|
else
|
|
{
|
|
TRefCountPtr <ID3D11Device1> Device1;
|
|
if ((Result = D3D11.EncoderDevice->QueryInterface(IID_PPV_ARGS(Device1.GetInitReference()))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::QueryInterface() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
else if ((Result = Device1->OpenSharedResource1(D3D11.SharedHandle, IID_PPV_ARGS(&D3D11.EncoderTexture))) != S_OK)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Error, TEXT("ID3D11Device::OpenSharedResource1() failed 0x%X - %s."), Result, *GetComErrorDescription(Result));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_SET_D3D11_OBJECT_NAME(D3D11.EncoderTexture, "FVideoEncoderInputFrame::SetTexture()");
|
|
D3D12.Texture = InTexture;
|
|
OnReleaseD3D12Texture = InOnReleaseTexture;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check(D3D12.Texture == nullptr);
|
|
check(D3D12.EncoderTexture == nullptr);
|
|
D3D12.Texture = InTexture;
|
|
D3D12.EncoderTexture = InTexture;
|
|
OnReleaseD3D12Texture = InOnReleaseTexture;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void FVideoEncoderInputFrame::SetTexture(CUarray InTexture, FReleaseCUDATextureCallback InOnReleaseTexture)
|
|
{
|
|
if (Format == EVideoFrameFormat::CUDA_R8G8B8A8_UNORM)
|
|
{
|
|
CUDA.EncoderTexture = InTexture;
|
|
OnReleaseCUDATexture = InOnReleaseTexture;
|
|
if (!CUDA.EncoderTexture)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Warning, TEXT("SetTexture | Cuda device pointer is null"));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if PLATFORM_DESKTOP && !PLATFORM_APPLE
|
|
void FVideoEncoderInputFrame::SetTexture(VkImage InTexture, FReleaseVulkanTextureCallback InOnReleaseTexture)
|
|
{
|
|
if (Format == EVideoFrameFormat::VULKAN_R8G8B8A8_UNORM)
|
|
{
|
|
Vulkan.EncoderTexture = InTexture;
|
|
OnReleaseVulkanTexture = InOnReleaseTexture;
|
|
if (!Vulkan.EncoderTexture)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Warning, TEXT("SetTexture | Vulkan VkImage is null"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FVideoEncoderInputFrame::SetTexture(VkImage InTexture, VkDeviceMemory InTextureDeviceMemory, uint64 InTextureMemorySize, FReleaseVulkanTextureCallback InOnReleaseTexture)
|
|
{
|
|
if (Format == EVideoFrameFormat::VULKAN_R8G8B8A8_UNORM)
|
|
{
|
|
Vulkan.EncoderTexture = InTexture;
|
|
Vulkan.EncoderDeviceMemory = InTextureDeviceMemory;
|
|
Vulkan.EncoderMemorySize = InTextureMemorySize;
|
|
OnReleaseVulkanTexture = InOnReleaseTexture;
|
|
|
|
if (!Vulkan.EncoderTexture || !InTextureDeviceMemory)
|
|
{
|
|
UE_LOG(LogVideoEncoder, Warning, TEXT("SetTexture | Vulkan VkImage or VkTextureDeviceMemory is null"));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// *** FVideoEncoderInputFrameImpl ****************************************************************
|
|
|
|
FVideoEncoderInputFrameImpl::FVideoEncoderInputFrameImpl(FVideoEncoderInputImpl* InInput)
|
|
: Input(InInput)
|
|
{
|
|
}
|
|
|
|
FVideoEncoderInputFrameImpl::FVideoEncoderInputFrameImpl(const FVideoEncoderInputFrameImpl& InCloneFrom, FCloneDestroyedCallback InCloneDestroyedCallback)
|
|
: FVideoEncoderInputFrame(InCloneFrom)
|
|
, Input(InCloneFrom.Input)
|
|
, ClonedReference(InCloneFrom.Obtain())
|
|
, OnCloneDestroyed(MoveTemp(InCloneDestroyedCallback))
|
|
{
|
|
}
|
|
|
|
FVideoEncoderInputFrameImpl::~FVideoEncoderInputFrameImpl()
|
|
{
|
|
if (ClonedReference)
|
|
{
|
|
ClonedReference->Release();
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
|
|
void FVideoEncoderInputFrameImpl::Release() const
|
|
{
|
|
// User managed frames get released without checking reference count, as the reference count doesn't matter for them
|
|
if (Input->IsUserManagedFrame(this))
|
|
{
|
|
Input->ReleaseInputFrame(const_cast<FVideoEncoderInputFrameImpl*>(this));
|
|
}
|
|
else if (NumReferences.Decrement() == 0)
|
|
{
|
|
if (ClonedReference)
|
|
{
|
|
OnCloneDestroyed(this);
|
|
delete this;
|
|
}
|
|
else
|
|
{
|
|
Input->ReleaseInputFrame(const_cast<FVideoEncoderInputFrameImpl*>(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clone frame - this will create a copy that references the original until destroyed
|
|
const FVideoEncoderInputFrame* FVideoEncoderInputFrameImpl::Clone(FCloneDestroyedCallback InCloneDestroyedCallback) const
|
|
{
|
|
FVideoEncoderInputFrameImpl* ClonedFrame = new FVideoEncoderInputFrameImpl(*this, MoveTemp(InCloneDestroyedCallback));
|
|
return ClonedFrame;
|
|
}
|
|
|
|
|
|
|
|
} /* namespace AVEncoder */
|