GraphEdit is particularly useful because its UI commands correspond almost exactly to DirectShow method calls. The following code listing is a complete console application that plays a video file.
#include "dshow.h" // DirectShow header file. #include "atlbase.h" // ATL smart pointers. #pragma comment(lib, "strmiids") // Link to DirectShow GUIDs. void main(void) { // Initialize the COM library. HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { printf("Could not initialize the COM library.\n"); return; } { // Scope for smart pointers. // Create the Filter Graph Manager. CComPtr<IGraphBuilder> pGraph; hr = pGraph.CoCreateInstance(CLSID_FilterGraph); if (FAILED(hr)) { printf("Could not create the Filter Graph Manager.\n"); return; } // Query for interfaces. CComQIPtr<IMediaControl> pControl(pGraph); CComQIPtr<IMediaEventEx> pEvent(pGraph); // Build the graph. (Make sure to use a real file name!) hr = pGraph->RenderFile(L"C:\Example.avi", NULL); if (FAILED(hr)) { printf("Could not build the graph.\n"); return; } // Run the graph. hr = pControl->Run(); if (FAILED(hr)) { printf("Could not build the graph.\n"); return; } // Wait for playback to complete. long evCode; pEvent->WaitForCompletion(INFINITE, &evCode); } CoUninitialize(); }
This application uses ATL smart pointers to manage reference counts on COM interfaces. In case you ve never used the ATL smart pointer classes before, they are the CComPtr and CComQIPtr variables in the code.
CComPtr<IGraphBuilder> pGraph;
The basic idea behind smart pointers is that they automatically call AddRef and Release as needed ” well, almost automatically. You can find more information about them in Appendix A. We ll be using smart pointers extensively in the next few chapters.
The application begins by initializing COM. Then it creates a new instance of the Filter Graph Manager by calling CoCreateInstance with the Filter Graph Manager s class identifier (CLSID), which is CLSID_FilterGraph . The call returns a pointer to the IGraphBuilder interface, which has methods for building the filter graph. We ll need two other interfaces on the Filter Graph Manager as well, so we query for them right away: IMediaControl has methods for starting and stopping playback, and IMediaEventEx has methods for listening to events from the graph.
Next, the IGraphBuilder::RenderFile method assembles a filter graph to play the specified file. This method is equivalent to the Render Media File command in GraphEdit. If the method succeeds, we now have a functioning filter graph. (If the method fails, it usually means that DirectShow does not support the file type, assuming you passed in a valid file name.) The IMediaControl::Run method starts playback, and is equivalent to the Play button in GraphEdit. This is the point when the video window is displayed.
(Incidentally, if you re wondering why the title bar on the video window says ActiveMovie Window, this is a holdover from the days when DirectShow was named ActiveMovie. The title bar hasn t changed since 1996! In practice, end users will never see it, because you will display the video inside your own application window.)
The IMediaEventEx::WaitForCompletion method blocks until playback completes. Normally you wouldn t do this in a real application, because it blocks for the duration of the video, or until the user closes the window. It works as a quick example, but you ll see a better approach later in this chapter. After WaitForCompletion returns, we can release all of our interfaces (which occurs automatically when the ATL smart pointers go out of scope) and call CoUninitialize to clean up. And that s it ” video playback in about a dozen lines of code. The next step is to mix two video streams and display the result inside the application window, instead of using the ActiveMovie window.