Configuration of Visual Studio .NET for DirectShow Programming

Configuration of Visual Studio .NET for DirectShow Programming

The development environment used in this book for DirectShow applications is the Microsoft Visual Studio .NET integrated development environment. The Visual C++ and Visual Basic programming languages are included in Visual Studio .NET, and they provide the raw platform for the creation of Windows applications.

Beyond Visual Studio .NET, the DirectX 9.0 Software Development Kit (SDK) is an essential element in the creation of DirectShow applications. The DirectX 9.0 SDK contains all the source files, headers, and libraries (along with a lot of helpful documentation) that will need to be linked with your own source code to create a functional DirectShow application.

If you already have Visual Studio .NET installed on the computer you ll be using for DirectShow application development, you might need to check whether the correct DirectX 9.0 SDK directories are in the include paths for the Visual C++ compiler and linker. (You ll know pretty quickly if your environment hasn t been set up correctly because your applications will generate errors during the compile or linking phases of program generation.)

To inspect the settings for your projects, open the Property Pages dialog box for your Visual C++ project. In the C/C++ folder, examine the value of the field labeled Additional Include Directories. The file path for the DirectX 9.0 SDK include files should be the first value in that field.

After you ve ensured that the DirectX 9.0 SDK include files are available to the compiler, click on the folder labeled Linker and examine the value of the field Additional Dependencies. Here you should find a file path that points to the DirectX 9.0 SDK object libraries. If you don t, add the file path to the list of other file paths (if any) in the field.

At this point, Visual Studio .NET is ready for your programming projects. To test it, open the project DSRender (on the CD-ROM) and try to build it. If it compiles and executes without errors, everything has been set up correctly. If you have problems, ensure that the DirectX 9.0 SDK has been installed correctly.

Now, with all these important essentials out of the way, let s take a look at DSRender, our first peek at a DirectShow application program. Like many of the other projects presented in this book, it s designed for console-mode operation. This means that many of the Windows API calls that deal with the particulars of the graphical user interface windows, menus, dialog boxes, and the like have been left out of the project, leaving only the meat of the DirectShow application. The code samples provided with this book are designed to become the kernels of your own DirectShow applications, so the code is clean and uncluttered by the requirements of a fully loaded Windows application.

DSRender: A DirectShow Media Player in C++

DSRender, one of the simplest DirectShow applications to write, would be among the most difficult to engineer without DirectShow. The application s action is straightforward: it displays an Open File dialog box, allows the user to select a file, and then attempts to render that file on the user s computer. The media type doesn t matter it could be an AVI movie, a WAV sound, Windows Media, an MP3 song, or an MPEG movie. As long as there are DirectShow filters to handle the specifics of the file format, the file will be rendered.

Examining main

The source code file DSRender.cpp has only three functions, including the standard C/C++ function main, the entry point for the program. It s the only function of interest to us, so here it is in all its (brief) glory:

