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

基于C++Builder与Media Foundation的视频上层图形绘制方案问询

在C++Builder + Media Foundation播放器上叠加绘制图形的解决方案

看起来你遇到的核心问题是Media Foundation(MF)的视频渲染层级在你绘制内容的上方,导致图形被视频覆盖。这是因为MF默认使用硬件加速(比如Direct3D)直接在目标窗口的后台缓冲区渲染视频,绕过了常规的GDI绘制流程,所以你用GDI/GDI+等绘制的内容要么被后续视频帧覆盖,要么渲染在视频下方。

先分析下你现有代码的几个问题:

  • 绘图时错误使用了this->Handle(Form的窗口句柄)调用BeginPaint/EndPaint,但视频是渲染在Panel2上的,应该针对Panel2的句柄操作。
  • 提前在FormCreate中获取Panel2的DC不可靠,窗口DC可能被系统重置,建议绘图时临时获取并释放。
  • 坐标未转换:Dot控件放在Form上,直接用它们的Left/Top坐标绘制到Panel2会有偏移,需要转换为Panel2的客户区坐标。

下面给出两种可行的解决方案:

方法1:修正GDI绘图逻辑(适合暂停场景)

这种方法仅在视频暂停时生效,确保MF不再刷新视频帧后,直接在Panel2上叠加绘制:

// 将imfp声明为Form的成员变量,不要定义为局部变量
IMFPMediaPlayer* imfp = nullptr;

void __fastcall TForm6::FormCreate(TObject *Sender) {
    Panel1->Color = clRed;
    // 注意路径转义,原代码的E:\xxx.mp4要改成E:\\xxx.mp4
    HRESULT hres = MFPCreateMediaPlayer(L"E:\\xxx.mp4", TRUE, 0, imfpcall, Panel2->Handle, &imfp);
    imfp->SetBorderColor(RGB(125,85,0));
}

void __fastcall TForm6::DrawL() {
    // 仅在视频暂停时执行绘制
    if (!imfp) return;
    MFP_MEDIAPLAYER_STATE state;
    if (FAILED(imfp->GetState(&state)) || state != MFP_MEDIAPLAYER_STATE_PAUSED) {
        return;
    }

    // 获取Panel2的设备上下文
    HDC dc = GetDC(Panel2->Handle);
    if (!dc) return;

    // 创建画笔并绑定到DC
    HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 130, 0));
    HPEN hOldPen = static_cast<HPEN>(SelectObject(dc, hPen));

    // 转换Form控件坐标到Panel2的客户区
    POINT pt1 = {Dot1->Left + 15, Dot1->Top + 15};
    POINT pt2 = {Dot2->Left + 15, Dot2->Top + 15};
    POINT pt3 = {Dot3->Left + 15, Dot3->Top + 15};
    ScreenToClient(Panel2->Handle, &pt1);
    ScreenToClient(Panel2->Handle, &pt2);
    ScreenToClient(Panel2->Handle, &pt3);

    // 绘制线条
    MoveToEx(dc, pt2.x, pt2.y, nullptr);
    LineTo(dc, pt1.x, pt1.y);
    MoveToEx(dc, pt3.x, pt3.y, nullptr);
    LineTo(dc, pt2.x, pt2.y);

    // 恢复DC状态并释放资源
    SelectObject(dc, hOldPen);
    DeleteObject(hPen);
    ReleaseDC(Panel2->Handle, dc);
}

方法2:使用分层叠加控件(推荐,支持播放/暂停场景)

这种方法更可靠,通过创建一个透明的子控件放在视频面板上方,专门用于绘制图形,完全避开MF的渲染层级冲突:

IMFPMediaPlayer* imfp = nullptr;
TPanel* OverlayPanel = nullptr;

void __fastcall TForm6::FormCreate(TObject *Sender) {
    Panel1->Color = clRed;
    HRESULT hres = MFPCreateMediaPlayer(L"E:\\xxx.mp4", TRUE, 0, imfpcall, Panel2->Handle, &imfp);
    imfp->SetBorderColor(RGB(125,85,0));

    // 创建叠加面板,作为Panel2的子控件
    OverlayPanel = new TPanel(this);
    OverlayPanel->Parent = Panel2;
    OverlayPanel->Align = alClient; // 和Panel2大小保持一致
    OverlayPanel->Transparent = true; // 透明属性让视频内容透过显示
    OverlayPanel->DoubleBuffered = true; // 避免绘图闪烁
    OverlayPanel->OnPaint = &OverlayPanelPaint; // 绑定绘制事件
}

void __fastcall TForm6::OverlayPanelPaint(TObject *Sender) {
    TPanel* panel = static_cast<TPanel*>(Sender);
    HDC dc = panel->Canvas->Handle;

    HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 130, 0));
    HPEN hOldPen = static_cast<HPEN>(SelectObject(dc, hPen));

    // 转换Dot控件坐标到叠加面板的客户区
    POINT pt1 = {Dot1->Left + 15, Dot1->Top + 15};
    POINT pt2 = {Dot2->Left + 15, Dot2->Top + 15};
    POINT pt3 = {Dot3->Left + 15, Dot3->Top + 15};
    ScreenToClient(panel->Handle, &pt1);
    ScreenToClient(panel->Handle, &pt2);
    ScreenToClient(panel->Handle, &pt3);

    // 绘制线条
    MoveToEx(dc, pt2.x, pt2.y, nullptr);
    LineTo(dc, pt1.x, pt1.y);
    MoveToEx(dc, pt3.x, pt3.y, nullptr);
    LineTo(dc, pt2.x, pt2.y);

    // 清理资源
    SelectObject(dc, hOldPen);
    DeleteObject(hPen);
}

// 需要更新图形时,调用此方法刷新叠加面板
void __fastcall TForm6::UpdateOverlay() {
    if (OverlayPanel) {
        OverlayPanel->Invalidate();
    }
}

为什么这个方法有效?

OverlayPanel是Panel2的子控件,处于视频渲染层级的上方,透明属性让视频内容可以透过显示,而你在它上面绘制的图形会直接叠加在视频之上。无论视频是播放还是暂停,图形都会保持显示,不会被MF的渲染覆盖。

额外提示

  • 如果需要在视频播放时也显示图形,方法2是唯一可靠的选择,因为播放时MF会持续刷新视频帧,直接在Panel2上绘制的内容会被立即覆盖。
  • 避免长期持有窗口DC,每次绘图时获取、用完立即释放,防止资源泄漏。
  • 如果需要半透明等复杂图形效果,可以在OverlayPanel中使用GDI+或Direct2D进行绘制,效果会更出色。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 22:02:36