65.9K
CodeProject 正在变化。 阅读更多。
Home

用于使用 VMR9 播放视频的包装类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (37投票s)

2003年2月11日

CPOL

5分钟阅读

viewsIcon

372958

downloadIcon

10563

使用 DirectX9 Video Mixing Renderer 播放和混合视频文件的类。

引言

本文演示了一个简单的封装类,用于使用新的 DirectShow Video Mixing Renderer 9 播放视频文件。

在 DirectX9 中,多媒体应用程序可以使用新的视频渲染器来显示解码后的帧,但出于兼容性考虑,该渲染器并非默认渲染器。在 Windows XP 上,默认渲染器是 VMR7,而在较旧的 Windows 版本中,则是 Video Renderer。主要区别在于性能和覆盖混合功能:由于旧渲染器使用不同版本的 DirectDraw API(Video Renderer 使用的 API 更旧),而 VMR9 基于 DirectX Graphics,因此它利用了 3D 显卡的 Direct3D 功能。结果是,在较新的 3D 显卡上性能得到提升,覆盖混合支持更好,兼容所有支持 DirectX9 的 Windows 版本,并支持去隔行和 ProcAmp(对比度、饱和度等)等新功能。

因此,新的 VMR9 看起来很棒,但它不是默认渲染器,无论 Windows 版本如何……我们必须手动构建它,这就是我编写这个类的原因。

你需要什么...

要运行演示,你的系统必须安装 DirectX9 运行时,并且需要有 Direct3D 兼容的显示适配器。要进行编译,你的系统必须安装 DirectX9 SDK。源代码是在 Visual C++ 6 SP5 下创建的。

我进行的所有测试都在我的 WinXP 机器上完成,运行的是 ATI Radeon M7(类似于 Radeon 7500)。由于我无法检查与许多其他 Windows 版本和显卡之间的实际兼容性,请在你的系统上进行测试,并在论坛上发表评论。

使用代码

初窥

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

class CVMR9Graph  
{
    // Constructor / destructor
public:
    CVMR9Graph();
    CVMR9Graph(HWND MediaWindow, 
             int NumberOfStream = 4);
    virtual ~CVMR9Graph();

    // Methods
public:
    // Graph configuration
    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);

    // Graph control
    BOOL PlayGraph();
    BOOL StopGraph();
    BOOL ResetGraph();
    IMediaEvent* GetPtrMediaEvent();
    IMediaControl* GetPtrMediaControl();
    IMediaSeeking* GetPtrMediaSeeking();
    IBasicAudio* GetPtrBasicAudio();


    // Layer control
    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);

    // Bitmap control
    BOOL SetBitmap(const char* pszBitmapFileName, 
      int nAlpha, COLORREF cTransColor, RECT bitmapRect);
    BOOL SetBitmapParams(int nAlpha, 
      COLORREF cTransColor, RECT bitmapRect);

    // Reflected from window
    BOOL Repaint();
    BOOL Resize();

    // helper
    LPCTSTR GetLastError();

protected:
    // [...]
};

为了方便起见,头文件和实现文件包含 DirectShow 包含文件、Direct3D 包含文件以及用于 libpragma 指令。

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

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

  1. VMR9Graph.hVMR9Graph.cpp 包含到你的项目中,
  2. 在你的应用程序中添加 CVMR9Graph 的一个实例,
  3. 提供一个用于播放视频的窗口,
  4. 调用 CVMR9Graph::SetMediaWindow(hMyVideoPlaybackHandle) 来设置视频播放窗口,
  5. 调用 CVMR9Graph::SetMediaFile(0, pszPathToMyFile) 来设置要渲染的视频文件,
  6. 调用 CVMR9Graph::RunGraph() 来播放视频。

此时视频播放可以工作,但视频没有随你的窗口缩放...

第二步:转发事件

你的应用程序必须告诉图控件何时需要重绘或调整视频大小

  • 创建一个 WM_SIZE 消息的处理器,并调用 CVMR9Graph::Resize()
  • 创建一个 WM_PAINT 消息的处理器,并调用 CVMR9Graph::Repaint()

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

好了,这样看起来好多了……该玩玩视频混合了。

第三步:混合视频

多个文件播放通过图层来处理。每个图层播放一个视频,并支持多种属性,如顺序、Alpha 混合、大小和位置。多个图层产生的视频称为合成,其大小与最大的媒体相同。

你插入的每个图层都由其图层索引标识;CVMR9Graph 允许你使用 10 个图层,默认值为 4 个图层(VMR9 默认)。

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

// load media files
myGraph.SetMediaFile(0, "C:\\Video1.avi");
myGraph.SetMediaFile(1, "C:\\Video2.mpg");

// set alpha value to video1
myGraph.SetAlphaLayer(0, 50);

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

注意 1: 我未能混合两个 DivX 文件,只能混合一个 DivX 和其他编解码器,如 MPEG……我不知道原因……可能是硬件不足,因为 DirectShow 示例似乎也有同样的麻烦。

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

在较新的计算机上看起来很酷……我们还能添加更多吗?

第四步:设置覆盖位图

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

要设置覆盖位图,请调用 CVMR9Graph::SetBitmap(),并附带以下参数:

  • 一个位图文件路径,
  • 一个 Alpha 值(覆盖位图始终位于合成的最顶层),
  • 用于位图透明度的颜色键,
  • 以及位图的大小和位置(请记住,视频/位图大小是相对于合成大小的)。

覆盖位图是一项很棒的功能,可用于

  • 显示一个小的覆盖指示器,
  • 创建视频显示的遮罩(也许有人会尝试用窗口区域来使用它。这可能很有趣)。

最后一步:更进一步

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

  • IMediaEvent:提供图的状态和事件。CVMR9Graph 自动向你的视频播放窗口发送一个 WM_MEDIA_NOTIF 消息;当这种情况发生时,调用 IMediaEvent::GetEvent() 来获取事件类型。
  • IMediaControl:提供对播放的控制,例如暂停。
  • IMediaSeeking:提供对播放速率和位置的控制。
  • IBasicAudio:提供对声音渲染器的控制,例如音量和平衡。

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

已知问题

当图正在运行时,可以调用 CVMR9Graph::Stop()CVMR9Graph::SetMediaFile(),但在某些情况下,之后合成可能无法正确运行,特别是对于位图媒体文件...

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

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

// TODO:一些改进

IVMRMonitorConfig9 接口已检索但未使用。多显示器可以很棒。

不支持去隔行或 ProcAmp 控制(ProAmp 在我当前的 ATI 显示驱动程序上不起作用……哼!)

不支持动态覆盖位图……由于 Direct3D surface 可以通过设备上下文句柄或直接锁定和修改,这可能相当简单……我不知道为什么我没有写它……可能是因为缺少睡眠。

© . All rights reserved.