// DSRender.cpp // A very simple program to render media files using DirectShow // int main(int argc, char* argv[]) { IGraphBuilder *pGraph = NULL; // Graph builder interface IMediaControl *pControl = NULL; // Media control interface IMediaEvent *pEvent = NULL; // Media event interface if (!GetMediaFileName()) { // Local function to get a file name return(0); // If we didn't get it, exit } // Initialize the COM library. HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { // We'll send our error messages to the console. printf("ERROR - Could not initialize COM library"); return hr; } // Create the Filter Graph Manager and query for interfaces. hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph); if (FAILED(hr)) // FAILED is a macro that tests the return value { printf("ERROR - Could not create the Filter Graph Manager."); return hr; } // Use IGraphBuilder::QueryInterface (inherited from IUnknown) // to get the IMediaControl interface. hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl); if (FAILED(hr)) { printf("ERROR - Could not obtain the Media Control interface."); pGraph->Release(); // Clean up after ourselves pGraph = NULL; CoUninitialize(); // And uninitialize COM return hr; } // And get the Media Event interface, too. hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent); if (FAILED(hr)) { printf("ERROR - Could not obtain the Media Event interface."); pGraph->Release(); // Clean up after ourselves pControl->Release(); CoUninitialize(); // And uninitialize COM return hr; } // To build the filter graph, only one call is required. // We make the RenderFile call to the Filter Graph Manager // to which we pass the name of the media file. #ifndef UNICODE WCHAR wFileName[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, g_PathFileName, -1, wFileName, MAX_PATH); // This is all that's required to create a filter graph // that will render a media file! hr = pGraph->RenderFile((LPCWSTR)wFileName, NULL); #else hr = pGraph->RenderFile((LPCWSTR)g_PathFileName, NULL); #endif if (SUCCEEDED(hr)) { // Run the graph. hr = pControl->Run(); if (SUCCEEDED(hr)) { // Wait for completion. long evCode; pEvent->WaitForCompletion(INFINITE, &evCode); // Note: Do not use INFINITE in a real application // because it can block indefinitely. } // And stop the filter graph. hr = pControl->Stop(); // Before we finish, save the filter graph to a file. SaveGraphFile(pGraph, L"C:\\MyGraph.GRF"); } // Now release everything and clean up. pControl->Release(); pEvent->Release(); pGraph->Release(); CoUninitialize(); return 0; }

Understanding DSRender Line by Line

We ve walked through much of the code in the section COM Basics earlier in this chapter. The application enters at main, sets up storage for a few variables (pointers to COM objects), and gets a file name with a call to the local function GetMediaFileName (peek at the source code if you need to see the details of that basic Windows function), and then initializes COM with a call to CoInitialize.

If all of this has proceeded successfully (and it should), the application next instantiates a Filter Graph Manager object with a call to CoCreateInstance, and obtains in that same call the IGraphBuilder interface on that object, which provides methods that allow you to build a filter graph. Once the IGraphBuilder interface has been obtained (if this fails, this might indicate problems with DirectX or the operating system), two QueryInterface method calls are made to retrieve additional interfaces that are exposed by the Filter Graph Manager. The first of these calls returns an IMediaControl interface, which has methods for changing the execution state of the filter graph, as explained previously. The second of these calls requests an IMediaEvent object. The IMedia Event interface provides a way for the filter graph to signal its own state changes to the DirectShow application. In this case, IMediaEvent will be used to track the progress of media playback, and it will pause execution of the application until playback is done. (For operating system geeks: this is possible because the DirectShow filters execute in a different thread from the DirectShow application.)

Now some magic happens. With just a single line of code, the entire filter graph is built. When the IGraphBuilder method RenderFile is invoked (with the name of the media file), the Filter Graph Manager object examines the media file s type and determines the appropriate set of filters source, transform, and renderer that need to be added to the filter graph. These filters are added to the filter graph and then connected together. If RenderFile returns without errors, DirectShow found a path from source to renderer. If the call to RenderFile fails, DirectShow lacked the filters to play the media file or perhaps the file was corrupted.

With the filter graph built, a one-line call to the IMediaControl interface invoking its Run method begins execution of the filter graph. Although the filter graph begins executing, the Run method returns immediately because the data streaming code is running in a separate thread that has been started by the source filter. Media file playback commences. If the media file is a movie, a playback window will open on the display; if it s a sound file, there won t be any visible sign of playback, but sounds should start coming from the computer s speakers. Figure 3-1 shows an AVI file being played.

figure 3-1 dsrender playing the avi file sunset.avi

Figure 3-1. DSRender playing the AVI file Sunset.avi

This application, as written, needs to pause during playback of the media file. If it didn t, the application would terminate just after the filter graph had started playback, and that wouldn t be very useful. This is where the IMedia Event interface comes into play. Invoking its WaitForCompletion method with a value of INFINITE causes the application to wait until the Filter Graph Manager learns that the media file has completed its playback. In a real-world application, you wouldn t use a value of INFINITE in the call to WaitForCompletion; if something happened to stall or halt the playback of the media file, the application would wait forever. This is fine for a first DirectShow example, but other programming examples in this book will show you how to exploit the IMedia Event interface more effectively.

