Version 2.0!
Features
Tutorials
Files
Glossary
Projects
Contact
Links
Message Board
Extras
LuckyCam
Old News
Sign Guestbook
View Guestbook
VB Horoscope
VB Photo Album
.
ATTENTION READERS! Lucky's VB Gaming Site is no longer active. For updated game programming information and tutorials, please visit The Game Programming Wiki!

DirectShow Introduction / Simple Playback


What is DirectShow?

Active Movie was the original name for DirectShow. Now that DirectShow has become a part of DirectX, since version 8, the term ActiveMovie is no longer used. However the type library we are going to use is called "ActiveMovie control type library" (QUARTZ.DLL).

DirectShow is an interface for high-quality and high-speed playback and capture of multimedia stream files. It supports a large set of formats, including ASF, MPEG, AVI, MP3, and WAV files. DirectShow is integrated with other DirectX technologies, and automatically detects and uses video and audio acceleration hardware when available, but also supports systems without acceleration hardware.


DirectShow initialization

First go to the project - reference menu and check the "ActiveMovie control type library".

The next step is to declare the needed objects. Place this code in your applications declaration:

Private m_objBasicAudio As IBasicAudio
Private m_objBasicVideo As IBasicVideo
Private m_objMediaEvent As IMediaEvent
Private m_objVideoWindow As IVideoWindow
Private m_objMediaControl As IMediaControl
Private m_objMediaPosition As IMediaPosition

OK... before we can create instances of these objects to play any media file, we have to remove the old object instances! Here's a handy procedure to clean up our objects:

Sub RemoveDShow()
    On Local Error GoTo RemoveDShowError
    
    'If a MediaControl instance exists, then stop it from playing
    If ObjPtr(m_objMediaControl) > 0 Then
        m_objMediaControl.Stop
    End If
    'If a VideoWindow instance exists, then remove the link to the render target
    If ObjPtr(m_objVideoWindow) > 0 Then
        m_objVideoWindow.Owner = 0
    End If
    'Destroy all objects
    If ObjPtr(m_objBasicAudio) > 0 Then Set m_objBasicAudio = Nothing
    If ObjPtr(m_objBasicVideo) > 0 Then Set m_objBasicVideo = Nothing
    If ObjPtr(m_objMediaControl) > 0 Then Set m_objMediaControl = Nothing
    If ObjPtr(m_objVideoWindow) > 0 Then Set m_objVideoWindow = Nothing
    If ObjPtr(m_objMediaPosition) > 0 Then Set m_objMediaPosition = Nothing
    Exit Sub
RemoveDShowError:
    Err.Clear
    Exit Sub
End Sub

Now we are ready for opening a media file. It works like this:

Sub OpenDShowFile(Filename As String)
    On Local Error GoTo OpenFileError
    Set m_objMediaControl = New FilgraphManager
    Call m_objMediaControl.RenderFile(Filename)
    
    Set m_objBasicAudio = m_objMediaControl
    m_objBasicAudio.Volume = 0 'Loudest
    m_objBasicAudio.Balance = 0 'Centered
    
    Set m_objVideoWindow = m_objMediaControl
    m_objVideoWindow.WindowStyle = WS_VISIBLE 'WS_VISIBLE = &H10000000
    m_objVideoWindow.Top = 0
    m_objVideoWindow.Left = 0
    m_objVideoWindow.Width = picTarget.Width
    m_objVideoWindow.Height = picTarget.Height
    m_objVideoWindow.Owner = picTarget.hWnd
    
    Set m_objMediaEvent = m_objMediaControl
    
    Set m_objMediaPosition = m_objMediaControl
    m_objMediaPosition.Rate = 1 'Normal forward playback speed
    
    Exit Sub
OpenFileError:
    Err.Clear
    Resume Next
End Sub

Before I begin to describe what's happening here, you might be wondering, why we just execute the next command if any errors occur. Well, that's simple: Imagine you want to open an Audio file. Unfortunately Audio files don't contain Video data (at least my audio files don't do). So DirectShow returns an error, when we try to initialize the VideoWindow object.

First we create an instance of the FilterGraph object for the requested file format. A filter graph determines how to process media files opened with DirectShow.

Then we create and initialize our BasicAudio object, and set the initial volume and balance. These three lines may also cause an error. For example if you open a video file that contains no audio data.

In our next step we create an instance of the VideoWindow object, that is needed for visualization of motion picture files. As mentioned before it is possible, that these lines cause error, due to the contents of the source file! We set the WindowStyle property of the VideoWindow object to WS_VISIBLE. All available WindowStyle constants can be found at the bottom of this document, and in the API Text viewer files, that shipped with Visual Basic. The Top, Left, Width, and Height properties determine the size of the output window. Then we have to assign an owner. That's our render target. In this case, I have used a simple PictureBox control, and pass its WindowHandle.

Now we initialize the MediaEvent object, that we need to control our media playback. We're going to use this object to get the playback status later.

