使用硬件在DeckLink上将YUV转换为RGB

时间:2015-01-16 15:46:45

标签: wpf video-capture hardware-acceleration writeablebitmap pixelformat

我目前正通过DeckLink 4K Extreme上的HDMI输入从摄像机以59.94 FPS摄取HD1080p视频。

我的目标是在WPF UI元素中复制传入的图像。为了实现这一点,我在C#WPF应用程序中使用DeckLink SDK。

在这个程序中,我实现了VideoInputFrameArrived回调。在这个回调中,我将每个帧中的字节复制到一个WriteableBitmap中,我将其设置为Image的源。

所有这一切都可以正常工作,当我运行程序时,Image确实会在帧到达时实时更新。

我的问题是,视频输入中唯一支持的两种Pixel格式是8BitYUV和10BitYUV,它们都不能在计算机显示器上本地显示。

WriteableBitmap只能采用各种RGB,黑白和CMYK格式。

这是我到目前为止所尝试的内容。


我尝试使用IDeckLinkVideoConversion :: ConvertFrame()

转换每个帧

问题:ConvertFrame()要求使用IDeckLinkOutput :: CreateVideoFrame()在DeckLink上呈现目标框架。正如我目前所理解的那样,DeckLink不能同时充当输入(捕获视频输入)和输出(渲染目标帧)。


我已将传入流设置为8BitYUV,并将每个帧复制到WriteableBitmap中,格式为BGR32。

问题:正如我之前提到的,这将显示图像,但颜色不正确,图片只是它需要的宽度的一半。

原因是8BitYUV的输入流是16位/像素,而Bitmap需要32位/像素,因此Bitmap将每个传入的MacroPixel(4个字节)视为一个像素而不是2个像素真的是。


目前我使用像素着色器来修复颜色,使用RenderTransform将图像水平缩放2倍以“修复”宽高比。问题是,我有一半的原始分辨率。

我不认为这是硬件限制,因为当我将另一台显示器连接到DeckLink上的HDMI输出时,输入的图像以完整的1080p显示为完美的颜色。是否有可能在内存中的某处捕获该传出流?

TL; DR 将4:2:2 YUV(UYVY)实时转换为RGB或CMYK像素格式的最佳方法是什么? (1080p @ 59.94 FPS)

优选地是硬件解决方案,即DeckLink或GPU。

2 个答案:

答案 0 :(得分:2)

这里有几个选项。

首先,您可以直接显示UYVY。大多数视频适配器将通过DirectDraw,DirectShow,DirectX版本最多9个API接受UYVY数据,您不需要对视频帧进行实时转换。将其集成到WPF应用程序中可能需要一些努力,也许最流行的方式是通过DirectShow.NET库和WPF Media Kit使用DirectShow。但是,通过这种方式,您还可以使用DeckLink的视频捕获DirectShow过滤器捕获视频。您可以更快地将所有部分连接在一起,但是您已经使用DeckLink SDK进行捕获,这样您就可以在捕获过程中获得更多控制和灵活性,因此您可能不想回到DirectShow。

第二个选项是根据需要转换为RGB。我不认为DeckLink可以为您做到这一点,并且基于GPU的转换肯定存在(转换公式众所周知,简单且易于并行化),但是依赖于硬件或者不能立即使用。相反,微软发布了Color Converter DSP,它可以以非常有效的方式进行转换(从8位,而不是10位)。该API是原生的,您可能需要Media Foundation .NET才能从您的应用中访问它。另外一个有效的软件转换也可以使用FFmpeg的libswscale(通过各自的包装器为托管应用程序)完成。

答案 1 :(得分:0)

我只是使用decklink api来完成此操作,因为我拥有的卡既可以用作输入也可以用作输出。输出 不需要 必须处于播放模式才能访问api的这一部分:

com_ptr<IDeckLinkOutput> m_deckLinkOutput;
if (SUCCEEDED(m_deckLink->QueryInterface(IID_IDeckLinkOutput, (void **)&m_deckLinkOutput)))
{
    IDeckLinkMutableVideoFrame *pRGBFrame;
    if (SUCCEEDED(m_deckLinkOutput->CreateVideoFrame(videoFrame->GetWidth(), videoFrame->GetHeight(), videoFrame->GetWidth() * 4, bmdFormat8BitBGRA, videoFrame->GetFlags(), &pRGBFrame)))
    {
        m_deckLinkVideoConversion->ConvertFrame(pFrame, pRGBFrame);

        //use the rgbFrame

        pRGBFrame->Release();
    }
}