266 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# 相关蓝图类
 | 
						||
BP_Live:里面可以指定MediaPlayer以及MediaTexture,并且替换蓝图子StaticMesh材质中的EmissiveMap为MediaTexture。
 | 
						||
 | 
						||
# 导播台
 | 
						||
之后就可以将视频放到指定的Saved文件夹里,就可以在导播台播放了。
 | 
						||
 | 
						||
 | 
						||
# NDI 播放逻辑
 | 
						||
通过道具来添加NDI 设置。
 | 
						||
## 道具
 | 
						||
- BP_ProjectorD0
 | 
						||
- BP_Screen011
 | 
						||
 | 
						||
## 相关注释掉的代码
 | 
						||
- TsMapEnvironmentAssets.ts
 | 
						||
- TsMapEnvironmentSingleSelectItemView.ts
 | 
						||
	- SetMediaData()
 | 
						||
- TsScreenPlayerItemView.ts
 | 
						||
	- SetData()
 | 
						||
- TsScreenPlayerSelectItemPopupView.ts
 | 
						||
	- ChangeMediaType()
 | 
						||
 | 
						||
# NDI播放模糊问题解决
 | 
						||
- bool UNDIMediaReceiver::CaptureConnectedVideo()  
 | 
						||
 | 
						||
 | 
						||
```c++
 | 
						||
bool UNDIMediaReceiver::Initialize(const FNDIConnectionInformation& InConnectionInformation, UNDIMediaReceiver::EUsage InUsage)
 | 
						||
{
 | 
						||
	if (this->p_receive_instance == nullptr)
 | 
						||
	{
 | 
						||
		if (IsValid(this->InternalVideoTexture))
 | 
						||
			this->InternalVideoTexture->UpdateResource();
 | 
						||
 | 
						||
		// create a non-connected receiver instance
 | 
						||
		NDIlib_recv_create_v3_t settings;
 | 
						||
		settings.allow_video_fields = false;
 | 
						||
		settings.bandwidth = NDIlib_recv_bandwidth_highest;
 | 
						||
		settings.color_format = NDIlib_recv_color_format_fastest;
 | 
						||
 | 
						||
		p_receive_instance = NDIlib_recv_create_v3(&settings);
 | 
						||
 | 
						||
		// check if it was successful
 | 
						||
		if (p_receive_instance != nullptr)
 | 
						||
		{
 | 
						||
			// If the incoming connection information is valid
 | 
						||
			if (InConnectionInformation.IsValid())
 | 
						||
			{
 | 
						||
				//// Alright we created a non-connected receiver. Lets actually connect
 | 
						||
				ChangeConnection(InConnectionInformation);
 | 
						||
			}
 | 
						||
 | 
						||
			if (InUsage == UNDIMediaReceiver::EUsage::Standalone)
 | 
						||
			{
 | 
						||
				this->OnNDIReceiverVideoCaptureEvent.Remove(VideoCaptureEventHandle);
 | 
						||
				VideoCaptureEventHandle = this->OnNDIReceiverVideoCaptureEvent.AddLambda([this](UNDIMediaReceiver* receiver, const NDIlib_video_frame_v2_t& video_frame)
 | 
						||
				{
 | 
						||
					FTextureRHIRef ConversionTexture = this->DisplayFrame(video_frame);
 | 
						||
					if (ConversionTexture != nullptr)
 | 
						||
					{
 | 
						||
						if ((GetVideoTextureResource() != nullptr) && (GetVideoTextureResource()->TextureRHI != ConversionTexture))
 | 
						||
						{
 | 
						||
							GetVideoTextureResource()->TextureRHI = ConversionTexture;
 | 
						||
							RHIUpdateTextureReference(this->VideoTexture->TextureReference.TextureReferenceRHI, ConversionTexture);
 | 
						||
						}
 | 
						||
						if ((GetInternalVideoTextureResource() != nullptr) && (GetInternalVideoTextureResource()->TextureRHI != ConversionTexture))
 | 
						||
						{
 | 
						||
							GetInternalVideoTextureResource()->TextureRHI = ConversionTexture;
 | 
						||
							RHIUpdateTextureReference(this->InternalVideoTexture->TextureReference.TextureReferenceRHI, ConversionTexture);
 | 
						||
						}
 | 
						||
					}
 | 
						||
				});
 | 
						||
 | 
						||
				// We don't want to limit the engine rendering speed to the sync rate of the connection hook
 | 
						||
				// into the core delegates render thread 'EndFrame'
 | 
						||
				FCoreDelegates::OnEndFrameRT.Remove(FrameEndRTHandle);
 | 
						||
				FrameEndRTHandle.Reset();
 | 
						||
				FrameEndRTHandle = FCoreDelegates::OnEndFrameRT.AddLambda([this]()
 | 
						||
				{
 | 
						||
					while(this->CaptureConnectedMetadata())
 | 
						||
						; // Potential improvement: limit how much metadata is processed, to avoid appearing to lock up due to a metadata flood
 | 
						||
					this->CaptureConnectedVideo();
 | 
						||
				});
 | 
						||
 | 
						||
#if UE_EDITOR
 | 
						||
				// We don't want to provide perceived issues with the plugin not working so
 | 
						||
				// when we get a Pre-exit message, forcefully shutdown the receiver
 | 
						||
				FCoreDelegates::OnPreExit.AddWeakLambda(this, [&]() {
 | 
						||
					this->Shutdown();
 | 
						||
					FCoreDelegates::OnPreExit.RemoveAll(this);
 | 
						||
				});
 | 
						||
 | 
						||
				// We handle this in the 'Play In Editor' versions as well.
 | 
						||
				FEditorDelegates::PrePIEEnded.AddWeakLambda(this, [&](const bool) {
 | 
						||
					this->Shutdown();
 | 
						||
					FEditorDelegates::PrePIEEnded.RemoveAll(this);
 | 
						||
				});
 | 
						||
#endif
 | 
						||
			}
 | 
						||
 | 
						||
			return true;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return false;
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
绘制函数
 | 
						||
```c++
 | 
						||
