在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指令。
构建一个非常简单的视频播放器相当容易:
包括VMR9Graph.h和VMR9Graph.cpp在项目中,
在应用程序中添加一个CVMR9Graph的实例,
提供一个窗口用于视频播放,
调用CVMR9Graph::SetMediaWindow(hMyVideoPlaybackHandle)来设置视频播放窗口,
调用CVMR9Graph::SetMediaFile(0, pszPathToMyFile)来设置要渲染的视频文件,
调用CVMR9Graph::RunGraph()来播放视频。
此时视频播放工作,但视频没有随着窗口调整大小...
应用程序必须告诉图表何时需要重新绘制或调整大小:
创建一个WM_SIZE消息的处理程序,并调用CVMR9Graph::Resize(),
创建一个WM_PAINT消息的处理程序,并调用CVMR9Graph::Repaint()。
可以注意到,默认情况下视频播放保留了长宽比。可以通过调用CVMR9Graph::PreserveAspectRatio(FALSE)来改变这一点。
好的,看起来好多了...是时候玩视频混合了。
多个文件播放由层处理。每个层播放一个视频,并支持一些属性,如排序、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)来添加另一个声音渲染器。
在最近的计算机上看起来很酷...能添加更多吗?
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接口被检索但未使用。多监视器可能很好。