The last object to initialize is the MediaPosition object. The MediaPosition object enables us to get and set the current position, the playback speed (rate) and to get the duration of our media file. In the code above we set the speed to 1, which is normal playback speed. Higher values cause DirectShow to play the file faster, lower will play it slower.

Now we're finished with initialization.


Playing the file

We'll start with playing our loaded file:

If CLng(m_objMediaPosition.CurrentPosition) = CLng(m_objMediaPosition.Duration) Then
    m_objMediaPosition.CurrentPosition = 0
End If
Call m_objMediaControl.Run

It's as easy as it looks like! :) First we check if the current position is at the end. If so, then reset it to the start. Then we just call the run method of the MediaControl object, and we're fine!


Pausing playback

To pause the playback simply call the pause method of the MediaControl object:

Call m_objMediaControl.Pause


Stopping playback

Sometimes silly users want to stop the playback, and want to restart from the beginning. Well, then we'll let them do so. :)

Call m_objMediaControl.Stop
m_objMediaPosition.CurrentPosition = 0


Seek forward and backward

Another simple feature a potential user might want to use. :) We will achieve this by altering the CurrentPosition property of the MediaPosition object. This WILL cause the filter graph to pause the media file, set the new position, and then resume playing the file. But this method has proven to be fast enough for real-time use.

Public Sub SeekPosition(Amount As Double)
    If m_objMediaPosition.CurrentPosition + Amount < 0 Then
       m_objMediaPosition.CurrentPosition = 0
    ElseIf m_objMediaPosition.CurrentPosition + Amount > _
       m_objMediaPosition.Duration Then
        m_objMediaPosition.CurrentPosition = m_objMediaPosition.Duration
    Else
        m_objMediaPosition.CurrentPosition = m_objMediaPosition.CurrentPosition + Amount
    End If
End Sub

Simply pass a negative value for seeking backwards. Now that we give such a powerful tool to the user, we have to watch his inputs closely! :) If he wants to seek a position before the start, or behind the end, then we'll have to correct his mistakes.

The MediaEvent object

Syntax:

m_objMediaEvent.WaitForCompletion msTimeout, EvCode

We'll only use the WaitForCompletion method of this object. This method accepts two variables as arguments. The first determines when the method should give back control to us, and the second argument will be set by the function itself, and contains the event completion code. If you pass 0 or a higher value as msTimeout the method will return when the timelimit is reached (0 = immediately) or the end of file is reached, which ever comes first. If the timelimit was reached, then EvCode will be 0 (zero) and the run-time error 287 will be raised ("Application-defined or object-defined error"). You can handle this by using:

On Error Resume Next

So for anything else then -1 passed as msTimeout, you have to resume on the next line, and check the EvCode. 0 (zero) means DirectShow is still playing, 1 or anything higher means that the file end has been reached. If you pass -1 as msTimeout, then DirectShow will give back control to you as soon as the file end is reached. But in the meantime your application does not react on user input!


That's it!

That's all the basic stuff you need to know! With this code you are able to play every file, that you can play with your standard player! If you want to play a file, that doesn't use one of DirectShows native file formats, you need to install the proper codec (COder/DECoder) for it.

I also created a sample app that uses the code shown above. Download it here.

I hope you enjoyed our time together. 'Til next time! 

Have fun coding cool multimedia games and apps using DirectShow

-Paladin




Appendix

The WindowStyle constants... I guess you don't need more than the WS_VISIBLE, but I listed them all:

Public Const WS_BORDER = &H800000
Public Const WS_CAPTION = &HC00000 
Public Const WS_CHILD = &H40000000
Public Const WS_CHILDWINDOW = (WS_CHILD)
Public Const WS_CLIPCHILDREN = &H2000000
Public Const WS_CLIPSIBLINGS = &H4000000
Public Const WS_DISABLED = &H8000000
Public Const WS_DLGFRAME = &H400000
Public Const WS_GROUP = &H20000
Public Const WS_HSCROLL = &H100000
Public Const WS_ICONIC = WS_MINIMIZE
Public Const WS_MAXIMIZE = &H1000000
Public Const WS_MAXIMIZEBOX = &H10000
Public Const WS_MINIMIZE = &H20000000
Public Const WS_MINIMIZEBOX = &H20000
Public Const WS_OVERLAPPED = &H0&
Public Const WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED Or WS_CAPTION Or _
                                                                       WS_SYSMENU Or WS_THICKFRAME Or _
                                                                       WS_MINIMIZEBOX Or WS_MAXIMIZEBOX)
Public Const WS_POPUP = &H80000000
Public Const WS_POPUPWINDOW = (WS_POPUP Or WS_BORDER Or WS_SYSMENU)
Public Const WS_SIZEBOX = WS_THICKFRAME
Public Const WS_SYSMENU = &H80000
Public Const WS_TABSTOP = &H10000
Public Const WS_THICKFRAME = &H40000
Public Const WS_TILED = WS_OVERLAPPED
Public Const WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW
Public Const WS_VISIBLE = &H10000000
Public Const WS_VSCROLL = &H200000