/**
 | 
						||
	Attempts to immediately update the 'VideoTexture' object with the last capture video frame
 | 
						||
	from the connected source
 | 
						||
*/
 | 
						||
FTextureRHIRef UNDIMediaReceiver::DisplayFrame(const NDIlib_video_frame_v2_t& video_frame)
 | 
						||
{
 | 
						||
	// we need a command list to work with
 | 
						||
	FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
 | 
						||
 | 
						||
	// Actually draw the video frame from cpu to gpu
 | 
						||
	switch(video_frame.frame_format_type)
 | 
						||
	{
 | 
						||
		case NDIlib_frame_format_type_progressive:
 | 
						||
			if(video_frame.FourCC == NDIlib_FourCC_video_type_UYVY)
 | 
						||
				return DrawProgressiveVideoFrame(RHICmdList, video_frame);
 | 
						||
			else if(video_frame.FourCC == NDIlib_FourCC_video_type_UYVA)
 | 
						||
				return DrawProgressiveVideoFrameAlpha(RHICmdList, video_frame);
 | 
						||
			break;
 | 
						||
		case NDIlib_frame_format_type_field_0:
 | 
						||
		case NDIlib_frame_format_type_field_1:
 | 
						||
			if(video_frame.FourCC == NDIlib_FourCC_video_type_UYVY)
 | 
						||
				return DrawInterlacedVideoFrame(RHICmdList, video_frame);
 | 
						||
			else if(video_frame.FourCC == NDIlib_FourCC_video_type_UYVA)
 | 
						||
				return DrawInterlacedVideoFrameAlpha(RHICmdList, video_frame);
 | 
						||
			break;
 | 
						||
	}
 | 
						||
 | 
						||
	return nullptr;
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
DrawProgressiveVideoFrame
 | 
						||
 | 
						||
 | 
						||
UNDIMediaReceiver::CaptureConnectedVideo
 | 
						||
=>
 | 
						||
DisplayFrame    NDIlib_frame_format_type_progressive    NDIlib_FourCC_video_type_UYVY
 | 
						||
=>
 | 
						||
DrawProgressiveVideoFrame
 | 
						||
 | 
						||
## Shader Binding RT
 | 
						||
设置RT:
 | 
						||
```c++
 | 
						||
	FTextureRHIRef TargetableTexture;
 | 
						||
 | 
						||
	// check for our frame sync object and that we are actually connected to the end point
 | 
						||
	if (p_framesync_instance != nullptr)
 | 
						||
	{
 | 
						||
		// Initialize the frame size parameter
 | 
						||
		FIntPoint FrameSize = FIntPoint(Result.xres, Result.yres);
 | 
						||
 | 
						||
		if (!RenderTarget.IsValid() || !RenderTargetDescriptor.IsValid() ||
 | 
						||
			RenderTargetDescriptor.GetSize() != FIntVector(FrameSize.X, FrameSize.Y, 0) ||
 | 
						||
			DrawMode != EDrawMode::Progressive)
 | 
						||
		{
 | 
						||
			// Create the RenderTarget descriptor
 | 
						||
			RenderTargetDescriptor = FPooledRenderTargetDesc::Create2DDesc(
 | 
						||
				FrameSize, PF_B8G8R8A8, FClearValueBinding::None, TexCreate_None, TexCreate_RenderTargetable | TexCreate_SRGB, false);
 | 
						||
 | 
						||
			// Update the shader resource for the 'SourceTexture'
 | 
						||
			// The source texture will be given UYVY data, so make it half-width
 | 
						||
#if (ENGINE_MAJOR_VERSION > 5) || ((ENGINE_MAJOR_VERSION == 5) && (ENGINE_MINOR_VERSION >= 1))
 | 
						||
			const FRHITextureCreateDesc CreateDesc = FRHITextureCreateDesc::Create2D(TEXT("NDIMediaReceiverProgressiveSourceTexture"))
 | 
						||
				.SetExtent(FrameSize.X / 2, FrameSize.Y)
 | 
						||
				.SetFormat(PF_B8G8R8A8)
 | 
						||
				.SetNumMips(1)
 | 
						||
				.SetFlags(ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::Dynamic);
 | 
						||
 | 
						||
			SourceTexture = RHICreateTexture(CreateDesc);
 | 
						||
#elif (ENGINE_MAJOR_VERSION == 4) || (ENGINE_MAJOR_VERSION == 5)
 | 
						||
			FRHIResourceCreateInfo CreateInfo(TEXT("NDIMediaReceiverProgressiveSourceTexture"));
 | 
						||
			TRefCountPtr<FRHITexture2D> DummyTexture2DRHI;
 | 
						||
			RHICreateTargetableShaderResource2D(FrameSize.X / 2, FrameSize.Y, PF_B8G8R8A8, 1, TexCreate_Dynamic,
 | 
						||
			                                    TexCreate_RenderTargetable, false, CreateInfo, SourceTexture,
 | 
						||
			                                    DummyTexture2DRHI);
 | 
						||
#else
 | 
						||
			#error "Unsupported engine major version"
 | 
						||
#endif
 | 
						||
 | 
						||
			// Find a free target-able texture from the render pool
 | 
						||
			GRenderTargetPool.FindFreeElement(RHICmdList, RenderTargetDescriptor, RenderTarget, TEXT("NDIIO"));
 | 
						||
 | 
						||
			DrawMode = EDrawMode::Progressive;
 | 
						||
		}
 | 
						||
 | 
						||
#if ENGINE_MAJOR_VERSION >= 5
 | 
						||
		TargetableTexture = RenderTarget->GetRHI();
 | 
						||
#elif ENGINE_MAJOR_VERSION == 4
 | 
						||
		TargetableTexture = RenderTarget->GetRenderTargetItem().TargetableTexture;
 | 
						||
...
 | 
						||
...
 | 
						||
		// Initialize the Render pass with the conversion texture
 | 
						||
		FRHITexture* ConversionTexture = TargetableTexture.GetReference();
 | 
						||
		FRHIRenderPassInfo RPInfo(ConversionTexture, ERenderTargetActions::DontLoad_Store);
 | 
						||
 | 
						||
		// Needs to be called *before* ApplyCachedRenderTargets, since BeginRenderPass is caching the render targets.  
 | 
						||
		RHICmdList.BeginRenderPass(RPInfo, TEXT("NDI Recv Color Conversion"));
 | 
						||
```
 | 
						||
 | 
						||
设置NDI传入的UYVY:
 | 
						||
```c++
 | 
						||
// set the texture parameter of the conversion shader
 | 
						||
FNDIIOShaderUYVYtoBGRAPS::Params Params(SourceTexture, SourceTexture, FrameSize,
 | 
						||
										FVector2D(0, 0), FVector2D(1, 1),
 | 
						||
										bPerformsRGBtoLinear ? FNDIIOShaderPS::EColorCorrection::sRGBToLinear : FNDIIOShaderPS::EColorCorrection::None,
 | 
						||
										FVector2D(0.f, 1.f));
 | 
						||
ConvertShader->SetParameters(RHICmdList, Params);
 | 
						||
 | 
						||
// Create the update region structure
 | 
						||
FUpdateTextureRegion2D Region(0, 0, 0, 0, FrameSize.X/2, FrameSize.Y);
 | 
						||
 | 
						||
// Set the Pixel data of the NDI Frame to the SourceTexture
 | 
						||
RHIUpdateTexture2D(SourceTexture, 0, Region, Result.line_stride_in_bytes, (uint8*&)Result.p_data);
 | 
						||
```
 | 
						||
 | 
						||
## 解决方案
 | 
						||
[NDI plugin质量问题](https://forums.unrealengine.com/t/ndi-plugin-quality-trouble/1970097)
 | 
						||
 | 
						||
I changed only shader “NDIIO/Shaders/Private/NDIIOShaders.usf”.
 | 
						||
For example function **void NDIIOUYVYtoBGRAPS (// Shader from 8 bits UYVY to 8 bits RGBA (alpha set to 1)):**
 | 
						||
 | 
						||
_WAS:_
 | 
						||
 | 
						||
```c++
 | 
						||
float4 UYVYB = NDIIOShaderUB.InputTarget.Sample(NDIIOShaderUB.SamplerB, InUV);
 | 
						||
float4 UYVYT = NDIIOShaderUB.InputTarget.Sample(NDIIOShaderUB.SamplerT, InUV);
 | 
						||
float PosX = 2.0f * InUV.x * NDIIOShaderUB.InputWidth;
 | 
						||
float4 YUVA;
 | 
						||
float FracX = PosX % 2.0f;
 | 
						||
YUVA.x = (1 - FracX) * UYVYT.y + FracX * UYVYT.w;
 | 
						||
YUVA.yz = UYVYB.zx;
 | 
						||
YUVA.w = 1;
 | 
						||
```
 | 
						||
 | 
						||
_I DID:_
 | 
						||
 | 
						||
```c++
 | 
						||
float4 UYVYB = NDIIOShaderUB.InputTarget.Sample(NDIIOShaderUB.SamplerB, InUV);
 | 
						||
float4 UYVYT0 = NDIIOShaderUB.InputTarget.Sample(NDIIOShaderUB.SamplerT, InUV + float2(-0.25f / NDIIOShaderUB.InputWidth, 0));
 | 
						||
float4 UYVYT1 = NDIIOShaderUB.InputTarget.Sample(NDIIOShaderUB.SamplerT, InUV + float2(0.25f / NDIIOShaderUB.InputWidth, 0));
 | 
						||
float PosX = 2.0f * InUV.x * NDIIOShaderUB.InputWidth;
 | 
						||
float4 YUVA;
 | 
						||
float FracX = (PosX % 2.0f) * 0.5f;
 | 
						||
YUVA.x = (1 - FracX) * UYVYT1.y + FracX * UYVYT0.w;
 | 
						||
YUVA.yz = UYVYB.zx;
 | 
						||
YUVA.w = 1;
 | 
						||
```
 | 
						||
 | 
						||
Small changes but result is seems much more better.
 | 
						||
Of course, I added a bit of sharpness to the material after I changed the shader, but even without that, the result looks better than in the original version.
 | 
						||
 | 
						||
滤波资料:https://zhuanlan.zhihu.com/p/633122224
 | 
						||
## UYVY(YUV422)
 | 
						||
- https://zhuanlan.zhihu.com/p/695302926
 | 
						||
- https://blog.csdn.net/gsp1004/article/details/103037312
 | 
						||
 |