如何从IGraphBuilder获取IDirectDrawSurface等?已尝试方案未果
从IGraphBuilder获取DirectDraw组件/绘图句柄实现视频帧绘制(纯Win32)
核心思路
IGraphBuilder本身不持有DirectDraw相关资源,这些资源属于渲染器滤镜(如VMR-9、Overlay Mixer)。你需要从渲染器接口入手,而非直接从IGraphBuilder获取。以下是三种可行方案,均基于纯Win32 API实现:
方案1:修正IVMRWindowlessControl的使用(推荐,适合实时叠加)
你之前尝试IVMRWindowlessControl失败,大概率是渲染器初始化或窗口关联步骤有误。按以下步骤重新实现:
- 手动指定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");
- 启用窗口less模式并关联窗口
查询IVMRWindowlessControl9接口,设置目标窗口并初始化:
IVMRWindowlessControl9* pWC = nullptr; pVMR9->QueryInterface(IID_IVMRWindowlessControl9, (void**)&pWC); pWC->SetVideoClippingWindow(hWnd); // hWnd是你的Win32窗口句柄 pWC->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX); // 按需设置比例模式
- 获取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渲染器:
- 添加Overlay Mixer到Filter Graph
IBaseFilter* pOverlayMixer = nullptr; CoCreateInstance(CLSID_OverlayMixer, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pOverlayMixer); pGraph->AddFilter(pOverlayMixer, L"Overlay Mixer");
- 获取IDirectDrawVideo接口
IDirectDrawVideo* pDDVideo = nullptr; pOverlayMixer->QueryInterface(IID_IDirectDrawVideo, (void**)&pDDVideo);
- 获取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获取原始帧数据,自行绘制到窗口:
- 添加Sample Grabber滤镜
IBaseFilter* pGrabber = nullptr; CoCreateInstance(CLSID_SampleGrabber, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabber); pGraph->AddFilter(pGrabber, L"Sample Grabber");
- 配置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
相关产品推荐
相关产品推荐

