A DirectShow Tutorial

Building a filter graph

The next step is to try to build the same filter graph ourselves without using the RenderFile method. Instead, we will create each filter and connect them together. This way we will able to modify the graph by adding our own filters and thus performing the processing we want. Filters are connected together using their pins; an output pin of a filter is connected to the input pin of the next filter. To obtain the pin of a filter, you have to use the EnumPins method. You then iterate through all the pins until you find the required one (either output or input). This is what the following function does:
IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir)
{
    BOOL       bFound = FALSE;
    IEnumPins  *pEnum;
    IPin       *pPin;

    pFilter->EnumPins(&pEnum);
    while(pEnum->Next(1, &pPin, 0) == S_OK)
    {
        PIN_DIRECTION PinDirThis;
        pPin->QueryDirection(&PinDirThis);
        if (bFound = (PinDir == PinDirThis))
            break;
        pPin->Release();
    }
    pEnum->Release();
    return (bFound ? pPin : 0);  
}
The PIN_DIRECTION can be PINDIR_OUTPUT or PINDIR_INPUT. For example, to obtain a source filter and its output pin ready to be connected, we can do:
IBaseFilter*	pSource= NULL;
// Add a source filter to the current graph
pGraph->AddSourceFilter(mediaFile,0,&g_pSource);
// Obtain the output pin
IPin* pSourceOut= GetPin(pSource, PINDIR_OUTPUT);
To add a filter (it must first be created) to the filter graph, we use the AddFilter method:
// Add the pFilter to the current graph
pGraph->AddFilter( pFilter, L"Name of the Filter");
The second argument is a name for the filter that must identifies it uniquely in the filter graph (if you set it to NULL, the graph manager will generate one for you). To connect to pins together, we simply use the Connect method
// Connect pIn to pOut
pGraph->Connect(pOut, pIn);           
What filters do we need to display an AVI sequence? We know the answer from the results displayed in the filter list box or in the GraphEdit application:
  1. a Source filter that reads the file
  2. an AVI splitter that reads the stream and split it into a video and an audio channel (we ignore the latter here).
  3. an AVI video decompressor that decodes the video stream
  4. a Video renderer that plays the video sequence in a window.
Note that for some filter, the pins are created dynamically. This is the case of the AVI splitter that will create the required output pins (video and/or audio) only when the source is connected to its input. This makes sense since the format of the output of this kind of filter is known only when the type of its input is known. It must also be obvious that, to be connected together, the respective output and input pins of two filters must be of compatible types. The properties of a given pin (such as major type and subtype) can be obtained as follows:
    
AM_MEDIA_TYPE amt;
pPin->ConnectionMediaType(&amt); 
The following member function will now create the complete filter graph. The procedure is simple: we first create the filter using CoCreateInstance (finding the right CLSID identifier is the key to obtain the filter we want), add it to the filter graph, obtain its input pin and connect if to the output pin of the previous filter.
    
bool createFilterGraph(CString filename) {

  WCHAR *mediaFile= new WCHAR[filename.GetLength()+1];
  MultiByteToWideChar(CP_ACP, 0, filename, -1, 
                      mediaFile, filename.GetLength()+1);

  // Create a source filter specified by filename
  IbaseFilter* pSource= NULL;
  if(FAILED(pGraph->AddSourceFilter(mediaFile,0,&pSource)))
  {
    ::MessageBox( NULL, "Unable to create source filter", 
                  "Error", MB_OK | MB_ICONINFORMATION );
        return 0;
  }

  IPin* pSourceOut= GetPin(pSource, PINDIR_OUTPUT);
  if (!pSourceOut) {

    ::MessageBox( NULL, "Unable to obtain source pin", 
                  "Error", MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  // Create an AVI splitter filter
  IBaseFilter* pAVISplitter = NULL;
  if(FAILED(CoCreateInstance(CLSID_AviSplitter, NULL,   
                             CLSCTX_INPROC_SERVER,
                             IID_IBaseFilter,
              (void**)&pAVISplitter)) || !pAVISplitter)
  {
    ::MessageBox( NULL, "Unable to create AVI splitter", 
                  "Error", MB_OK | MB_ICONINFORMATION );
      return 0;
  }

  IPin* pAVIsIn= GetPin(pAVISplitter, PINDIR_INPUT);
  if (!pAVIsIn) {

    ::MessageBox( NULL, 
       "Unable to obtain input splitter pin", "Error",
        MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  // Connect the source and the splitter
  if(FAILED(pGraph->AddFilter( pAVISplitter, L"Splitter")) 
     || FAILED(pGraph->Connect(pSourceOut, pAVIsIn)) )           
  {
    ::MessageBox( NULL, 
       "Unable to connect AVI splitter filter", "Error",
        MB_OK | MB_ICONINFORMATION );
      return 0;
  }

  // Create an AVI decoder filter
  IBaseFilter* pAVIDec = NULL;
  if(FAILED(CoCreateInstance(CLSID_AVIDec, NULL, 
                             CLSCTX_INPROC_SERVER,
                             IID_IBaseFilter,
                    (void**)&pAVIDec)) || !pAVIDec)
  {
    ::MessageBox( NULL, "Unable to create AVI decoder", 
                  "Error", MB_OK | MB_ICONINFORMATION);
    return 0;
  }

  IPin* pAVIsOut= GetPin(pAVISplitter, PINDIR_OUTPUT);
  if (!pAVIsOut) {

    ::MessageBox( NULL, 
      "Unable to obtain output splitter pin", "Error", 
       MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  IPin* pAVIDecIn= GetPin(pAVIDec, PINDIR_INPUT);
  if (!pAVIDecIn) {

    ::MessageBox( NULL, 
       "Unable to obtain decoder input pin", "Error", 
        MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  if(FAILED(pGraph->AddFilter( pAVIDec, L"Decoder")) ||
     FAILED(pGraph->Connect(pAVIsOut, pAVIDecIn)) )           
  {
    ::MessageBox( NULL, 
             "Unable to connect AVI decoder filter",
             "Error", MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  IPin* pAVIDecOut= GetPin(pAVIDec, PINDIR_OUTPUT);
  if (!pAVIDecOut) {

    ::MessageBox( NULL, 
        "Unable to obtain decoder output pin", 
        "Error", MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  // Render from the decoder
  if(FAILED(pGraph->Render( pAVIDecOut ))) 
  {
    ::MessageBox( NULL, "Unable to connect to renderer",
                  "Error", MB_OK | MB_ICONINFORMATION );
    return 0;
  }

  SAFE_RELEASE(pAVIDecIn);
  SAFE_RELEASE(pAVIDecOut);
  SAFE_RELEASE(pAVIDec);
  SAFE_RELEASE(pAVIsOut);
  SAFE_RELEASE(pAVIsIn);
  SAFE_RELEASE(pAVISplitter);
  SAFE_RELEASE(pSourceOut);
  SAFE_RELEASE(pSource);

  return 1;
}
By executing this manually built filter, the result is the same as previously.

Check point #2: source code of the above example.

Top of the page

 

(c) Robert Laganiere 2011