DirectShow视频渲染器9的使用指南

在DirectX9中,多媒体应用程序可以使用一种新的视频渲染器来显示解码后的帧,但出于兼容性问题的考虑,这种渲染器并不是默认的渲染器。在Windows XP上,默认的渲染器是VMR7,而在更早的Windows版本中则是视频渲染器。主要的区别在于性能和覆盖混合能力:旧的渲染器使用不同版本的DirectDraw API(对于视频渲染器甚至是更旧的API),而VMR9基于DirectX图形,因此它使用了3D显卡的Direct3D能力。结果是在最近的3D显卡上性能得到了提升,更好地支持覆盖混合,与所有支持DirectX9的Windows版本兼容,并且增加了一些新功能,如去隔行和ProcAmp支持(对比度、饱和度等)。

新的VMR9看起来很好,但它不是默认的渲染器,不管Windows版本如何...必须手动构建它,这就是写这个类的原因。

需要什么...

要运行演示,必须在系统上安装DirectX9运行时,该系统必须具有Direct3D兼容的显示适配器。要构建,必须在系统上安装DirectX9 SDK。源代码是在Visual C++ 6 SP5下创建的。

所做的所有测试都是在运行ATI Radeon M7(类似于Radeon 7500)的WinXP机器上进行的。由于无法检查与许多其他Windows版本和视频卡的真正兼容性,请尝试使用系统,并在论坛上发表意见。

使用代码

首先看一下

所有的DirectShow图管理和VMR例程都包含在一个类中:

class CVMR9Graph { // 构造函数/析构函数 public: CVMR9Graph(); CVMR9Graph(HWND MediaWindow, int NumberOfStream = 4); virtual ~CVMR9Graph(); // 方法 public: // 图配置 void SetNumberOfLayer(int nNumberOfLayer); BOOL SetMediaWindow(HWND MediaWindow); BOOL SetMediaFile(const char* pszFileName, int nLayer = 0); BOOL PreserveAspectRatio(BOOL bPreserve = TRUE); IBaseFilter* AddFilter(const char* pszName, const GUID& clsid); // 图控制 BOOL PlayGraph(); BOOL StopGraph(); BOOL ResetGraph(); IMediaEvent* GetPtrMediaEvent(); IMediaControl* GetPtrMediaControl(); IMediaSeeking* GetPtrMediaSeeking(); IBasicAudio* GetPtrBasicAudio(); // 图层控制 BOOL GetVideoRect(LPRECT pRect); int GetAlphaLayer(int nLayer); BOOL SetAlphaLayer(int nLayer, int nAlpha); DWORD GetLayerZOrder(int nLayer); BOOL SetLayerZOrder(int nLayer, DWORD dwZOrder); BOOL SetLayerRect(int nLayer, RECT layerRect); // 位图控制 BOOL SetBitmap(const char* pszBitmapFileName, int nAlpha, COLORREF cTransColor, RECT bitmapRect); BOOL SetBitmapParams(int nAlpha, COLORREF cTransColor, RECT bitmapRect); // 反射自窗口 BOOL Repaint(); BOOL Resize(); // 帮助程序 LPCTSTR GetLastError(); protected: // [...] };

为了方便,头文件和实现文件包含DirectShow包含,Direct3D包含,以及库的pragma指令。

步骤1:构建一个简单的播放器

构建一个非常简单的视频播放器相当容易:

包括VMR9Graph.h和VMR9Graph.cpp在项目中,

在应用程序中添加一个CVMR9Graph的实例,

提供一个窗口用于视频播放,

调用CVMR9Graph::SetMediaWindow(hMyVideoPlaybackHandle)来设置视频播放窗口,

调用CVMR9Graph::SetMediaFile(0, pszPathToMyFile)来设置要渲染的视频文件,

调用CVMR9Graph::RunGraph()来播放视频。

此时视频播放工作,但视频没有随着窗口调整大小...

步骤2:转发事件

应用程序必须告诉图表何时需要重新绘制或调整大小:

创建一个WM_SIZE消息的处理程序,并调用CVMR9Graph::Resize(),

创建一个WM_PAINT消息的处理程序,并调用CVMR9Graph::Repaint()。

可以注意到,默认情况下视频播放保留了长宽比。可以通过调用CVMR9Graph::PreserveAspectRatio(FALSE)来改变这一点。

好的,看起来好多了...是时候玩视频混合了。

步骤3:混合视频

多个文件播放由层处理。每个层播放一个视频,并支持一些属性,如排序、alpha混合、大小和位置。由多个层产生的视频称为组合,并采用最大媒体的大小。

插入的每个层都由它的层索引标识;CVMR9Graph允许玩10层,它的默认值是4层(VMR9默认)。

以下示例加载2个视频文件,并将第一个视频的alpha值设置为50%:

// 加载媒体文件 myGraph.SetMediaFile(0, "C:\\Video1.avi"); myGraph.SetMediaFile(1, "C:\\Video2.mpg"); // 设置视频1的alpha值 myGraph.SetAlphaLayer(0, 50);

alpha值可以实时设置,如演示应用程序所示。

注意1:没能混合两个DivX文件,但只能混合一个DivX和其他编解码器,如MPEG...不知道为什么...也许是硬件不足,因为DirectShow样本似乎也有同样的问题。

注意2:CVMR9Graph在图表中只添加了一个声音渲染器,所以只有第一个视频流有声音。可以通过调用CVMR9Graph::AddFilter(_T"Another Sound Renderer", CLSID_DSoundRender)来添加另一个声音渲染器。

在最近的计算机上看起来很酷...能添加更多吗?

步骤4:设置覆盖位图

CVMR9Graph中的覆盖位图加载到Direct3D表面。位图可以是GIF、JPEG、PNG、BMP、DIB、TGA或DDS格式。

要设置覆盖位图,请调用CVMR9Graph::SetBitmap(),参数如下:

一个位图文件路径,

一个alpha值(覆盖位图总是在组合中位于最顶层),

一个用于位图透明度的颜色键,

以及位图的大小和位置(请记住,视频/位图大小相对于组合大小)。

覆盖位图是一个很酷的功能,可以用来:

显示一个小的覆盖指示器,

为视频显示创建一个遮罩(也许有人会尝试用窗口区域。这可能很有趣)。

最后一步:走得更远

为了保持类的简单性,播放控制是最小的,但可以通过获取一些DirectShowCOM接口来做更多的事情:

IMediaEvent:提供图表的状态和事件。

CVMR9Graph自动向视频播放窗口发送WM_MEDIA_NOTIF消息;当这种情况发生时,调用IMediaEvent::GetEvent()来获取事件类型。

IMediaControl:提供对播放的控制,如暂停。

IMediaSeeking:提供对播放速率和位置的控制。

IBasicAudio:提供对声音渲染器的控制,如音量和平衡。

注意:使用接口后,必须通过调用TheInterface::Release()来释放它。

已知问题

当图表正在运行时,可以调用CVMR9Graph::Stop()或CVMR9Graph::SetMediaFile(),但似乎在某些情况下,尤其是在处理位图媒体文件时,组合无法正确运行...

调用CVMR9Graph::ResetGraph()会清理图表并构建一个新的实例。

在演示应用程序中调整视频窗口大小时,会有一些闪烁...这是因为它是一个MFC窗口,具有标准的OnEraseBackgnd()实现。微软的指导方针明确指出要绕过标准的背景绘制。

IVMRMonitorConfig9接口被检索但未使用。多监视器可能很好。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485