我是靠谱客的博主 健壮水杯,这篇文章主要介绍DirectShow之视频渲染,现在分享给大家,希望可以做个参考。

概述

    DirectShow提供了几种渲染视频的滤镜:

   ·Video Renderer filter:此过滤器适用于支持DirectX的所有平台,并且没有特定的系统要求。 Video Renderer尽可能使用DirectDraw来呈现视频; 否则,它使用GDI。 此筛选器是比Windows XP早的平台上的默认视频渲染器。

   ·Video Mixing Renderer Filter 7 (VMR-7):VMR-7在Windows XP上可用,它是默认的视频渲染器。 VMR-7总是使用DirectDraw 7进行渲染。 它提供了旧版Video Renderer过滤器中没有的许多强大功能,包括应用程序控制用于渲染的DirectDraw表面的插件模型。

    ·Video Mixing Render Filter 9 (VMR-9):VMR-9是使用Direct3D 9进行渲染的视频混合渲染器的更新版本。 它适用于支持DirectX的所有平台。 但它不是默认的渲染器,因为它比Video Renderer过滤器具有更高的系统要求。

    ·Overlay Mixer滤镜专为DVD播放和广播视频而设计。 它还支持视频端口扩展(VPE),使其能够与硬件MPEG-2解码器或将视频直接发送到图形卡的模拟电视调谐器。

    ·增强视频渲染器(EVR)过滤器在Windows Vista中可用。 与早期的视频渲染器相比,它提供了改进的视频性能,尤其是在启用Windows Vista桌面组合时

 

   DirectShow视频渲染器可以在窗口模式或无窗口模式下运行:

  ·在窗口模式下,渲染器创建自己的窗口来显示视频。 通常,您将使此窗口成为应用程序窗口的子项。

  ·在无窗口模式下,渲染器将视频直接拖到应用程序窗口上。 它不创建它自己的窗口

   Video Renderer过滤器仅支持窗口模式。 VMR-7和VMR-9过滤器支持这两种模式。 它们默认为窗口模式以实现向后兼容,但无窗口模式是首选。 EVR仅支持无窗口模式。

 

Choosing the Right Video Renderer

   主要使用哪个渲染器取决于您需要支持哪些版本的Windows

   ·在Windows Vista及更高版本中,如果硬件支持,则应用程序应使用EVR。 否则,回到VMR-9或VMR-7。 EVR提供比以前的渲染器更好的性能和更好的视频质量。 此外,它旨在与桌面窗口管理器(DWM)一起使用。

   ·在Windows Vista之前,如果硬件支持它并且不需要视频端口功能,请使用VMR-9。 否则,请使用VMR-7。

   ·在较旧的系统上,您可能需要使用Overlay Mixer(用于视频端口或硬件叠加支持)或旧式Video Renderer过滤器。

   IGraphBuilder :: Render和RenderFile方法默认使用VMR-7。 如果硬件不支持VMR-7,这些方法将回退到传统的Video Renderer过滤器。 EVR和VMR-9绝不是默认的渲染器。

 

窗口模式

    在窗口模式下,视频渲染器创建自己的窗口,在该窗口绘制视频帧。 除非另有说明,否则此窗口是具有自己的边框和标题栏的顶层窗口。 但是,大多数情况下,您会将视频窗口附加到应用程序窗口,以便将视频集成到应用程序UI中。 这需要以下步骤:

   1.查询IVideoWindow。

   2.设置父窗口。

   3.设置新的窗口样式。

   4.将视频窗口放置在所有者窗口内。

   5.通知WM_MOVE消息的视频窗口。

 

Query for IVideoWindow

    在开始播放之前,请在Filter Graph Manager中查询IVideoWindow接口

复制代码
1
2
IVideoWindow *pVidWin = NULL; pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);

 

Set the Parent Window

 

    要设置父窗口,请调用带有应用程序窗口句柄的IVideoWindow :: put_Owner方法。 这个方法接受一个OAHWND类型的变量,所以把句柄转换为这种类型:

复制代码
1
pVidWin->put_Owner((OAHWND)hwnd);

 

Set New Window Styles

 

    通过调用IVideoWindow :: put_WindowStyle方法来更改视频窗口的样式:

