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

318 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AjaMediaOutput.h"
#include "AjaDeviceProvider.h"
#include "AJALib.h"
#include "AjaMediaCapture.h"
#include "AjaMediaSettings.h"
#include "IAjaMediaModule.h"
#include "IMediaIOCoreModule.h"
#include "Modules/ModuleManager.h"
#include "UObject/EnterpriseObjectVersion.h"
#define LOCTEXT_NAMESPACE "AjaMediaOutput"
/* UAjaMediaOutput
*****************************************************************************/
UAjaMediaOutput::UAjaMediaOutput(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bOutputWithAutoCirculating(false)
, TimecodeFormat(EMediaIOTimecodeFormat::LTC)
, PixelFormat(EAjaMediaOutputPixelFormat::PF_8BIT_YUV)
, bOutputIn3GLevelB(false)
, bInvertKeyOutput(false)
, bOutputAudio(false)
, NumberOfAJABuffers(2)
, bInterlacedFieldsTimecodeNeedToMatch(false)
, bWaitForSyncEvent(false)
, bLogDropFrame(true)
, bEncodeTimecodeInTexel(false)
{
}
bool UAjaMediaOutput::Validate(FString& OutFailureReason) const
{
if (!Super::Validate(OutFailureReason))
{
return false;
}
if (!OutputConfiguration.IsValid())
{
OutFailureReason = FString::Printf(TEXT("The Configuration of '%s' is invalid ."), *GetName());
return false;
}
IAjaMediaModule& MediaModule = FModuleManager::LoadModuleChecked<IAjaMediaModule>(TEXT("AjaMedia"));
if (!MediaModule.IsInitialized())
{
OutFailureReason = FString::Printf(TEXT("Can't validate MediaOutput '%s'. The Aja library was not initialized."), *GetName());
return false;
}
if (!MediaModule.CanBeUsed())
{
OutFailureReason = FString::Printf(TEXT("Can't validate MediaOutput '%s' because Aja card cannot be used. Are you in a Commandlet? You may override this behavior by launching with -ForceAjaUsage"), *GetName());
return false;
}
AJA::AJADeviceScanner Scanner;
AJA::AJADeviceScanner::DeviceInfo DeviceInfo;
if (!Scanner.GetDeviceInfo(OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceIdentifier, DeviceInfo))
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that doesn't exist on this machine."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
if (!DeviceInfo.bIsSupported)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that is not supported by the AJA SDK."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
const bool bDeviceHasOutput = DeviceInfo.NumSdiOutput > 0; // || DeviceInfo.NumHdmiOutput > 0 we do not support HDMI output, you should use a normal graphic card.
if (!bDeviceHasOutput)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that can't do playback."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
if (!DeviceInfo.bCanFrameStore1DoPlayback)
{
if (OutputConfiguration.MediaConfiguration.MediaConnection.PortIdentifier == 1)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that can't do playback on port 1."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
if (OutputConfiguration.OutputType == EMediaIOOutputType::FillAndKey && OutputConfiguration.MediaConfiguration.MediaConnection.PortIdentifier == 1)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that can't do playback on port 1."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
}
if (OutputConfiguration.OutputType == EMediaIOOutputType::FillAndKey)
{
// Even if YUV is selected we will later revert to RGBA to allow for Key, make sure we support it.
if (PixelFormat == EAjaMediaOutputPixelFormat::PF_8BIT_YUV && !DeviceInfo.bSupportPixelFormat8bitARGB)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that doesn't support the 8bit ARGB pixel format."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
if (PixelFormat == EAjaMediaOutputPixelFormat::PF_10BIT_YUV && !DeviceInfo.bSupportPixelFormat10bitRGB)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that doesn't support the 10bit RGB pixel format."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
}
else
{
if (PixelFormat == EAjaMediaOutputPixelFormat::PF_8BIT_YUV && !DeviceInfo.bSupportPixelFormat8bitYCBCR)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that doesn't support the 8bit YUV pixel format."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
if (PixelFormat == EAjaMediaOutputPixelFormat::PF_10BIT_YUV && !DeviceInfo.bSupportPixelFormat10bitYCBCR)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that doesn't support the 10bit YUV pixel format."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
}
if (bOutputIn3GLevelB)
{
if (!DeviceInfo.bCanDo3GLevelConversion)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' use the device '%s' that doesn't support the 3G level conversion."), *GetName(), *OutputConfiguration.MediaConfiguration.MediaConnection.Device.DeviceName.ToString());
return false;
}
AJA::AJAVideoFormats::VideoFormatDescriptor Descriptor = AJA::AJAVideoFormats::GetVideoFormat(OutputConfiguration.MediaConfiguration.MediaMode.DeviceModeIdentifier);
if (!Descriptor.bIsVideoFormatA)
{
OutFailureReason = FString::Printf(TEXT("The MediaOutput '%s' wants level A to level B conversion but it's not supported by the format."), *GetName());
return false;
}
}
return true;
}
FFrameRate UAjaMediaOutput::GetRequestedFrameRate() const
{
return OutputConfiguration.MediaConfiguration.MediaMode.FrameRate;
}
FIntPoint UAjaMediaOutput::GetRequestedSize() const
{
return OutputConfiguration.MediaConfiguration.MediaMode.Resolution;
}
EPixelFormat UAjaMediaOutput::GetRequestedPixelFormat() const
{
EPixelFormat Result = EPixelFormat::PF_A2B10G10R10;
switch (PixelFormat)
{
case EAjaMediaOutputPixelFormat::PF_8BIT_YUV:
Result = EPixelFormat::PF_B8G8R8A8;
break;
case EAjaMediaOutputPixelFormat::PF_10BIT_YUV:
Result = EPixelFormat::PF_A2B10G10R10;
break;
}
return Result;
}
EMediaCaptureConversionOperation UAjaMediaOutput::GetConversionOperation(EMediaCaptureSourceType InSourceType) const
{
EMediaCaptureConversionOperation Result = EMediaCaptureConversionOperation::NONE;
switch (PixelFormat)
{
case EAjaMediaOutputPixelFormat::PF_8BIT_YUV:
if (OutputConfiguration.OutputType == EMediaIOOutputType::Fill)
{
Result = EMediaCaptureConversionOperation::RGBA8_TO_YUV_8BIT;
}
else if (OutputConfiguration.OutputType == EMediaIOOutputType::FillAndKey && bInvertKeyOutput)
{
Result = EMediaCaptureConversionOperation::INVERT_ALPHA;
}
else
{
Result = EMediaCaptureConversionOperation::NONE;
}
break;
case EAjaMediaOutputPixelFormat::PF_10BIT_YUV:
if (OutputConfiguration.OutputType == EMediaIOOutputType::Fill)
{
Result = EMediaCaptureConversionOperation::RGB10_TO_YUVv210_10BIT;
}
else if (OutputConfiguration.OutputType == EMediaIOOutputType::FillAndKey && bInvertKeyOutput)
{
Result = EMediaCaptureConversionOperation::INVERT_ALPHA;
}
else
{
Result = EMediaCaptureConversionOperation::NONE;
}
break;
}
return Result;
}
#if WITH_EDITOR
FString UAjaMediaOutput::GetDescriptionString() const
{
IMediaIOCoreDeviceProvider* DeviceProviderPtr = IMediaIOCoreModule::Get().GetDeviceProvider(FAjaDeviceProvider::GetProviderName());
if (DeviceProviderPtr)
{
return DeviceProviderPtr->ToText(OutputConfiguration).ToString();
}
return Super::GetDescriptionString();
}
#endif // WITH_EDITOR
UMediaCapture* UAjaMediaOutput::CreateMediaCaptureImpl()
{
UMediaCapture* Result = NewObject<UAjaMediaCapture>();
if (Result)
{
Result->SetMediaOutput(this);
}
return Result;
}
#if WITH_EDITOR
bool UAjaMediaOutput::CanEditChange(const FProperty* InProperty) const
{
if (!Super::CanEditChange(InProperty))
{
return false;
}
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UAjaMediaOutput, bEncodeTimecodeInTexel))
{
return TimecodeFormat != EMediaIOTimecodeFormat::None;
}
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UAjaMediaOutput, bOutputIn3GLevelB))
{
bool bValid = false;
if (OutputConfiguration.IsValid())
{
AJA::AJAVideoFormats::VideoFormatDescriptor Descriptor = AJA::AJAVideoFormats::GetVideoFormat(OutputConfiguration.MediaConfiguration.MediaMode.DeviceModeIdentifier);
bValid = Descriptor.bIsVideoFormatA;
}
return bValid;
}
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UAjaMediaOutput, bInvertKeyOutput))
{
return (OutputConfiguration.OutputType == EMediaIOOutputType::FillAndKey);
}
if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UAjaMediaOutput, bInterlacedFieldsTimecodeNeedToMatch))
{
bool bValid = false;
if (OutputConfiguration.IsValid() && TimecodeFormat != EMediaIOTimecodeFormat::None)
{
AJA::AJAVideoFormats::VideoFormatDescriptor Descriptor = AJA::AJAVideoFormats::GetVideoFormat(OutputConfiguration.MediaConfiguration.MediaMode.DeviceModeIdentifier);
bValid = Descriptor.bIsInterlacedStandard;
}
return bValid;
}
return true;
}
void UAjaMediaOutput::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UAjaMediaOutput, TimecodeFormat))
{
if (TimecodeFormat == EMediaIOTimecodeFormat::None)
{
bEncodeTimecodeInTexel = false;
bInterlacedFieldsTimecodeNeedToMatch = false;
}
}
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UAjaMediaOutput, OutputConfiguration))
{
if (bOutputIn3GLevelB)
{
bOutputIn3GLevelB = false;
if (OutputConfiguration.IsValid())
{
AJA::AJAVideoFormats::VideoFormatDescriptor Descriptor = AJA::AJAVideoFormats::GetVideoFormat(OutputConfiguration.MediaConfiguration.MediaMode.DeviceModeIdentifier);
bOutputIn3GLevelB = Descriptor.bIsVideoFormatA;
}
}
if (OutputConfiguration.OutputType == EMediaIOOutputType::Fill)
{
bInvertKeyOutput = false;
}
if (bInterlacedFieldsTimecodeNeedToMatch)
{
bInterlacedFieldsTimecodeNeedToMatch = false;
if (OutputConfiguration.IsValid() && TimecodeFormat != EMediaIOTimecodeFormat::None)
{
AJA::AJAVideoFormats::VideoFormatDescriptor Descriptor = AJA::AJAVideoFormats::GetVideoFormat(OutputConfiguration.MediaConfiguration.MediaMode.DeviceModeIdentifier);
bInterlacedFieldsTimecodeNeedToMatch = Descriptor.bIsInterlacedStandard;
}
}
}
Super::PostEditChangeChainProperty(PropertyChangedEvent);
}
#endif //WITH_EDITOR
#undef LOCTEXT_NAMESPACE