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,并且提供了最好的性能。

 (注:一条graph链路多个render时,非窗体模式是最好的选择)

窗体模式和非窗体模式

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 去指定视频将要显示的窗体句柄。

现在调用IGraphBuilder::RenderFile完成过滤器图表余下的工作。过滤器图表管理器将自动使用这个你添加到过滤器图表中的VMR实例。

下面代码显示了这些工作:

HRESULT InitWindowlessVMR(

    HWND hwndApp,               // 视频窗体

    IGraphBuilder* pGraph,           //过滤器图表指针

    IVMRWindowlessControl** ppWc, // 接收VMR指针

    )

{

    if (!pGraph || !ppWc) return E_POINTER;

    IBaseFilter* pVmr = NULL;

    IVMRWindowlessControl* pWc = NULL;

   // 创建VMR

    HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,

        CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);

    if (FAILED(hr))

    {

        return hr;

    }

   

   // VMR添加到过滤器图表中

    hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer");

    if (FAILED(hr))

    {

        pVmr->Release();

        return hr;

    }

   // 设置显示模式

    IVMRFilterConfig* pConfig;

    hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig);

    if (SUCCEEDED(hr))

    {

        hr = pConfig->SetRenderingMode(VMRMode_Windowless);

        pConfig->Release();

    }

    if (SUCCEEDED(hr))

    {

       // 设置窗体

        hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);

        if( SUCCEEDED(hr))

        {

            hr = pWc->SetVideoClippingWindow(hwndApp);

            if (SUCCEEDED(hr))

            {

                *ppWc = pWc;//返回AddRef指针

            }

            else

            {

                pWc->Release();

            }

        }

    }

    pVmr->Release();

    return hr;

}

该函数假设正在显示一个视频流并没有混合的静态位图。你将看到按如下调用该函数:

IVMRWindowlessControl *pWc = NULL;

hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc);

if (SUCCEEDED(hr))

{

   // 建立图表

    pGraph->RenderFile(wszMyFileName, 0);

   //完成后释放VMR接口

    pWc->Release();

}

视频定位

       配置完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);

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: DirectShow 是一种用于多媒体应用程序开发的Microsoft技术,可以实现视频的实时显示并抓图,并且还可以设置视频参数。在DirectShow中,可以通过构建图形和过滤器图来实现这些功能。 首先,要实现视频的实时显示,我们需要创建一个视频捕捉过滤器,并连接到显示过滤器。通过DirectShow提供的接口,可以选择摄像头设备作为视频源,然后创建一个Sample Grabber过滤器用于捕获视频图像。Sample Grabber过滤器可以设置回调函数,在视频数据接收时即时处理图像数据。然后将捕捉到的视频数据通过DirectShow的某个视频渲染器过滤器进行显示,如VideoRenderer。 其次,要实现视频的抓图,可以使用Sample Grabber过滤器,捕获图像后保存为位图格式或其他图像格式。我们可以在回调函数中获取图像数据,并将其保存为图像文件。这样,我们就能够实现对视频流的实时截图。 最后,DirectShow还可以设置视频参数,如帧率、分辨率、亮度、对比度等。通过获取视频源过滤器的接口,可以设置这些参数,以调整视频显示效果。 综上所述,直接展示和抓取图像的方法包括创建视频捕捉过滤器、Sample Grabber过滤器和视频渲染器过滤器,并利用回调函数处理图像数据实现实时显示和抓图。此外,还可以通过DirectShow接口设置视频参数,以改变视频显示效果。 ### 回答2: DirectShow是一套功能强大的多媒体框架,可以用于实现视频的实时显示并抓图,并且还可以设置视频的参数。 要实现视频的实时显示,首先需要使用DirectShow创建一个图形化用户界面窗口,并将视频源与该窗口进行关联。可以使用DirectShow提供的基本组件来实现这一功能,比如视频捕捉组件,将摄像头或者其他视频源的输入传递给视频渲染组件,然后通过渲染组件将视频显示窗口中。 在实时显示视频的过程中,我们可以设置视频的参数。DirectShow提供了丰富的接口和功能,可以通过控制接口对视频进行参数设置。例如可以调整视频的亮度、对比度、色彩饱和度等等。还可以调整视频的分辨率、帧率、编解码格式等参数,以满足具体应用场景的需求。 另外,DirectShow还可以实现视频的抓图功能。通过调用DirectShow提供的接口,我们可以将当前视频帧保存为图像文件,以便于后续的处理和使用。可以设置抓图的分辨率和格式,以满足不同场景下的需求。 总结起来,DirectShow可以实现视频的实时显示并抓图,还可以设置视频的参数。它是一个功能强大、灵活性高的多媒体框架,为开发者提供了丰富的接口和工具,以便于实现多样化的视频处理需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值