DirectShow下的视频显示窗体

2024-01-05 08:58

本文主要是介绍DirectShow下的视频显示窗体,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

显示视频

DirectShow提供了如下过滤器来显示视频:

l        Video Renderer过滤器.该过滤器可用于所有的支持DirectX的平台,它对平台没有其它特殊的要求。可以是它,或GDI来显示视频。它是在WindowsXP之前操作系统的默认视频显示过滤器。

l        Video Mixing Renderer Filter 7(VMR-7). VMR-7可用于WindowsXP操作系统,并且是该系统下的默认视频显示过滤器。与老的视频显示过滤器相比,它具有一些更强大的性能,包括采用插件模式来控制DirectShow显示。

l        Video Mixing Renderer Filter 9 (VMR-9). VMR-9是一个更新的视频混合显示过滤器,它采用了Direct3D来显示。它可用于所有的支持DirectX的平台。它不是默认的显示过滤器,因为它与其它的显示过滤器相比,对系统要求更高。

 

一般来说,在视频显示应用上,VMR-9是首选。因为,它使用了最新的图像API,并且提供了最好的性能。

 

窗体模式和非窗体模式

DirectShow视频显示可以选择在窗体模式或者非窗体模式下进行。

l        在窗体模式下,视频将创建一个它自己的窗体来显示。

l        在非窗体模式下,视频可以自己在你程序的一个窗口上显示,而不让视频自己区创建窗体来显示。

 Video Renderer过滤器只支持窗体模式,VMR-7VMR-9支持这两种模式。它们默认状态是窗体模式。

设置视频窗口

  在窗体模式下,视频将创建一个窗口,然后在该窗口上显示视频。大多数情况下,你可能想要把该窗口绑定到你的应用程序中。通过使用IVideoWindow接口,可以设置视频窗口的类型和位置。

  在开始播放前,在过滤器图表管理器中去查找IVideoWindow接口:
IVideoWindow *pVidWin = NULL;
pGraph->QueryInterface(IID_IVideoWindow, (void **)&g_pVidWin);
调用IVideoWindow::put_Owner方法去处理你应用程序的窗体。该方法提供了一个OAHWND类型的变量,所以要把句柄转换为该类型:
pVidWin->put_Owner((OAHWND)hwnd);
调用IVideoWindow::Put_WindowStyle来改变视频窗体的类型:
pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
WS_CHILD标志设置视频窗体为一个子窗体,WS_CLIPSIBLINGS标志可以防止视频窗体在另一个子窗体的客户区内显示视频。
调用IVideoWindow::SetWindowPosition方法可以视频窗口的相对于你应用程序的客户区的位置。该方法的参数带了一个RECT参数,用它去指定视频窗口的位置。下例,让视频窗口和它父窗体的客户区想匹配。
RECT grc;
GetClientRect(hwnd, &grc);
pVidWin->SetWindowPosition(0, 0, grc.right, grc.bottom);
通过在过滤器图表管理器上调用IBaseicVideo::GetVideoSize方法可以得到视频本身的尺寸大小。你可以通过这些信息让视频保持正确的纵横比例。
在应用程序退出前,停止图表并重置视频窗口为NULL。否则,窗口消息可能被错误的发送给错误的窗口,从而导致错误发生,
pControl->Stop();
pVidWin->put_Visible(OAFALSE);
pVidWin->put_Owner(NULL); 

使用非窗体模式

视频混合显示过滤器(VMR-7 VMR-9)都支持非窗体模式。这里将描述窗体模式和非窗体模式之间的不同,以及如何使用非模式窗体。

      为了向后兼容已经在使用的应用程序,VMR默认的显示模式为窗体模式。在窗体模式中,视频创建一个它自己的窗体去显示视频。应用程序设置这个视频窗体为它的一个子窗体。这个单独存在的窗体会导致如下问题:

l        最严重的是,如果窗体的消息在线程间发送可能导致消息死锁。

l        过滤器图表管理器必须传递某些window消息,比如WM_PAINT,给视频显示器(Video Renderer)。这些对IvideoWIndow的操作必须是由过滤器图表管理器来完成,而不是视频显示器来完成,所以要靠过滤器图表管理器来纠正内部状态。

l        要视频窗体的鼠标或者键盘事件,应用程序必须建立一个“消息通道”,让视频窗口把消息传递给应用程序。

l        为了防止剪接的情况,视频窗体还必须拥有正确的窗口状态。

非窗体模式通过使用VMR直接在应用程序的客户区上画图来避免了上述的问题。它使用DirectDraw去剪接视频矩形。非窗体模式极大程度减少了死锁的偶然发生。同样,应用程序不必去设置视频自身创建的窗口和窗口的状态。事实上,当VRM使用窗体模式时,它也不使用IVideoWindow接口。

      要使用非窗体模式,你必须明确地去配置VMR。你会发现配置工作非常灵活并且比窗体模式更容易。

在配置VMR前应建立过滤器图表(Filter graph):

  1. 创建过滤器图表管理器(Filter Graph Manager)。
  2. 创建VMR并添加到过滤器图表中(filter graph)。
  3. VMR中调用IVMRFilterConfig::SetRenderingMode设置VMRMode_Windowless标识。
  4. VMR中调用IVMRWindowlessControl::SetVideoClippingWindow去指定视频将要显示的窗体句柄。

