在虚幻中处理MediaPlayer播放画面的时机

1

主题

2

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2023-1-17 07:10:25 | 显示全部楼层
很多时候我们需要对MediaPlayer播放的画面做处理,比如基于AI的美颜、扣像、面捕和动捕...
我们同时假设上述的这些处理需要在渲染线程中完成(这基本也是必须的,因为会涉及Texture & RHI相关操作)
MediaPlayer可以有一个对应的MediaTexture让我们能够实时的获取到播放的画面,这里会有一个关于帧率的问题:
比如一个视频的帧率是30FPS,虚幻的帧率是60FPS,总之就是当虚幻帧率高于视频帧率时。
基于视频帧率处理播放的画面

如果这时我们基于虚幻的帧率处理播放器的画面显然有一半的计算资源会被浪费,这里的优化方案是为MediaPlayer添加一个渲染Delegate
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnMediaTextureRendered, FRHICommandListImmediate&, UMediaTexture*); 然后在FMediaTextureResource::Render方法中Broadcast
void FMediaTextureResource::Render(const FRenderParams& Params)
{
    ...
    const auto MediaPlayer = Owner.GetMediaPlayer();
    if (MediaPlayer && MediaPlayer->OnTextureRendered.IsBound())
    {
        FRHICommandListImmediate& CommandList = FRHICommandListExecutor::GetImmediateCommandList();
        MediaPlayer->OnTextureRendered.Broadcast(CommandList, &Owner);
    }
}
后面我们可以在MediaPlayer->OnTextureRendered中处理播放的画面,这里要注意FMediaTextureResource::Render是在渲染线程中执行  
基于虚幻帧率处理播放的画面

如果虚幻的帧率要低于视频的帧率(这种情况理论上可以存在,实际上很少见),说白了就是需要基于虚幻的帧率处理播放的画面。这时我们可以通过向 IRendererModule的GetResolvedSceneColorCallbacks添加自己的Callback进行处理
IRendererModule* RendererModule = FModuleManager::GetModulePtr<IRendererModule>(RendererModuleName);
DelegateHandle = RendererModule->GetResolvedSceneColorCallbacks().AddLambda([MediaTexture](FRDGBuilder& GraphBuilder, const FSceneTextures& SceneTextures)
{
    DoSomething(MediaTexture, GraphBuilder.RHICmdList);
});
这种方式也存在一些问题,比如在UnrealEditor中PIE时,除了有GameViewport还可能存在其他的Viewport,这会导致在同一帧上述Callback会执行多次。在使用SceneCapture时也可能导致上述问题。要解决这个问题我们可以使用另一种方式,FSceneViewExtensionBase
class FMySceneViewExtension : public FSceneViewExtensionBase
{
public:
    //~ISceneViewExtension interface
    virtual void PostRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override;
    //~ISceneViewExtension interface
};

void FMySceneViewExtension::PostRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView)
{
    if (InView.bIsGameView && !InView.bIsSceneCapture)
    {
        DoSomething();
    }
}
在上述代码中我们可以通过InView中的一些状态进行过滤。  
当然我们也可以直接在GameThread中使用ENQUEUE_RENDER_COMMAND ^^
未经许可禁止转载
回复

举报 使用道具

您需要登录后才可以回帖 登录 | 立即注册
快速回复 返回顶部 返回列表