direct show tutorial

13
CMPT365 Multimedia PA2 Tutorial Cheng Lu DirectShow For Media Playback In Windows (Note: complete source is available at http://www.cs.sfu.ca/CC/365/mark/material/work/dsmediaplayer .zip) 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.

Upload: hwapyeong-cho

Post on 07-Apr-2015

832 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Direct Show Tutorial

CMPT365 Multimedia PA2 Tutorial Cheng Lu

DirectShow For Media Playback In Windows

(Note: complete source is available at http://www.cs.sfu.ca/CC/365/mark/material/work/dsmediaplayer.zip)

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.

Page 2: Direct Show Tutorial

In the next dialog select “A typical ‘Hello World!’ application”.

Step 2: Create menu optionsSelect the menu tab in the workspace window.Add the menu items Open, Play and Stop as shown in the following figure.

Page 3: Direct Show Tutorial

Specify the ID’s of these items as follows

Menu Caption Menu ID

Open IDM_OPEN

Play IDM_PLAY

Stop IDM_STOP

Step3:

Open the project settings dialog.

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.

Page 4: Direct Show Tutorial

Step3:

Modify the code as follows. For sake of brevity only relevant portions of code are reproduced below.

Include the following header files

#include <commdlg.h>#include <dshow.h>

// Global Variables:

//Declare global variables HWND hWnd;IGraphBuilder *pGraph;IMediaControl *pMediaControl;IMediaEventEx *pEvent;IVideoWindow *pVidWindow;

//Buffer for storing media filename

Page 5: Direct Show Tutorial

char szMediaFileName[256]="";wchar_t szWMediaFileName[256];

//Media selection filterchar szFilterString[] = "All media files (*.avi, *.mpg, *.wav, *.mp3)\0*.avi;*.mpg;*.wav;*.mp3;*.mid\0";

Make the following additions to the WinMain function

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ // TODO: Place code here.

MSG msg;HACCEL hAccelTable;

CoInitialize(NULL);

//Create the DirectShow FilterGraph component and obtain its interfacesCoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,

IID_IGraphBuilder, (void **)&pGraph);

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;

}

Add the following to the InitInstance function

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){ . . .

::hWnd = hWnd;

ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);

return TRUE;}

Page 6: Direct Show Tutorial

Add the following to window procedure

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){

int wmId, wmEvent;PAINTSTRUCT ps;HDC hdc;TCHAR szHello[MAX_LOADSTRING];LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

//Initialize the openfilename structure to be used with GetOpenFileNameOPENFILENAME ofn;

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 unicodeMultiByteToWideChar(0, 0, szMediaFileName,

strlen(szMediaFileName), szWMediaFileName, 256);

//Null terminate the widechar stringszWMediaFileName[strlen(szMediaFileName)]=0;

pGraph->RenderFile(szWMediaFileName, NULL);//Set the video window properties

//Make the video window a child of our application windowpVidWindow->put_Owner((OAHWND)hWnd);

pVidWindow->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS);

//Get the dimensions of our client areaRECT rect;GetClientRect(hWnd, &rect);pVidWindow->SetWindowPosition(0, 0, rect.right,

rect.bottom);}break;

case IDM_PLAY:if(pMediaControl){pMediaControl->Run();}break;

Page 7: Direct Show Tutorial

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:

We begin with a brief digression on DirectShow components. DirectShow supports a number of components, which are useful in the creation of multimedia applications. Each of these components supports a number of interfaces. The interface of the component can be said to be the sum-total of all the interfaces supported by it.

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;

Page 8: Direct Show Tutorial

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:

IGraphBuilder - This is the most important interface. It provides facilities to create a graph for a specific file, and to add/remove filters individually from the graph.

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.

IVideoWindow –This interface is used for controlling DirectShow’s video display window.

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);

to initializes the COM library.

The next step is to create the Filter Graph Manager component.

In COM, components are identified by 128-bit numbers called “class identifiers” (CLSID). Luckily for us these numbers are defined as constants with a “CLSID_” prefix. The identifier for Filter Graph Manager is called CLSID_FilterGraph.

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

Page 9: Direct Show Tutorial

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);

This application is a windows application and responds to menus COMMAND messages. We have earlier defined menu command identifiers while creating the menus using the menu editor. Windows uses those identifiers to notify us about the command selected. We perform appropriate action like open, play or stop in the WM_COMMAND part of the Window Procedure.

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 IVideoWindow interface is used for controlling DirectShow’s video display window. Ordinarily, when asked to render a video file DirectShow pop up its own video window. We use the IVideoWindow interface in order to merge the DirectShow window with our applications window hierarchy. In this example we make the DirectShow window a child window of our application main window and resize it to fill the entire window client area. We do this by using the following lines of code.

//Make the video window a child of our application windowpVidWindow->put_Owner((OAHWND)hWnd);pVidWindow->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS);

//Get the dimensions of our client area

Page 10: Direct Show Tutorial

RECT rect;GetClientRect(hWnd, &rect);pVidWindow->SetWindowPosition(0, 0, rect.right, rect.bottom);

The line

pMediaControl->Run();

plays the media file by running all the filters in the filter graph, and the line PMediaControl->Stop();

stops the playback.

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.

The source code is available at location at top of this file.

Go ahead and try it out.