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

Problems in the AudioVideoPlayback namespace of managed DirectX9

0.00/5 (No votes)
13 Sep 2004 1  
A guided tour of the many reasons NOT to use DirectX9 for your audio/video playback needs

Introduction

With DirectX 9, it's finally possible to use DirectX from C# without having to use interop. This includes the AudioVideoPlayback namespace, which as the name would imply, is used to play audio and video files. It comprises largely of two objects, an Audio class, and a Video class (which contains an Audio property). The interface looks pretty simple, and so you'd be forgiven for quickly deciding that writing a video player would be as easy as this:

if (browseForFile)
{
    if (video == null)
        video = new Video(filePath);    
    else
        video.Open(filePath);    
    video.Owner = videoPanel;
    video.Size = videoPanel.Size;
    video.Ending +=new EventHandler(video_Ending);
    video.Play();
}

Unfortunately, almost every line of this code is broken. The point of this article is to explain the pitfalls of the AudioVideoPlayback in DirectX 9, and the ways that I found to work around them. I'll do this line by line, turning the above example into something that reliably works.

The constructor

The first one is the worst one, in a lot of ways. The Video class offers an Open method, it does not offer a parameterless constructor. In other words, you must build a Video object out of a file, and then you have a method whereby you can open other videos with the same object. It leaks memory. Like crazy. In order to reuse a Video object, you need to first delete it, like this:

if (video != null)
{
    video.Stop();
    video.Dispose();
    video = null;
}
video = new Video(filePath);

Yes, you need to force destruction of the Video object, and create a new one. I have no idea why, or how this can be adequately explained (especially given the great fuss that Microsoft made over code now being 'managed'). I certainly urge you not to ever use the Open method.

The owner and the size

Setting the owner of the video is fine, not too much can go wrong, so long as you get it right. I got some really weird behavior during development, but nothing that didn't go away when I sorted out my code. The size is a different story. You can set the size of a video and it will play in that size fine. But once the video ends, the control it is playing on will resize itself to the native resolution of the video. To defeat this, you need to set an OnSize event handler in the control that the video plays on, and manually resize the control to the desired size, which can be stored as a property or calculated for a resizable window, or simply write code that stops a window from ever shrinking if your app is topmost and full screen (so the screen res is never changed, and the app is never resized). In the case of the sample app, it looks like this:

private void videoPanel_SizeChanged(object sender, EventArgs e)
{
    videoPanel.Size.Width = this.Size.Width - 10;
    videoPanel.Size.Height = this.Size.Height - 10;
}

Catching the end of play

When I saw that the Video object has an event handler for the end of a file, I thought that seemed pretty logical and useful. It is, and sometimes, it even gets called. The Audio object event always seems to fire, unless it's the Audio property on a Video object, but video just does not consistently work. So I set up a separate timer, to try and work out for myself what was going on, something like:

private void videoTimer(object sender, EventArgs e)
{
    if (!video.Playing)
        OnVideoEnd(sender, e);
}

Unfortunately, even when a video stops, the Playing property remains 'true'. So instead, I had to do this:

private void videoTimer(object sender, EventArgs e)
{
    if (video.CurrentPosition >= video.Duration)
        OnVideoEnd(sender, e);
}

The CurrentPosition always seems to go past the Duration, even though the Playing property remains true, and the Ending event often does not fire.

How loud is my audio?

The Audio property of a Video contains all the same methods as the Audio class, but they work on the audio contained in a video clip. For my application, I needed to play the clip on the second monitor, and provide a preview on the main monitor. So I decided to play the video twice, and turn the sound off on one of them. The Audio class has a Volume property, an integer. I tried setting this to 0, and nothing seemed to happen. I read some docs, which said that off was 10000, and on is 0, so I tried that and it blew up. Finally, I found another snippet in the MSDN which told me (correctly) that off is -10000. I'm sure there is some esoteric reason why the property is called Volume, and 0 is full on, and -10000 (why not -100000, or -94567547???) is off. Nevertheless, that's how it works.

More than video?

If you want to use a panel to display still images and video, this may work, so long as you destroy the video object first. I had no luck though, the last frame of video always stayed, and I could not draw over it. If you create a custom control which encapsulates bringing forward a Panel for video, or a PictureBox for stills, that won't work either. Putting a PictureBox and a Panel on your main form and calling BringToFront on whichever one you are using works fine though.

Two videos at once?

My next problem was the showstopper. My application runs in two screens, with one screen showing full screen video, and the other showing a thumbnail. The only way I could see to do this was to play two videos. The screen with a thumbnail also has a custom control which shows a list of available media. As soon as this control is used, the main video screen would freeze. A bit of experimentation established that no matter what size your two video objects were, the biggest one freezes when there are paint messages about. The client was not happy.....

A wrapper class?

I fully intended to provide a wrapper class that turns the Video object into something that behaves in a rational manner, so people could use that instead, but then I heard from someone else that they were having trouble playing 320 kbps MP3s, and getting the same callback problems with audio that I had with video. I have tested mostly on video, given that it seems a superset of audio in terms of what it does, so with a week to go before delivery, I gutted my project and replaced DX9 with the Windows Media Player control. Yes, the code is not as pretty, but guess what? Once I worked out the messages it sends me (and sends me every time, may I add), I suddenly found that everything worked for the first time. Not only that, but I can play two big videos with no problems at all. The only trick is to stop the players from autostarting, so both can load their copy of the video before asking both to play it. Unless you have a very specific reason to use DirectX9, I totally recommend you forget it, and play audio and video in C# using the Media Player control. The sample in the SDK gives you everything you need to work it all out, but if a lot of people ask for a tutorial, I'll write one.

Where from here?

The first thing I did when taking on this job was order a book from Amazon on Managed DirectX 9. Do not buy this book if you want info on playing video and audio. You've got a lot more info in this article than the book provides. There's only one bit of info I have not covered that the book explains, and it's this. The AudioVideoPlayback functionally in the C# portion of DirectX 9 is not meant to be complete, but just enough to allow simple playback of video files. There are no plans to update/improve/add to this functionality in Managed DirectX. Enough said, in my opinion. I'm just not sure why they bothered.

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