Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

601 lines
17 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "AjaCoreModule.h"
#include "Async/Future.h"
#include "Misc/Timecode.h"
class FRHITexture;
namespace UE::GPUTextureTransfer
{
class ITextureTransfer;
}
namespace AJA
{
/* Types provided from the interface
*****************************************************************************/
typedef void* FDeviceScanner;
typedef void* FDeviceInfo;
typedef void* FAJADevice;
typedef uint32_t FAJAVideoFormat;
/* Logging Callbacks
*****************************************************************************/
using LoggingCallbackPtr = void(*)(const TCHAR* Format, ...);
/* Pixel formats supported
*****************************************************************************/
enum struct EPixelFormat
{
PF_8BIT_YCBCR, // As Input/Output
PF_8BIT_ARGB, // As Input/Output
PF_10BIT_RGB, // As Input/Output
PF_10BIT_YCBCR, // As Input/Output
};
/* SDI transport type
*****************************************************************************/
enum class ETransportType
{
TT_SdiSingle,
TT_SdiSingle4kTSI,
TT_SdiDual,
TT_SdiQuadSQ,
TT_SdiQuadTSI,
TT_Hdmi,
TT_Hdmi4kTSI,
};
/* Timecode
*****************************************************************************/
struct AJACORE_API FTimecode
{
FTimecode();
bool operator== (const FTimecode& Other) const;
uint32_t Hours;
uint32_t Minutes;
uint32_t Seconds;
uint32_t Frames;
bool bDropFrame;
};
enum struct ETimecodeFormat
{
TCF_None,
TCF_LTC,
TCF_VITC1,
};
namespace Private
{
class AutoDetectChannel;
class DeviceScanner;
class InputChannel;
class OutputChannel;
class SyncChannel;
class TimecodeChannel;
class VideoFormatsScanner;
}
/* AJADeviceScanner definition
*****************************************************************************/
class AJACORE_API AJADeviceScanner
{
public:
const static int32_t FormatedTextSize = 64;
using FormatedTextType = TCHAR[FormatedTextSize];
struct DeviceInfo
{
bool bIsSupported;
bool bCanFrameStore1DoPlayback;
bool bCanDoDualLink;
bool bCanDo2K;
bool bCanDo4K;
bool bCanDo12GSdi;
bool bCanDo12GRouting;
bool bCanDoMultiFormat;
bool bCanDoAlpha;
bool bCanDo3GLevelConversion;
bool bCanDoCustomAnc;
bool bCanDoLtc;
bool bCanDoLtcInRefPort;
bool bCanDoTSI;
bool bSupportPixelFormat8bitYCBCR;
bool bSupportPixelFormat8bitARGB;
bool bSupportPixelFormat10bitRGB;
bool bSupportPixelFormat10bitYCBCR;
uint32_t NumberOfLtcInput;
uint32_t NumberOfLtcOutput;
int32_t NumSdiInput;
int32_t NumSdiOutput;
int32_t NumHdmiInput;
int32_t NumHdmiOutput;
};
AJADeviceScanner();
~AJADeviceScanner();
AJADeviceScanner(const AJADeviceScanner&) = delete;
AJADeviceScanner& operator=(const AJADeviceScanner&) = delete;
int32_t GetNumDevices() const;
bool GetDeviceTextId(int32_t InDeviceIndex, FormatedTextType& OutTextId) const;
bool GetDeviceInfo(int32_t InDeviceIndex, DeviceInfo& OutDeviceInfo) const;
private:
Private::DeviceScanner* Scanner;
};
/* AJAVideoFormats definition
*****************************************************************************/
struct AJACORE_API AJAVideoFormats
{
struct AJACORE_API VideoFormatDescriptor
{
VideoFormatDescriptor();
FAJAVideoFormat VideoFormatIndex;
uint32_t FrameRateNumerator;
uint32_t FrameRateDenominator;
uint32_t ResolutionWidth;
uint32_t ResolutionHeight;
bool bIsProgressiveStandard;
bool bIsInterlacedStandard;
bool bIsPsfStandard;
bool bIsVideoFormatA;
bool bIsVideoFormatB;
bool bIs372DualLink;
bool bIsSD;
bool bIsHD;
bool bIs2K;
bool bIs4K;
bool bIsValid;
};
AJAVideoFormats(int32_t InDeviceId);
~AJAVideoFormats();
AJAVideoFormats(const AJAVideoFormats&) = delete;
AJAVideoFormats& operator=(const AJAVideoFormats&) = delete;
int32_t GetNumSupportedFormat() const;
VideoFormatDescriptor GetSupportedFormat(int32_t InIndex) const;
static VideoFormatDescriptor GetVideoFormat(FAJAVideoFormat InVideoFormatIndex);
static std::string VideoFormatToString(FAJAVideoFormat InVideoFormatIndex);
static bool VideoFormatToString(FAJAVideoFormat InVideoFormatIndex, char* OutCStr, uint32_t MaxLen);
private:
Private::VideoFormatsScanner* Formats;
};
/* AJADeviceOptions definition
*****************************************************************************/
struct AJACORE_API AJADeviceOptions
{
AJADeviceOptions(uint32_t InChannelIndex)
: DeviceIndex(InChannelIndex)
, bWantMultiFormatMode(true)
{}
uint32_t DeviceIndex;
bool bWantMultiFormatMode;
};
/* AJASyncChannel definition
*****************************************************************************/
struct AJACORE_API IAJASyncChannelCallbackInterface
{
IAJASyncChannelCallbackInterface();
virtual ~IAJASyncChannelCallbackInterface();
virtual void OnInitializationCompleted(bool bSucceed) = 0;
};
struct AJACORE_API AJASyncChannelOptions
{
AJASyncChannelOptions(const TCHAR* DebugName);
IAJASyncChannelCallbackInterface* CallbackInterface;
ETransportType TransportType;
uint32_t ChannelIndex; // [1...x]
FAJAVideoFormat VideoFormatIndex;
ETimecodeFormat TimecodeFormat;
bool bOutput; // port is output
bool bWaitForFrameToBeReady; // port is input and we want to wait for the image to be sent to Unreal Engine before ticking
bool bAutoDetectFormat;
bool bStopOnCardTimeout = true; // Stop sync channel whenever we timeout while waiting for a vertical interrupt from the card (Default: true)
};
class AJACORE_API AJASyncChannel
{
public:
AJASyncChannel(AJASyncChannel&) = delete;
AJASyncChannel& operator=(AJASyncChannel&) = delete;
AJASyncChannel();
~AJASyncChannel();
public:
bool Initialize(const AJADeviceOptions& InDevice, const AJASyncChannelOptions& InOption);
void Uninitialize();
// Only available if the initialization succeeded
bool WaitForSync() const;
bool GetTimecode(FTimecode& OutTimecode) const;
bool GetSyncCount(uint32_t& OutCount) const;
bool GetVideoFormat(FAJAVideoFormat& OutVideoFormat);
private:
Private::SyncChannel* Channel;
};
/* AJATimecodeChannel definition
*****************************************************************************/
struct AJACORE_API IAJATimecodeChannelCallbackInterface
{
IAJATimecodeChannelCallbackInterface();
virtual ~IAJATimecodeChannelCallbackInterface();
virtual void OnInitializationCompleted(bool bSucceed) = 0;
};
struct AJACORE_API AJATimecodeChannelOptions
{
AJATimecodeChannelOptions(const TCHAR* DebugName);
IAJATimecodeChannelCallbackInterface* CallbackInterface;
bool bUseDedicatedPin;
//Timecode read from dedicated pin
bool bReadTimecodeFromReferenceIn;
uint32_t LTCSourceIndex; //[1...x]
uint32_t LTCFrameRateNumerator;
uint32_t LTCFrameRateDenominator;
//Timecode read from input channels
ETransportType TransportType;
uint32_t ChannelIndex; // [1...x]
FAJAVideoFormat VideoFormatIndex;
ETimecodeFormat TimecodeFormat;
bool bAutoDetectFormat;
};
class AJACORE_API AJATimecodeChannel
{
public:
AJATimecodeChannel(AJATimecodeChannel&) = delete;
AJATimecodeChannel& operator=(AJATimecodeChannel&) = delete;
AJATimecodeChannel();
~AJATimecodeChannel();
public:
bool Initialize(const AJADeviceOptions& InDevice, const AJATimecodeChannelOptions& InOption);
void Uninitialize();
// Only available if the initialization succeeded
bool GetTimecode(FTimecode& OutTimecode) const;
private:
Private::TimecodeChannel* Channel;
};
/**
* HDR Transfer function.
*/
enum class EAjaHDRMetadataEOTF : uint8
{
SDR,
HLG,
PQ,
Unspecified
};
/**
* HDR Color Gamut.
*/
enum class EAjaHDRMetadataGamut : uint8
{
Rec709,
Rec2020,
Invalid
};
/**
* HDR Luminance.
*/
enum class EAjaHDRMetadataLuminance : uint8
{
YCbCr,
ICtCp
};
/**
* Set of metadata describing a HDR video signal.
*/
struct AJACORE_API FAjaHDROptions
{
/** Transfer function to use for converting the video signal to an optical signal. */
EAjaHDRMetadataEOTF EOTF = EAjaHDRMetadataEOTF::SDR;
/** The color gamut of the video signal. */
EAjaHDRMetadataGamut Gamut = EAjaHDRMetadataGamut::Rec709;
/** Color representation format of the video signal. */
EAjaHDRMetadataLuminance Luminance = EAjaHDRMetadataLuminance::YCbCr;
};
/* AJAInputFrameData definition
*****************************************************************************/
struct AJACORE_API AJAInputFrameData
{
AJAInputFrameData();
FTimecode Timecode;
uint32_t FramesDropped; // frame dropped by the AJA
};
struct AJACORE_API AJAOutputFrameData : AJAInputFrameData
{
AJAOutputFrameData();
uint32_t FramesLost; // frame ready by the game but not sent to AJA
};
struct AJACORE_API AJAAncillaryFrameData
{
AJAAncillaryFrameData();
uint8_t* AncBuffer;
uint32_t AncBufferSize;
uint8_t* AncF2Buffer;
uint32_t AncF2BufferSize;
};
struct AJACORE_API AJAAudioFrameData
{
AJAAudioFrameData();
uint8_t* AudioBuffer;
uint32_t AudioBufferSize;
uint32_t NumChannels;
uint32_t AudioRate;
uint32_t NumSamples;
};
struct AJACORE_API AJAVideoFrameData
{
AJAVideoFrameData();
FAJAVideoFormat VideoFormatIndex;
uint8_t* VideoBuffer;
uint32_t VideoBufferSize;
uint32_t Stride;
uint32_t Width;
uint32_t Height;
EPixelFormat PixelFormat;
bool bIsProgressivePicture;
FAjaHDROptions HDROptions;
};
struct AJACORE_API AJARequestInputBufferData
{
AJARequestInputBufferData();
bool bIsProgressivePicture;
uint32_t AncBufferSize;
uint32_t AncF2BufferSize;
uint32_t AudioBufferSize;
uint32_t VideoBufferSize;
};
struct AJACORE_API AJARequestedInputBufferData
{
AJARequestedInputBufferData();
uint8_t* AncBuffer;
uint8_t* AncF2Buffer;
uint8_t* AudioBuffer;
uint8_t* VideoBuffer;
};
struct AJACORE_API IAJAInputOutputChannelCallbackInterface : IAJASyncChannelCallbackInterface
{
IAJAInputOutputChannelCallbackInterface();
virtual bool OnRequestInputBuffer(const AJARequestInputBufferData& RequestBuffer, AJARequestedInputBufferData& OutRequestedBuffer) = 0;
virtual bool OnInputFrameReceived(const AJAInputFrameData& InFrameData, const AJAAncillaryFrameData& InAncillaryFrame, const AJAAudioFrameData& InAudioFrame, const AJAVideoFrameData& InVideoFrame) = 0;
virtual void OnOutputFrameStarted() { }
virtual bool OnOutputFrameCopied(const AJAOutputFrameData& InFrameData) = 0;
virtual void OnCompletion(bool bSucceed) = 0;
virtual void OnFormatChange(FAJAVideoFormat VideoFormat) {}
};
/* AJAInputOutputChannelOptions definition
*****************************************************************************/
enum class EAJAReferenceType
{
EAJA_REFERENCETYPE_EXTERNAL,
EAJA_REFERENCETYPE_FREERUN,
EAJA_REFERENCETYPE_INPUT,
};
struct AJACORE_API AJAInputOutputChannelOptions
{
AJAInputOutputChannelOptions(const TCHAR* DebugName, uint32_t InChannelIndex);
IAJAInputOutputChannelCallbackInterface* CallbackInterface;
uint32_t NumberOfAudioChannel;
ETransportType TransportType;
uint32_t ChannelIndex; // [1...x]
uint32_t SynchronizeChannelIndex; // [1...x]
uint32_t KeyChannelIndex; // [1...x] for output
uint32_t OutputNumberOfBuffers; // [1...x] supported but not suggested (min of 2 is suggested)
FAJAVideoFormat VideoFormatIndex;
EPixelFormat PixelFormat;
FAjaHDROptions HDROptions;
ETimecodeFormat TimecodeFormat;
EAJAReferenceType OutputReferenceType;
uint32_t BurnTimecodePercentY;
union
{
struct
{
uint32_t bUseAutoCirculating : 1;
uint32_t bOutput : 1; // port is output
uint32_t bUseKey : 1; // output will also sent the key on OutputKeyPortIndex
uint32_t bOutputInterlacedFieldsTimecodeNeedToMatch : 1; // when trying to find the odd field that correspond to the even field, the 2 timecode need to match
uint32_t bOutputInterlaceAsProgressive : 1; // whether to treat outgoing interlace frames as progressive.
uint32_t bOutputInterlaceOnEvenFrames : 1; // whether to force output interlace on even timecode frames.
uint32_t bUseAncillary : 1; // enable ANC system
uint32_t bUseAudio : 1; // enable audio input/output
uint32_t bUseVideo : 1; // enable video input/output
uint32_t bBurnTimecode : 1; // burn the timecode to the input or output image
uint32_t bDisplayWarningIfDropFrames : 1;
uint32_t bConvertOutputLevelAToB : 1; // enable video output 3G level A to convert it to 3G level B
uint32_t bTEST_OutputInterlaced : 1; // when outputting, warn if the field 1 and field 2 are the same color. It except one of the line to be white and the other line to be not white
uint32_t bUseGPUDMA : 1; // Whether to use GPU Direct when outputting.
uint32_t bAutoDetectFormat : 1; // Whether to autodetect format on input.
uint32_t bDirectlyWriteAudio : 1; // Experimental mode to set audio from the audio thread directly.
uint32_t bStopOnTimeout : 1; // Stop input/output whenever we timeout while waiting for a vertical interrupt from the card.
};
uint32_t Options;
};
};
/* AJAInputChannel definition
*****************************************************************************/
class AJACORE_API AJAInputChannel
{
public:
AJAInputChannel(AJAInputChannel&) = delete;
AJAInputChannel& operator=(AJAInputChannel&) = delete;
AJAInputChannel();
virtual ~AJAInputChannel();
public:
bool Initialize(const AJADeviceOptions& InDevice, const AJAInputOutputChannelOptions& Options);
void Uninitialize();
// Only available if the initialization succeeded
uint32_t GetFrameDropCount() const;
const AJAInputOutputChannelOptions& GetOptions() const;
const AJADeviceOptions& GetDeviceOptions() const;
private:
Private::InputChannel* Channel;
};
/* AJAOutputFrameBufferData definition
*****************************************************************************/
struct AJACORE_API AJAOutputFrameBufferData
{
AJAOutputFrameBufferData();
static const uint32_t InvalidFrameIdentifier;
FTimecode Timecode;
uint32_t FrameIdentifier;
bool bEvenFrame;
};
/* AJAOutputChannel definition
*****************************************************************************/
class AJACORE_API AJAOutputChannel
{
public:
AJAOutputChannel(AJAOutputChannel&) = delete;
AJAOutputChannel& operator=(AJAOutputChannel&) = delete;
AJAOutputChannel();
virtual ~AJAOutputChannel();
public:
bool Initialize(const AJADeviceOptions& InDevice, const AJAInputOutputChannelOptions& Options);
/**
* Clean up card connections for this channel. (Asynchronous)
* @param Promise Used to inform when the uninitialization is complete.
*/
void Uninitialize(TPromise<void> Promise = {});
// Set a new buffer that will be copied to the AJA.
bool SetAncillaryFrameData(const AJAOutputFrameBufferData& InFrameData, uint8_t* AncillaryBuffer, uint32_t AncillaryBufferSize);
bool SetAudioFrameData(const AJAOutputFrameBufferData& InFrameData, uint8_t* AudioBuffer, uint32_t AudioBufferSize);
bool SetVideoFrameData(const AJAOutputFrameBufferData& InFrameData, uint8_t* VideoBuffer, uint32_t VideoBufferSize);
bool SetVideoFrameData(const AJAOutputFrameBufferData& InFrameData, FRHITexture* RHITexture);
bool DMAWriteAudio(const uint8* Buffer, int32 BufferSize);
// Return whether to capture or discard a frame based on its timecode and frame number.
bool ShouldCaptureThisFrame(::FTimecode Timecode, uint32 SourceFrameNumber);
bool GetOutputDimension(uint32_t& OutWidth, uint32_t& OutHeight) const;
int32_t GetNumAudioSamplesPerFrame(const AJAOutputFrameBufferData& InFrameData) const;
private:
Private::OutputChannel* Channel;
};
/* AJAAutoDetectChannelCallbackInterface definition
*****************************************************************************/
struct AJACORE_API IAJAAutoDetectCallbackInterface
{
IAJAAutoDetectCallbackInterface();
virtual ~IAJAAutoDetectCallbackInterface();
virtual void OnCompletion(bool bSucceed) = 0;
};
/* AJAAutoDetectChannel definition
*****************************************************************************/
class AJACORE_API AJAAutoDetectChannel
{
public:
struct AutoDetectChannelData
{
AutoDetectChannelData();
FAJAVideoFormat DetectedVideoFormat;
uint32_t DeviceIndex; // [0...x]
uint32_t ChannelIndex; // [1...x]
ETimecodeFormat TimecodeFormat;
};
public:
AJAAutoDetectChannel(AJAAutoDetectChannel&) = delete;
AJAAutoDetectChannel& operator=(AJAAutoDetectChannel&) = delete;
AJAAutoDetectChannel();
virtual ~AJAAutoDetectChannel();
public:
bool Initialize(IAJAAutoDetectCallbackInterface* InCallbackInterface);
void Uninitialize();
int32_t GetNumOfChannelData() const;
AutoDetectChannelData GetChannelData(int32_t Index) const;
private:
Private::AutoDetectChannel* AutoChannel;
};
}