Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Wrapper class for playing video with the VMR9

0.00/5 (No votes)
10 Feb 2003 11  
Class for playing and mixing video files using DirectX9 Video Mixing Renderer.

Introduction

This article demonstrates a simple wrapper class for playing video files with the new DirectShow Video Mixing Renderer 9.

In DirectX9, multimedia applications can use a new video renderer to display decoded frames, but this renderer is not the default renderer for compatibility issue. On Windows XP, the default renderer is the VMR7, but on older Windows version it's the Video Renderer. The main difference is performance and overlay mixing capabilities: as older renderer use different versions of DirectDraw API (even older API for the Video Renderer), the VMR9 is based on DirectX Graphics, so it uses the Direct3D capability of your 3D video card. The result is an improved performance on recent 3D cards, better support of overlay mixing, compatibility with all Windows versions that support DirectX9, and some new capability such as de-interlacing and ProcAmp support (contrast, saturation, etc.).

So the new VMR9 looks great but it's not the default renderer, regardless of Windows version... We've to build it manually, and this is why I wrote this class.

What you need...

To run the demo, DirectX9 runtime has to be installed on your system, which must have a Direct3D compatible display adapter. To build, DirectX9 SDK must be installed on your system. The source code was created under Visual C++ 6 SP5.

All the tests I've made is on my WinXP box, running an ATI Radeon M7 (kind of Radeon 7500). As I can't check real compatibility with many other Windows releases and video cards, please try with your system and put a word in the forum.

Using the code

A first look

All of the DirectShow graph management and VMR routines are included in one class : 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:
    // [...]

};

For convenience, header and implementation files contains the DirectShow includes, Direct3D includes, and pragma directives for lib.

Step 1 : Building a simple player

Building a very simple video player is quite easy:

  1. Include VMR9Graph.h and VMR9Graph.cpp in your project,
  2. Add an instance of CVMR9Graph in your application,
  3. Provide a window for video playback,
  4. Call CVMR9Graph::SetMediaWindow(hMyVideoPlaybackHandle) to set the video playback window,
  5. Call CVMR9Graph::SetMediaFile(0, pszPathToMyFile) to set the video file to render,
  6. Call CVMR9Graph::RunGraph() to play video.

At this point video playback works but the video wasn't resized with your window...

Step 2 : Forwarding events

Your application have to tell to the graph when the video has to repaint or size:

  • Create a handler for WM_SIZE message and make a call to CVMR9Graph::Resize(),
  • Create a handler for WM_PAINT message and make a call to CVMR9Graph::Repaint()

You can notice that video playback preserves aspect ratio by default. You can change this by a call to CVMR9Graph::PreserveAspectRatio(FALSE).

Ok, that looks much better... time to play with video mixing.

Step 3: Mixing video

Multiple file playback was handled by layers. Each layer plays a video and supports several properties such as ordering, alpha blending, size and position. The video produced by multiple layers is called a composition, and takes the size of the biggest media.

Each layer you insert is identified by it's layer index; CVMR9Graph lets you play with 10 layers, with it's default value to 4 layers (VMR9 default).

The following example loads 2 video files and sets an alpha value of 50% to the first:

// 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 value can be set in real time, as show in the demo app.

Note 1: I have not been able to mix two DivX files, but only one DivX and other codecs such as MPEG... Don't know why... perhaps a hardware lack, since DirectShow samples seems to have the same troubles.

Note 2: The CVMR9Graph adds only one sound renderer in the graph, so only the first video stream has sound. You can add another sound renderer with a call to CVMR9Graph::AddFilter(_T"Another Sound Renderer", CLSID_DSoundRender).

Looks cool on a recent computers... Can we add more?

Step 4 : Setting an overlay bitmap

Overlay bitmap in CVMR9Graph is loaded in a Direct3D surface. The bitmap can be in GIF, JPEG, PNG, BMP, DIB, TGA, or DDS format.

To set an overlay bitmap, call CVMR9Graph::SetBitmap(), with the following parameters :

  • a bitmap file path,
  • an alpha value (overlay bitmap is always topmost in composition),
  • a color key for bitmap transparency,
  • and the bitmap size and position (keep in mind that video/bitmap size is relative to composition size).

Overlay bitmap is a cool feature that can be used to:

  • display a small overlay indicator,
  • create a mask for video display (perhaps someone will try it with a window region. This can be fun).

Last step: Go further away

To keep the class simple, playback control is minimal, but you can do more by getting some DirectShow COM interfaces:

  • IMediaEvent: gives the state and event of the graph. CVMR9Graph automatically sends a WM_MEDIA_NOTIF message to your video playback window; when this occurs, call IMediaEvent::GetEvent() to get the event type.
  • IMediaControl: provides control over the playback, such as pause.
  • IMediaSeeking: provides control over playback rate and position.
  • IBasicAudio: provides control over the sound renderer, such as volume and balance.

Note: After the use of an interface, you have to release it by a call to TheInterface::Release().

Known issue

When the graph is running, a call to CVMR9Graph::Stop() or CVMR9Graph::SetMediaFile() can be done, but it seems that in some cases composition can't run correctly after, particularly with bitmap media files...

A call to CVMR9Graph::ResetGraph() cleans up the graph and constructs a new fresh instance.

When resizing video window in demo app, there is some flickering... That's because it's a MFC window with the standard OnEraseBackgnd() implementation. Microsoft guidelines clearly indicate to bypass standard background paint.

// TODO : some improvements

The IVMRMonitorConfig9 interface is retrieved but not used. Multi monitor can be great.

There's no support for de-interlacing or ProcAmp control (ProcAmp don't work on my current ATI display drivers... Grrr)

There's no support for dynamic overlay bitmap... As a Direct3D surface can be locked and modified by a device context handle or even directly, this can be quite simple...don't know why I din't code it... probably a small lack of sleep.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here