After playback is complete, a call to the Stop method of the IMediaControl object halts the execution of the filter graph. This stop call is necessary because a filter graph doesn t stop by itself when a media file has been fully rendered.

Saving a Filter Graph to a .GRF File

Immediately after the filter graph is stopped, you ll see a call to a local function, SaveFilterGraph, which takes two arguments: a pointer to the IGraphBuilder interface and a file name. SaveFilterGraph saves a GraphEdit-viewable copy of the filter graph, so you can see the filter graph that s been built by DSRender. Here s the code for SaveFilterGraph:

// Pass it a file name in wszPath, and it will save the filter graph // to that file. HRESULT SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath) { const WCHAR wszStreamName[] = L"ActiveMovieGraph"; HRESULT hr; IStorage *pStorage = NULL; // First, create a document file that will hold the GRF file hr = StgCreateDocfile( wszPath, STGM_CREATE STGM_TRANSACTED STGM_READWRITE STGM_SHARE_EXCLUSIVE, 0, &pStorage); if(FAILED(hr)) { return hr; } // Next, create a stream to store. IStream *pStream; hr = pStorage->CreateStream( wszStreamName, STGM_WRITE STGM_CREATE STGM_SHARE_EXCLUSIVE, 0, 0, &pStream); if (FAILED(hr)) { pStorage->Release(); return hr; } // The IpersistStream::Save method converts a stream // into a persistent object. IPersistStream *pPersist = NULL; pGraph->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersist)); hr = pPersist->Save(pStream, TRUE); pStream->Release(); pPersist->Release(); if (SUCCEEDED(hr)) { hr = pStorage->Commit(STGC_DEFAULT); } pStorage->Release(); return hr; }

This function is straightforward, although it uses a few components we haven t yet encountered. Beginning with a call to the Windows function Stg CreateDocfile, an output file is opened, creating an IStorage object (in other words, an object that exposes the IStorage interface) that represents the file. (Note that this is an example of a COM object that is not created directly through CoCreateInstance but rather through a helper function.) Next an IStream stream object is created; this stream is used to provide a data path to the output file. The magic in this function happens when the Filter Graph Manager s IPersistStream interface is obtained by a call to the QueryInterface method of IGraphBuilder. The IPersistStream interface contains methods that create persistent stream objects, which can be written to a storage medium such as a file and retrieved later. When the Save method of IPersistStream is invoked with a parameter that points to the IStream object the filter graph data structure is written to the stream.

If all of this goes as planned, a call to the Commit method of the IStorage interface writes the data to disk. At this point, a snapshot of the filter graph has been written out. This program uses the hard-coded string C:\MyGraph.GRF as the file name, but this name can be modified by you to any system-legal file path and name. After you run DSRender you ll find the file MyGraph.GRF on your hard disk. Double-click it and GraphEdit will launch; you ll see the filter graph created by DSRender. This filter graph will vary, depending on the media type of the file being rendered. Figure 3-2 shows the MyGraph.GRF filter graph.

figure 3-2 graphedit showing the filter graph mygraph.grf created by dsrender

Figure 3-2. GraphEdit showing the filter graph MyGraph.GRF created by DSRender

DSRender is a very slapdash example of a DirectShow application no frills, no extra UI details, just media playback. Yet a very broad set of media can be played with this simple application because the DirectShow IGraphBuilder object handles the hard work of selecting and connecting the appropriate filters together to create a functional filter graph. Now we need to move on and learn how to do the heavy lifting for ourselves, building a filter graph in C++ code line by line. Well, mostly .



Programming Microsoft DirectShow for Digital Video and Television
Programming Microsoft DirectShow for Digital Video and Television (Pro-Developer)
ISBN: 0735618216
EAN: 2147483647
Year: 2002
Pages: 108
Authors: Mark D. Pesce

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net