You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从IGraphBuilder获取IDirectDrawSurface等?已尝试方案未果

从IGraphBuilder获取DirectDraw组件/绘图句柄实现视频帧绘制(纯Win32)

核心思路

IGraphBuilder本身不持有DirectDraw相关资源,这些资源属于渲染器滤镜(如VMR-9、Overlay Mixer)。你需要从渲染器接口入手,而非直接从IGraphBuilder获取。以下是三种可行方案,均基于纯Win32 API实现:


方案1:修正IVMRWindowlessControl的使用(推荐,适合实时叠加)

你之前尝试IVMRWindowlessControl失败,大概率是渲染器初始化或窗口关联步骤有误。按以下步骤重新实现:

  1. 手动指定VMR-9为渲染器
    默认情况下GraphBuilder可能使用System Renderer,不支持窗口less模式。需手动创建VMR-9滤镜并添加到图中:
IBaseFilter* pVMR9 = nullptr;
CoCreateInstance(CLSID_VideoMixingRenderer9, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pVMR9);
pGraph->AddFilter(pVMR9, L"VMR-9 Renderer");
  1. 启用窗口less模式并关联窗口
    查询IVMRWindowlessControl9接口,设置目标窗口并初始化:
IVMRWindowlessControl9* pWC = nullptr;
pVMR9->QueryInterface(IID_IVMRWindowlessControl9, (void**)&pWC);
pWC->SetVideoClippingWindow(hWnd); // hWnd是你的Win32窗口句柄
pWC->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX); // 按需设置比例模式
  1. 获取HDC进行实时绘制
    在视频播放期间,通过GetDC获取视频表面的HDC,绘制后立即释放:
HDC hdc = nullptr;
RECT rcVideo;
pWC->GetVideoPosition(nullptr, &rcVideo); // 获取视频在窗口中的区域
if (SUCCEEDED(pWC->GetDC(&hdc))) {
    // 用GDI函数绘制,比如绘制文本
    SetTextColor(hdc, RGB(255, 0, 0));
    DrawText(hdc, L"Overlay Text", -1, &rcVideo, DT_CENTER | DT_VCENTER);
    pWC->ReleaseDC(hdc);
}

注意:绘制操作需放在视频帧更新的时机(如通过定时器或VMR的回调触发),避免与渲染线程冲突。


方案2:通过Overlay Mixer获取IDirectDraw

若需直接操作DirectDraw组件,可使用Overlay Mixer渲染器:

  1. 添加Overlay Mixer到Filter Graph
IBaseFilter* pOverlayMixer = nullptr;
CoCreateInstance(CLSID_OverlayMixer, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pOverlayMixer);
pGraph->AddFilter(pOverlayMixer, L"Overlay Mixer");
  1. 获取IDirectDrawVideo接口
IDirectDrawVideo* pDDVideo = nullptr;
pOverlayMixer->QueryInterface(IID_IDirectDrawVideo, (void**)&pDDVideo);
  1. 获取IDirectDraw及Surface
IDirectDraw* pDD = nullptr;
pDDVideo->GetDirectDraw(&pDD);
// 后续可通过pDD创建或获取DirectDrawSurface,进而获取HDC
IDirectDrawSurface* pSurface = nullptr;
// 需根据Overlay Mixer的配置获取对应Surface,例如主表面
DDSURFACEDESC ddsd = {sizeof(ddsd)};
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
pDD->CreateSurface(&ddsd, &pSurface, nullptr);
// 获取Surface的HDC
HDC hdcSurface = nullptr;
pSurface->GetDC(&hdcSurface);

方案3:使用Sample Grabber捕获帧后自行绘制

若DirectDraw路径仍有问题,可通过Sample Grabber获取原始帧数据,自行绘制到窗口:

  1. 添加Sample Grabber滤镜
IBaseFilter* pGrabber = nullptr;
CoCreateInstance(CLSID_SampleGrabber, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabber);
pGraph->AddFilter(pGrabber, L"Sample Grabber");
  1. 配置Sample Grabber并设置回调
    实现ISampleGrabberCB接口,在回调中处理帧数据:
class CSampleGrabberCB : public ISampleGrabberCB {
public:
    STDMETHODIMP SampleCB(double SampleTime, IMediaSample* pSample) {
        // 获取帧数据缓冲区
        BYTE* pBuffer = nullptr;
        pSample->GetPointer(&pBuffer);
        LONG lSize = pSample->GetSize();
        // 假设帧格式为RGB24,创建内存DC绘制
        HDC hdcMem = CreateCompatibleDC(hWndDC);
        HBITMAP hBmp = CreateBitmap(width, height, 1, 24, pBuffer);
        HGDIOBJ hOldBmp = SelectObject(hdcMem, hBmp);
        // 叠加绘制内容
        DrawText(hdcMem, L"Overlay", -1, &rc, DT_LEFT);
        // 绘制到窗口DC
        BitBlt(hWndDC, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
        // 释放资源
        SelectObject(hdcMem, hOldBmp);
        DeleteObject(hBmp);
        DeleteDC(hdcMem);
        return S_OK;
    }
    // 实现其他必要接口(QueryInterface、AddRef、Release)
};

然后将回调绑定到Sample Grabber:

ISampleGrabber* pSG = nullptr;
pGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pSG);
pSG->SetCallback(new CSampleGrabberCB(), 1); // 1表示调用SampleCB

关键注意事项

  • 所有COM接口使用后必须调用Release()释放资源,避免内存泄漏。
  • 确保在程序启动时调用CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)初始化COM环境。
  • IBasicVideo::GetCurrentImage仅能获取单帧拷贝,无法实现实时叠加,因此不适合你的场景。

内容的提问来源于stack exchange,提问作者Milivoje Stankovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:04:52