复制代码
1
pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);

   WS_CHILD标志将窗口设置为子窗口,并且WS_CLIPSIBLINGS标志防止窗口在另一个子窗口的客户区域内绘制。

 

Position the Video Window

    要设置视频相对于应用程序窗口客户区的位置,请调用IVideoWindow :: SetWindowPosition方法。 此方法采用指定视频窗口的左边缘,顶边缘,宽度和高度的矩形。 例如,以下代码将视频窗口拉伸到适合父窗口的整个客户区:

复制代码
1
2
3
RECT rc; GetClientRect(hwnd, &rc); pVidWin->SetWindowPosition(0, 0, rc.right, rc.bottom);

    要获得视频的原始大小,请在Filter Graph Manager上调用IBasicVideo :: GetVideoSize方法。 您可以使用该信息来缩放视频并保持正确的宽高比。

 

Respond to WM_MOVE Messages

   为了获得最佳性能,每当窗口在图形暂停时移动时,都应通知视频渲染器。 调用IVideoWindow :: NotifyOwnerMessage方法来转发WM_MOVE消息:

复制代码
1
2
3
4
// (Inside your WindowProc) case WM_MOVE: pVidWin->NotifyOwnerMessage((OAHWND)hWnd, msg, wParam, lParam); break;

    如果渲染器正在使用硬件叠加层,则此通知将导致渲染器更新叠加层位置。 (VMR-9不使用叠加层,因此如果您使用VMR-9,则无需调用此方法。)

 

Cleap Up

   在应用程序退出之前,停止图形并将视频窗口的所有者重置为NULL。 否则,窗口消息可能被发送到错误的窗口,这可能会导致错误。 此外,隐藏视频窗口,否则您可能会在屏幕上瞬间看到视频图像闪烁:

复制代码
1
2
3
pControl->Stop(); pVidWin->put_Visible(OAFALSE); pVidWin->put_Owner(NULL);

 

 

 

无窗口模式

Configure the VMR for Windowless Mode

·VMR-7

   1.创建过滤器图形管理器。

   2.创建VMR-7并将其添加到过滤器图形中。

   3.使用VMRMode_Windowless标志在VMR-7上调用IVMRFilterConfig :: SetRenderingMode。

   4.查询IVMRWindowlessControl接口的VMR-7。

   5.在VMR-7上调用IVMRWindowlessControl :: SetVideoClippingWindow。指定视频应该出现的窗口的句柄。

·VMR-9   

   1.创建过滤器图形管理器。

   2.创建VMR-9并将其添加到过滤器图形中。

   3.使用VMR9Mode_Windowless标志调用VMR-9上的IVMRFilterConfig9 :: SetRenderingMode。

   4.查询IVMRWindowlessControl9界面的VMR-9。

   5.在VMR-9上调用IVMRWindowlessControl9 :: SetVideoClippingWindow。 指定视频应该出现的窗口的句柄。

 

   以下代码显示了一个辅助函数,它可以创建VMR-7,将其添加到图形中,并设置无窗口模式。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
