Direct Show Tutorial
Direct Show Tutorial
A few years ago, Microsoft introduced a media-streaming layer on top of DirectX, called
DirectShow, that was meant to handle virtually any type of media.
This tutorial walks you through the creation of a DirectShow application supporting the
windows GUI. The application allows you to select media files for playback using the
standard windows file open box. It allows you to start and stop media playback using
windows menu commands.
Step1:
Create a new project named DSMediaPlayer.
Select Win32 Application as your project type.
Click “OK” to continue.
In the next dialog select “A typical ‘Hello World!’ application”.
Step3:
Select the link tab and select the General Category. Enter the link library strmbasd.lib at
the end in the Object/library modules edit box.
Step3:
Modify the code as follows. For sake of brevity only relevant portions of code are
reproduced below.
#include <commdlg.h>
#include <dshow.h>
// Global Variables:
CoInitialize(NULL);
pGraph->QueryInterface(IID_IMediaControl, (void**)&pMediaControl);
pGraph->QueryInterface(IID_IMediaEventEx, (void**)&pEvent);
pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVidWindow);
.
.
.
pMediaControl->Release();
pEvent->Release();
pVidWindow->Release();
pGraph->Release();
CoUninitialize();
return msg.wParam;
}
::hWnd = hWnd;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
memset(&ofn, 0, sizeof(OPENFILENAME));
ofn.lpstrFile = szMediaFileName;
ofn.nMaxFile = 256;
ofn.lpstrFilter = szFilterString;
ofn.lpstrDefExt = "*.avi";
ofn.lStructSize = sizeof(OPENFILENAME);
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_OPEN:
if(GetOpenFileName(&ofn) == IDOK)
{
//Convert filename from ascii to wide char unicode
MultiByteToWideChar(0, 0, szMediaFileName,
strlen(szMediaFileName), szWMediaFileName, 256);
pGraph->RenderFile(szWMediaFileName, NULL);
//Set the video window properties
case IDM_PLAY:
if(pMediaControl)
{
pMediaControl->Run();
}
break;
case IDM_STOP:
if(pMediaControl)
{
pMediaControl->Stop();
}
break;
.
.
.
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_SIZE:
pVidWindow->SetWindowPosition(0, 0, LOWORD(lParam), HIWORD(lParam));
break;
case WM_PAINT:
Explanation:
The directive
#include <dshow.h>
includes the DirectShow header file, bringing in all the interface and component
declarations.
The lines
IGraphBuilder *pGraph;
IMediaControl *pMediaControl;
IMediaEventEx *pEvent;
IVideoWindow *pVidWindow;
declare variables of interfaces. In this example we work with 4 interfaces of the Filter
Graph Manager class.
IGraphBuilder;
IMediaControl;
IMediaEventEx;
IVideoWindow;
The basic DirectX facilities, such as DirectDraw, DirectSound, and Direct3D, for the
most part are accessed through one interface, IDirectDraw, IDirectSound, and IDirect3D
respectively. DirectShow is a little more complex than this. In a typical application using
DirectShow, you may have 3 or more interfaces you need in order to control your filter
graph. Here's a list of the most common interfaces, which we will be using:
IMediaControl - This interface provides us with methods to start and stop the filter
graph.
IMediaEventEx - This interface is used for setting up notifications that will be sent to
the app when things happen in the graph (reach end of media, buffer under runs, etc.)
IMediaPosition - This interface will let us seek to certain points in our media, let us set
playback rates and other useful bits.
All these interfaces are implemented by the “Filter Graph Manager” component, which is
provided by DirectShow.
We need to initialize the COM library before we can use any COM components. Direct
Show library is a collection of COM components.
We use
CoInitialize(NULL);
Similarly, interface identifiers are also identified by 128-bit numbers called “interface
identifiers” (IID). These identifiers posses a “IID_” prefix.
COM components implement one or more interfaces. While creating the component, we
also need to specify which “one” out of these interfaces we are interested in at the
moment, for eg. IID_IGraphBuilder.
We use the COM library function CoCreateInstance to create the Filter Graph
Component as follows
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
Note that the last parameter is the address to our pGraph variable. In effect we are
passing a pointer to a pointer. This is required in order to receive the interface pointer.
CoCreateInstance creates an instance of the Filter Graph Manager and returns a pointer to
the IGraphBuilder on it.
Next we obtain pointers to the Media Control and Media Event interfaces implemented
by the same instance of the Filter Graph Manager. We use the COM Library’s
QueryInterface function for this purpose. Note that we identify the required interfaces
using IID_.
pGraph->QueryInterface(IID_IMediaControl, (void**)&pMediaControl);
pGraph->QueryInterface(IID_IMediaEventEx, (void**)&pEvent);
pGraph->QueryInterface(IID_IVideoWindow, (void**)&pVidWindow);
In this tutorial we let the user specify the media file to be played. We use windows
GetOpenFileName function to pop-up the standard file-open dialog to assist us in getting
user input. It has only one parameter-a pointer to OPENFILENAME structure that needs
to be filled as shown in program.
Note that RenderFile only accepts WCHAR strings, so we have to convert if we are using
ANSI char strings. We use MultiByteToWideChar to maps a character string to a wide-
character string.
Once we know what file we wish to play, we instruct the Filter Graph Manager to create
a default filter graph using the IGraphBuilder::Render function
The line
pMediaControl->Run();
plays the media file by running all the filters in the filter graph, and the line
PMediaControl->Stop();
Finally, we release all interfaces we have acquired on the Filter Graph Manager. This is
conceptually equivalent to calling delete in C++. This is a required step.
pMediaControl->Release();
pEvent->Release();
pVidWindow->Release();
pGraph->Release();
Before returning from main, we need to signal to the COM library that we are done and
uninitialize it:
CoUninitialize();
You are now ready to build and run your program. Note this particular application can
play only one file at a time. It needs to be restarted for playing a second media file.