//设置视频显示窗口,无窗口模式
BOOL CDXGraph::SetVideoWindow(IBaseFilter* pVmr, bool isFullScreen, HWND hwnd)
{
// 设置显示模式 
    IVMRFilterConfig* pConfig; 
IVMRWindowlessControl* pWc;
HRESULT hr;
    hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig); 
   if (SUCCEEDED(hr)) 
   { 
        hr = pConfig->SetRenderingMode(VMRMode_Windowless); 
        pConfig->Release(); 
    }
    if (true) //SUCCEEDED(hr))
    {
        // 设置窗体 
        hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);
        if( SUCCEEDED(hr)) 
        { 


if (isFullScreen)
{
hwnd = GetDesktopWindow();  //获得屏幕的句柄
}
            hr = pWc->SetVideoClippingWindow(hwnd); 
            if (SUCCEEDED(hr))
            {
// Find the native video size.
long lWidth, lHeight; 
hr = pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL); 
if (SUCCEEDED(hr))
{
RECT rcSrc, rcDest; 
SetRect(&rcSrc, 0, 0, lWidth, lHeight); 
GetClientRect(hwnd, &rcDest); 
SetRect(&rcDest, 0, 0, rcDest.right/2, rcDest.bottom/2);
hr = pWc->SetVideoPosition(&rcSrc, &rcDest); 
}
}
            else
            {
                pWc->Release();
}
        }
    } 
return hr;
}

视频定位

      配置完VMR后,下一个步骤就是去设置视频显示的位置。有两个矩形位置要考虑,一个是Source矩形位置,一个是desitnation矩形位置。Source定义视频显示的位置。Destination指定包含视频的窗体的客户区的位置。VMRsource把图像按destination的尺寸匹配后显示出来。

      调用IVMRWindowlessControl::SetVideoPosition去指定这个两个矩形位置。Source矩形的大小必须等于或小于视频本身的尺寸大小;你可以使用IVMRWindowlessControl::GetNativeVideoSize去获得视频本身的尺寸。

下面的实例,将设置Source等于视频尺寸1/4(左上角的位置相等),并设置destination矩形等于窗体客户区的大小1/(左上角的位置相等):


//获得视频自身尺寸大小
long lWidth, lHeight;
HRESULT hr = g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);
if (SUCCEEDED(hr))
{
    RECT rcSrc, rcDest;
   // 设置Source尺寸
    SetRect(&rcSrc, 0, 0, lWidth/2, lHeight/2);
   
   // 获得显示窗体的客户区尺寸
    GetClientRect(hwnd, &rcDest);
   //设置destination尺寸
    SetRect(&rcDest, 0, 0, rcDest.right/2, rcDest.bottom/2);
   
   // 视频定位
    hr = g_pWc->SetVideoPosition(&rcSrc, &rcDest);
}

处理窗体消息

      因为VMR没有自己的窗体,当视频需要重画或者尺寸要改变是,你必须要通知窗体来适应。

l          当接收到一个WM_PAINT消息,可调用IVMRWindowlessControl::RepaintVideo来重画图像。

l          当接收到一个WM_DISPLAYCHANGE消息,可调用IVMRWindowlessControl::DisplayModeChanged消息。VMR就可以获得如下行为比如改变分辨率或者色深。

l          当接收到一个WM_SIZE消息,可以重新调用SetVideoPosition来改变视频的显示位置。

下面显示如何处理WM_PAINT消息。它将在窗体的客户区重绘,但是不会对视频显示的区域进行重绘。不对视频显示的区域进行重绘,是因为VMR会对该区域显示视频,如果你的程序再对该区域重绘会引起屏幕闪烁。也是应为这个原因,所有不要在你窗体类中去设置背景刷。



void OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    HDC         hdc;
    RECT        rcClient;
    GetClientRect(hwnd, &rcClient);
    hdc = BeginPaint(hwnd, &ps);
    if (g_pWc != NULL)
    {
       // 查找窗体需要重绘的客户区,该区域应该减去视频显示的区域
       // (这里假设g_rcDest是已经计算好了的区域)
        HRGN rgnClient = CreateRectRgnIndirect(&rcClient);
        HRGN rgnVideo  = CreateRectRgnIndirect(&g_rcDest); 
        CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF); 
       
       // 重绘窗体
        HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE);
        FillRgn(hdc, rgnClient, hbr);
 
       // 释放对象
        DeleteObject(hbr);
        DeleteObject(rgnClient);
        DeleteObject(rgnVideo);
 
       // 请求VMR to重绘视频
        HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc); 
    }
    else // 没有视频显示,重绘整个客户区
    {
        FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1));
    }
    EndPaint(hwnd, &ps);
}

这篇关于DirectShow下的视频显示窗体的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/572342

相关文章

如何设置vim永久显示行号

《如何设置vim永久显示行号》在Linux环境下,vim默认不显示行号,这在程序编译出错时定位错误语句非常不便,通过修改vim配置文件vimrc,可以在每次打开vim时永久显示行号... 目录设置vim永久显示行号1.临时显示行号2.永www.chinasem.cn久显示行号总结设置vim永久显示行号在li

Python视频处理库VidGear使用小结

《Python视频处理库VidGear使用小结》VidGear是一个高性能的Python视频处理库,本文主要介绍了Python视频处理库VidGear使用小结,文中通过示例代码介绍的非常详细,对大家的... 目录一、VidGear的安装二、VidGear的主要功能三、VidGear的使用示例四、VidGea

电脑显示hdmi无信号怎么办? 电脑显示器无信号的终极解决指南

《电脑显示hdmi无信号怎么办?电脑显示器无信号的终极解决指南》HDMI无信号的问题却让人头疼不已,遇到这种情况该怎么办?针对这种情况,我们可以采取一系列步骤来逐一排查并解决问题,以下是详细的方法... 无论你是试图为笔记本电脑设置多个显示器还是使用外部显示器,都可能会弹出“无HDMI信号”错误。此消息可能

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器