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

Convert YUV to RGB on DeckLink using hardware

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

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

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

所有这些都按预期工作,当我 运行 程序时,图像确实会随着帧到达而实时更新。

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

WriteableBitmap只能接受各种RGB、黑白和CMYK格式。

这是我到目前为止所尝试过的方法。


我尝试使用 IDeckLinkVideoConversion::ConvertFrame()

转换每一帧

问题:ConvertFrame() 需要使用 IDeckLinkOutput::CreateVideoFrame() 在 DeckLink 上渲染目标帧。据我目前了解,DeckLink 不能同时充当输入(捕获视频源)和输出(渲染目标帧)。


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

问题: 正如我之前提到的,这将显示图像,但颜色不正确并且图像只有所需宽度的一半。

原因是 8BitYUV 的传入流是 16 bits/pixel,而 Bitmap 期望 32 bits/pixel,因此 Bitmap 将每个传入的 MacroPixel(4 字节)视为一个像素而不是它真正的 2 个像素。


目前,我正在使用像素着色器修复颜色,并使用 RenderTransform 将图像水平缩放 2 倍至 "fix" 纵横比。问题是我只有原始分辨率的一半。

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

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

最好是硬件解决方案,即 DeckLink 或 GPU。

这里有多种选择。

首先可以直接显示UYVY。大多数视频适配器将通过 DirectDraw、DirectShow、DirectX 版本最多 9 个 API 接受 UYVY 数据,您不需要对视频帧进行实时转换。将其集成到 WPF 应用程序中可能需要一些努力,也许最流行的方法是通过 DirectShow.NET library and WPF Media Kit. On this way, however, you could also capture video using DeckLink's video capture DirectShow filter. You could connect all parts together faster, however you already capture using DeckLink SDK 使用 DirectShow,这样您就可以在捕获过程中拥有更多的控制权和灵活性,因此您可能不想返回到 DirectShow。

第二个选项是根据需要转换为 RGB。我不认为 DeckLink 可以为你做这件事,并且基于 GPU 的转换肯定存在(转换公式众所周知,简单且易于并行化),但是取决于硬件或者不能立即使用。相反,Microsoft 发布 Color Converter DSP which can do the conversion (from 8 bits, not 10 though) in a very efficient way. The API is native, and you might need Media Foundation .NET 以从您的应用访问它。也可以使用 FFmpeg 的 libswscale(对于通过相应包装器管理的应用程序)来完成替代的高效软件转换。

我刚刚用 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();
    }
}