65.9K
CodeProject 正在变化。 阅读更多。
Home

将 Blackmagic 视频帧集成到 DirectX 场景

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2017年10月30日

CPOL

2分钟阅读

viewsIcon

6341

我将解释如何获取包含 Blackmagic 捕获帧的纹理,以便将其显示到你的 DirectX 场景中。

引言

我将与你分享一些关于如何在 DirectX 环境中显示 Blackmagic 捕获的图像的技巧。

创建纹理

首先尝试使用 IDeckLinkDX9ScreenPreviewHelper 接口,它可以为你完成所有操作。这个类的缺点是你无法获得纹理,因此无法随意放置它。 无论如何,如果你想使用这个类,你会注意到它会破坏整个场景。 技巧是将其包含在 Sprite::Begin Sprite::End 中。 例如

m_device->SetTransform(D3DTS_WORLD, &matrix); 
m_sprite->Begin(D3DXSPRITE_OBJECTSPACE | D3DXSPRITE_ALPHABLEND);
helper->Render(NULL);
m_sprite->End();

在我的例子中,我希望完全控制纹理,并尽可能地降低延迟,我想所有开发者都希望如此。

基本思路是使用 DirectX 支持的格式 D3DFMT_UYVY,至少在我的例子中,我使用的是 NVIDIA QUADO 600。 这意味着我只处理 bmdFormat8BitYUV 格式。

如果你想告诉驱动程序使用这种格式,可以在 IDeckLinkInput::EnableVideoInput 函数的参数中指定它。 例如

HRESULT MFDeckLinkImpl::VideoInputFormatChanged 
(/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in 
*/ IDeckLinkDisplayMode *newMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags)
    {    
        Error* error = NULL;
            //if (detectedSignalFlags & bmdDetectedVideoInputRGB444)
            //    pixelFormat = bmdFormat10BitRGB;

            HRC(m_deckLinkInput->StopStreams());
            HRC(m_deckLinkInput->EnableVideoInput
               (newMode->GetDisplayMode(), bmdFormat8BitYUV, bmdVideoInputEnableFormatDetection));
            HRC(m_deckLinkInput->StartStreams());
        }

done:
        if (error)
            return error->Hr;

        return S_OK;
    } 

这样,你将强制所有接收到的帧都以 8 位 YUV 格式显示。 对于 SDI,你会注意到它不起作用,但更改一个设置就可以解决问题。 转到“Blackmagic Desktop Video Setup”转换,并选择 SD->HD 和变形变换。 这样,帧将使用 8 位 YUV 数据正确格式化。

接下来,我们需要将 8 位 YUV 图像转换为纹理。 我所做的是创建一个离线表面

m_device->CreateOffscreenPlainSurface(_width, _height, D3DFMT_UYVY, D3DPOOL_DEFAULT, &m_surface, NULL);

每当你在 DrawFrame 回调方法中接收到帧时,只需将“theFrame”的数据填充到 surface 中即可。

m_surface->LockRect(&rect, 0, D3DLOCK_DISCARD);
memcpy(rect.pBits, _buffer, _bufferSize);
m_surface->UnlockRect();

_buffer 来自 theFrame->GetBytes(); _bufferSize 在 8 位 YUV 的情况下是 width * height * 2

你需要将 surface 扩展为 RGB 格式的纹理,因此你创建纹理

m_device->CreateTexture(_width, _height, 1, D3DUSAGE_RENDERTARGET, 
D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &m_texture, NULL);

然后复制 surface

IDirect3DSurface9* surface;

m_texture->GetSurfaceLevel(0, &surface);
HRESULT hr = m_device->StretchRect(m_surface, NULL, surface,  NULL, D3DTEXF_NONE);
surface->Release();

现在你有了纹理,可以对它做任何你想做的事情了 :).

注意事项

你应该小心设计你的应用程序,通常 DrawFrame 在与你的 DirectX 场景不同的线程上运行,如果你在 DrawFrame 线程中使用上述转换,LockRectStretchRect 会导致延迟。 我所做的是复制从“theFrame”接收到的字节,该字节位于 DrawFrame 线程上,然后使用字节数组在绘制到场景线程之前拉伸图像和纹理。

希望这能帮助你在使用 DirectX 和 Blackmagic 卡时获得最佳性能。

© . All rights reserved.