HRESULT InitWindowlessVMR( HWND hwndApp, // Window to hold the video. IGraphBuilder* pGraph, // Pointer to the Filter Graph Manager. IVMRWindowlessControl** ppWc // Receives a pointer to the VMR. ) { if (!pGraph || !ppWc) { return E_POINTER; } IBaseFilter* pVmr = NULL; IVMRWindowlessControl* pWc = NULL; // Create the VMR. HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr); if (FAILED(hr)) { return hr; } // Add the VMR to the filter graph. hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer"); if (FAILED(hr)) { pVmr->Release(); return hr; } // Set the rendering mode. IVMRFilterConfig* pConfig; hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig); if (SUCCEEDED(hr)) { hr = pConfig->SetRenderingMode(VMRMode_Windowless); pConfig->Release(); } if (SUCCEEDED(hr)) { // Set the window. hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc); if( SUCCEEDED(hr)) { hr = pWc->SetVideoClippingWindow(hwndApp); if (SUCCEEDED(hr)) { *ppWc = pWc; // Return this as an AddRef'd pointer. } else { // An error occurred, so release the interface. pWc->Release(); } } } pVmr->Release(); return hr; }

    此功能假定仅显示一个视频流,而不是在视频上混合静态位图。你可以这样调用这个函数:

复制代码
1
2
3
4
5
6
7
8
9
IVMRWindowlessControl *pWc = NULL; hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc); if (SUCCEEDED(hr)) { // Build the graph. For example: pGraph->RenderFile(wszMyFileName, 0); // Release the VMR interface when you are done. pWc->Release(); }

 

 

 

Positon the Video

·VMR-7

   1.调用IVMRWindowlessControl :: SetVideoPosition方法来指定两个矩形。

   2.源矩形必须等于或小于原始视频大小; 您可以使用IVMRWindowlessControl :: GetNativeVideoSize方法获取原生视频大小。

·VMR-9

   1.调用IVMRWindowlessControl9 :: SetVideoPosition方法来指定两个矩形。

   2.源矩形必须等于或小于原始视频大小; 您可以使用IVMRWindowlessControl9 :: GetNativeVideoSize方法来获取原生视频大小。

   例如,以下代码设置VMR-7的源矩形和目标矩形。 它将源矩形设置为等于整个视频图像,目标矩形等于整个窗口客户区:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Find the native video size. long lWidth, lHeight; HRESULT hr = g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL); if (SUCCEEDED(hr)) { RECT rcSrc, rcDest; // Set the source rectangle. SetRect(&rcSrc, 0, 0, lWidth, lHeight); // Get the window client area. GetClientRect(hwnd, &rcDest); // Set the destination rectangle. SetRect(&rcDest, 0, 0, rcDest.right, rcDest.bottom); // Set the video position. hr = g_pWc->SetVideoPosition(&rcSrc, &rcDest); }

 

 

 

Handle Window Messages

·VMR-7

    1.WM_PAINT。 调用IVMRWindowlessControl :: RepaintVideo。 此方法会导致VMR-7重新绘制最近的视频帧。                 2.WM_DISPLAYCHANGE:调用IVMRWindowlessControl :: DisplayModeChanged。 此方法通知VMR-7必须以新的分辨率或颜色深度显示视频。

    3.WM_SIZE或WM_WINDOWPOSCHANGED:重新计算视频的位置并在需要时调用IVMRWindowlessControl :: SetVideoPosition更新位置。

·VMR-9

   1.WM_PAINT。 调用IVMRWindowlessControl9 :: RepaintVideo。 此方法会导致VMR-9重新绘制最近的视频帧。                2.WM_DISPLAYCHANGE:调用IVMRWindowlessControl9 :: DisplayModeChanged。 此方法通知VMR-9必须以新的分辨率或颜色深度显示视频。

   3.WM_SIZE或WM_WINDOWPOSCHANGED:重新计算视频的位置并在需要时调用IVMRWindowlessControl9 :: SetVideoPosition更新位置。

 

    以下示例显示了一个WM_PAINT消息处理程序。 它绘制由客户矩形减去视频矩形定义的区域。 不要在视频矩形上绘制,因为VMR会覆盖它,导致闪烁。 出于同样的原因,不要在窗口类中设置背景画笔。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void OnPaint(HWND hwnd) { PAINTSTRUCT ps; HDC hdc; RECT rcClient; GetClientRect(hwnd, &rcClient); hdc = BeginPaint(hwnd, &ps); if (g_pWc != NULL) { // Find the region where the application can paint by subtracting // the video destination rectangle from the client area. // (Assume that g_rcDest was calculated previously.) HRGN rgnClient = CreateRectRgnIndirect(&rcClient); HRGN rgnVideo = CreateRectRgnIndirect(&g_rcDest); CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF); // Paint on window. HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE); FillRgn(hdc, rgnClient, hbr); // Clean up. DeleteObject(hbr); DeleteObject(rgnClient); DeleteObject(rgnVideo); // Request the VMR to paint the video. HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc); } else // There is no video, so paint the whole client area. { FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1)); } EndPaint(hwnd, &ps); }

参考:

https://www.yuque.com/docs/share/6c6329ba-62e6-4b66-8d01-0c57d47828de

最后

以上就是健壮水杯最近收集整理的关于DirectShow之视频渲染的全部内容,更多相关DirectShow之视频渲染内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部