About Cutlists


This article introduces cutlists, discusses interfaces that provide cutlist support and the limitations of the current cutlist implementation, and provides sample code for using cutlists in an application.

Contents of this article:

Introduction to Cutlists

A cutlist is a list of audio or video clips (cutlist elements) you want to play back sequentially. For each clip, the cutlist element contains the file name from which to create the clip, and details about the clip including start and stop time within that file. A cutlist is either video- or audio- specific, and that video or audio data must all be of the same media type. The beginning time for the clip, relative to the source file, is called the trimin position and the ending time for the clip is the trimout position.

You use cutlists to edit pieces of AVI and WAV files together. For instance a video cutlist could be made up of video clips with characteristics as follows:
Clip # File Name Start Time Stop Time Type Stream #
1 venus.avi 5 seconds 10 seconds video 0
2 mars.avi 15 seconds 20 seconds video 0
3 venus.avi 15 seconds 30 seconds video 0

In the example above, the first and third clips are both taken from the same file. One clip is from seconds 5 through 10 of Venus.avi while another is from seconds 15 through 30 of the same file. Between those clips, the cutlist contains seconds 15 through 20 of Mars.avi. All clips are taken from the first video stream (stream 0) in their respective files. The clips play back sequentially (1, 2, and then 3).

Each of the three clips is referred to as a "cutlist element", and the whole thing is referred to as the "cutlist".

Cutlist Objects and Interfaces

DirectShow defines the following objects that implement the specified interfaces. Applications use these objects and interfaces to create, manipulate, and play cutlists.
Object Supported Interfaces Description
CLSID_SimpleCutList IStandardCutList Cutlist object
CLSID_VideoFileClip IFileClip Cutlist element (individual clip) object for video
CLSID_AudioFileClip IFileClip Cutlist element (individual clip) object for audio
CLSID_CutListGraphBuilder ICutListGraphBuilder Cutlist graph builder object

These interfaces allow application writers to construct filter graphs without having to worry about the specifics of each cutlist object. They provide a simple way to create and manipulate cutlists, and to create a filter graph to play an edited movie in real time.

If you need more advanced cutlist functionality that is not provided by the above interfaces, see the following interfaces.

The following section discusses how to use cutlists.

Using Cutlists

Create cutlist objects and use the interfaces they expose in order to use cutlists. The following list summarizes the steps in using cutlists in your application. Cutlist Example 1 -- Video Only presents the example code from this section as one unit rather than many separate fragments.

  1. Create a standard cutlist object (CLSID_SimpleCutList) for each cutlist. Each cutlist can contain only one media type so if you want both audio and video you must create one standard cutlist for all of your video cuts, and one standard cutlist for all of your audio cuts.
    
    CoCreateInstance((REFCLSID)CLSID_SimpleCutList, NULL, CLSCTX_INPROC,
                      (REFIID)IID_IStandardCutList,(void**)&pVCutList);
    
  2. Create a video file clip object (CLSID_VideoFileClip) or an audio file clip object (CLSID_AudioFileClip), as appropriate, for each stream in each AVI or WAV file you will use as the source for clips. If you want to play back both video and audio from the same file you must still create separate video and audio file clip objects since the file clip objects are on a per stream basis.
    
    CoCreateInstance((REFCLSID)CLSID_VideoFileClip, NULL, CLSCTX_INPROC,
                      (REFIID)IID_IFileClip, (void**)&pVFileClip);
    
  3. Tell the file clip object what file and stream in that file to use by calling IFileClip::SetFileAndStream as follows. The first video and audio streams (stream 0) are the only streams currently supported.
    
    hr = pVFileClip->SetFileAndStream(L"jupiter.avi", 0);
    
  4. Create one or more cutlist elements (individual clips) from each file clip by calling IFileClip::CreateCut. Each file clip represents a stream of data and you can create more than one clip from each data stream. For instance, the example in Introduction to Cutlists specifies two elements from Venus.avi, and one element from Mars.avi.
  5. Add each cutlist element to the cutlist by calling IStandardCutList::AddElement.

    The following example creates two cutlist elements from the file clip and adds them to the cutlist. The file clip is the first video stream (stream zero) of Jupiter.avi, as specified previously in IFileClip::SetFileAndStream. The first element plays seconds five through ten of Jupiter.avi and the second element plays second zero through five of the same file.

    
    hr = pVFileClip->CreateCut(&pVElement1,5*SCALE,10*SCALE,0,5*SCALE,0);
    hr = pVCutList->AddElement(pVElement1,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
    hr = pVFileClip->CreateCut(&pVElement2,0,5*SCALE,0,5*SCALE,0);
    hr = pVCutList->AddElement(pVElement2,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
    

    The first three parameters of IFileClip::CreateCut are the important ones. The first, ppElement specifies the element, and is filled in for you. The second (mtTrimIn) and third (mtTrimOut) specify, respectively, the start and stop times for the clip relative to the original file (Jupiter.avi in this case). The last three parameters always have to be zero, mtTrimOut minus mtTrimIn, and zero, respectively. A scale value of 10,000,000 scales the start and stop times to seconds.

    The first parameter in the IStandardCutList::AddElement call, pElement, is the element obtained from the call to IFileClip::CreateCut. The last two parameters must be CL_DEFAULT_TIME to indicate that the element should be added to the end of the current cutlist, and its duration is the same as its duration in the original file.

  6. Create a cutlist graph builder object (CLSID_CutListGraphBuilder) and use it to build a filter graph that will play your cutlists. Give it a standard cutlist object using ICutListGraphBuilder::AddCutList, and then call ICutListGraphBuilder::Render to have it build a filter graph which can play the cutlist. The following code fragment illustrates these calls.
    
    CoCreateInstance((REFCLSID)CLSID_CutListGraphBuilder,NULL,CLSCTX_INPROC,
    
                      (REFIID)IID_ICutListGraphBuilder,(void**)&pGraphBuilder);
    
    .
    .
    .
    // Give the cutlist to the graph builder
    hr = pGraphBuilder->AddCutList(pVCutList);
    
    // Tell the cutlist graph builder to build the graph
    hr = pGraphBuilder->Render();
    
  7. Play the cutlist filter graph and clean up resources as in the following example. Be sure to stop the graph before you remove the cutlist from the graph using ICutListGraphBuilder::RemoveCutList.
    
    // Get the filter graph the builder just made
    hr = pGraphBuilder->GetFilterGraph(&pGraph);
    
    // Now get some useful graph interfaces
    pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
    pGraph->QueryInterface(IID_IMediaEvent, (void**)&pEvent);
    pGraph->Release();
    
    // Run the graph
    pControl->Run();
    
    // Arbitrarily assumes 10000 millisecond timeout
    pEvent->WaitForCompletion(10000, &lEventCode);
    pControl->Stop();
    pEvent->Release();
    pControl->Release();
    
    // Cleanup
    hr = pGraphBuilder->RemoveCutList(pVCutList);
    pVElement1->Release();
    pVElement2->Release();
    pVCutList->Release();
    pVFileClip->Release();
    pGraphBuilder->Release();
    

See the cutlist examples later in this article for more complete sample code illustrating these steps.

Cutlist Limitations

The following list discusses limitations of which you should be aware when using the current cutlist implementation.

  1. All clips in a cutlist must be the same format (media type).

    For video cutlists, this means all the video clips must be of the same compression type, size, dimensions, bit depth, and so forth. In other words all video clips in the cutlist must be represented by the same BITMAPINFOHEADER structure. For audio cutlists, this means they must all use the same compression format, sampling rate, bit depth, and number of channels. In other words all audio clips in the cutlist must be represented by the same WAVEFORMATEX structure.

    The first clip you add to a cutlist determines the media type of the cutlist, and the media type required for all other clips you add to the cutlist. IStandardCutList::AddElement returns an invalid media type error (VFW_E_INVALIDMEDIATYPE) if you attempt to add a clip of a different media type to an existing cutlist.

  2. All cuts must begin on a keyframe. If not, there will be a free but probably unwanted "fade in" effect at the cut point instead of a clean switch from one clip to the next.
  3. There is no way to save (persist) a cutlist. Every time your application runs, you must build the cutlist by hand. There is no file format for saving a cutlist you have previously constructed.
  4. Audio cuts not made during silence may cause an audible "click" sound at the cut point if there is low to moderate volume and sparse audio at the cut point. If this becomes a problem, a downstream filter can fade each cut in and out quickly to avoid this.
  5. You can only use the first video or audio stream of an AVI file for a cutlist. Extra streams in files with multiple video or audio streams are ignored.
  6. WAVE files and AVI files are the only types of files that can be used as source material for a cutlist. Other formats such as MPEG are not supported.
  7. WAVE files used in cutlists cannot be identified by a universal network connection (UNC) network name. For example the file name "x:\venus.wav" is valid, but "\\server\share\venus.wav" is not.
  8. The cutlist graph builder can build a graph with at most one video cutlist and at most one audio cutlist. Such a graph cannot contain more than one video or more than one audio cutlist filter. This means that you cannot call ICutListGraphBuilder::AddCutList more than twice, once with a video standard cut list, and once with an audio standard cut list.

Cutlist Example 1 -- Video Only

The following code creates and plays a cutlist consisting of two video clips from one AVI file. It plays seconds five through ten of the file followed by seconds zero through five. The code fragment contains no error checking for the sake of brevity. See Simple Cutlist (Simplecl) SDK Sample for an example that performs error checking.


HRESULT              hr;
ICutListGraphBuilder *pGraphBuilder;
IMediaControl        *pControl;
IMediaEvent          *pEvent;
IGraphBuilder        *pGraph;
IStandardCutList     *pVCutList;
IFileClip            *pVFileClip;
ICutListElement      *pVElement1, *pVElement2;
LONG                 lEventCode=0L;

CoInitialize(NULL);
// we need these 3 objects
CoCreateInstance((REFCLSID)CLSID_CutListGraphBuilder,NULL,CLSCTX_INPROC,
                  (REFIID)IID_ICutListGraphBuilder,(void**)&pGraphBuilder);
CoCreateInstance((REFCLSID)CLSID_VideoFileClip, NULL, CLSCTX_INPROC,
                  (REFIID)IID_IFileClip, (void**)&pVFileClip);
CoCreateInstance((REFCLSID)CLSID_SimpleCutList, NULL, CLSCTX_INPROC,
                  (REFIID)IID_IStandardCutList,(void**)&pVCutList);

// Tell the clip what file to use as a source file
hr = pVFileClip->SetFileAndStream(L"jupiter.avi", 0);

// Create some cutlist elements and add them to the standard cutlist
// from 5 to 10 seconds, then from 0 to 5 seconds
hr = pVFileClip->CreateCut(&pVElement1,5*SCALE,10*SCALE,0,5*SCALE,0);
hr = pVCutList->AddElement(pVElement1,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
hr = pVFileClip->CreateCut(&pVElement2,0,5*SCALE,0,5*SCALE,0);
hr = pVCutList->AddElement(pVElement2,CL_DEFAULT_TIME,CL_DEFAULT_TIME);

// Give the cutlist to the graph builder
hr = pGraphBuilder->AddCutList(pVCutList);

// Tell the cutlist graph builder to build the graph
hr = pGraphBuilder->Render();

// Get the filter graph the builder just made
hr = pGraphBuilder->GetFilterGraph(&pGraph);

// Now get some useful graph interfaces
pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
pGraph->QueryInterface(IID_IMediaEvent, (void**)&pEvent);
pGraph->Release();

// Run the graph
pControl->Run();

// Arbitrarily assumes 10000 millisecond timeout
pEvent->WaitForCompletion(10000, &lEventCode);
pControl->Stop();
pEvent->Release();
pControl->Release();

// Cleanup
hr = pGraphBuilder->RemoveCutList(pVCutList);
pVElement1->Release();
pVElement2->Release();
pVCutList->Release();
pVFileClip->Release();
pGraphBuilder->Release();

CoUninitialize();

// Exit
PostMessage(WM_QUIT, 0, 0);

The example above uses video only. The example in the next section uses both audio and video.

Cutlist Example 2 -- Video and Audio

The following code takes a file name from the command line and plays five different pieces of that AVI file back to back, with both sound and video synchronized. The code fragment contains no error checking for the sake of brevity. See Simple Cutlist (Simplecl) SDK Sample for an example that performs error checking.


HRESULT              hr;
ICutListGraphBuilder *pGraphBuilder;
IMediaControl        *pControl;
IMediaEvent          *pEvent;
IGraphBuilder        *pGraph;
IStandardCutList     *pVCutList, *pACutList;
IFileClip            *pAFileClip1;
IFileClip            *pVFileClip1;
ICutListElement      *pVElement1;
ICutListElement      *pVElement2;
ICutListElement      *pVElement3;
ICutListElement      *pVElement4;
ICutListElement      *pVElement5;
ICutListElement      *pAElement1;
ICutListElement      *pAElement2;
ICutListElement      *pAElement3;
ICutListElement      *pAElement4;
ICutListElement      *pAElement5;
LONG                 lEventCode=0L;
WCHAR                lpwstr[256];

CoInitialize(NULL);

CoCreateInstance((REFCLSID)CLSID_CutListGraphBuilder,NULL,CLSCTX_INPROC,
                  (REFIID)IID_ICutListGraphBuilder,(void**)&pGraphBuilder);

CoCreateInstance((REFCLSID)CLSID_AudioFileClip, NULL, CLSCTX_INPROC,
                  (REFIID)IID_IFileClip, (void**)&pAFileClip1);

CoCreateInstance((REFCLSID)CLSID_VideoFileClip, NULL, CLSCTX_INPROC,
                  (REFIID)IID_IFileClip, (void**)&pVFileClip1);

CoCreateInstance((REFCLSID)CLSID_SimpleCutList, NULL, CLSCTX_INPROC,
                  (REFIID)IID_IStandardCutList,(void**)&pVCutList);
CoCreateInstance((REFCLSID)CLSID_SimpleCutList, NULL, CLSCTX_INPROC,
                  (REFIID)IID_IStandardCutList,(void**)&pACutList);

// Get the Unicode filename to use from the command line
MultiByteToWideChar(CP_ACP, 0, m_lpCmdLine, strlen(m_lpCmdLine)+1,
                     lpwstr, sizeof(lpwstr)/sizeof(*lpwstr));

// tell the clips what file they are reading from
hr = pVFileClip1->SetFileAndStream(lpwstr, 0);
hr = pAFileClip1->SetFileAndStream(lpwstr, 0);

// Create some cuts and add them the cutlist

// from 2 to 6 seconds
hr = pVFileClip1->CreateCut(&pVElement1,2*SCALE,6*SCALE,0,4*SCALE,0);
hr = pVCutList->AddElement(pVElement1,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
hr = pAFileClip1->CreateCut(&pAElement1,2*SCALE,6*SCALE,0,4*SCALE,0);
hr = pACutList->AddElement(pAElement1,CL_DEFAULT_TIME,CL_DEFAULT_TIME);

// from 20 to 24 seconds
hr = pVFileClip1->CreateCut(&pVElement2,20*SCALE,24*SCALE,0,4*SCALE,0);
hr = pVCutList->AddElement(pVElement2,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
hr = pAFileClip1->CreateCut(&pAElement2,20*SCALE,24*SCALE,0,4*SCALE,0);
hr = pACutList->AddElement(pAElement2,CL_DEFAULT_TIME,CL_DEFAULT_TIME);

// from 65 to 69 seconds
hr = pVFileClip1->CreateCut(&pVElement3,65*SCALE,69*SCALE,0,4*SCALE,0);
hr = pVCutList->AddElement(pVElement3,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
hr = pAFileClip1->CreateCut(&pAElement3,65*SCALE,69*SCALE,0,4*SCALE,0);
hr = pACutList->AddElement(pAElement3,CL_DEFAULT_TIME,CL_DEFAULT_TIME);

// from 35 to 39 seconds
hr = pVFileClip1->CreateCut(&pVElement4,35*SCALE,39*SCALE,0,4*SCALE,0);
hr = pVCutList->AddElement(pVElement4,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
hr = pAFileClip1->CreateCut(&pAElement4,35*SCALE,39*SCALE,0,4*SCALE,0);
hr = pACutList->AddElement(pAElement4,CL_DEFAULT_TIME,CL_DEFAULT_TIME);

// from 12 to 16 seconds
hr = pVFileClip1->CreateCut(&pVElement5,12*SCALE,16*SCALE,0,4*SCALE,0);
hr = pVCutList->AddElement(pVElement5,CL_DEFAULT_TIME,CL_DEFAULT_TIME);
hr = pAFileClip1->CreateCut(&pAElement5,12*SCALE,16*SCALE,0,4*SCALE,0);
hr = pACutList->AddElement(pAElement5,CL_DEFAULT_TIME,CL_DEFAULT_TIME);

// Add the cutlists to the graph
hr = pGraphBuilder->AddCutList(pVCutList);
hr = pGraphBuilder->AddCutList(pACutList);

// Tell the cutlist graph builder to build the graph
hr = pGraphBuilder->Render();

// Get the filter graph the builder just made
hr = pGraphBuilder->GetFilterGraph(&pGraph);

// Now get some useful graph interfaces
pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
pGraph->QueryInterface(IID_IMediaEvent, (void**)&pEvent);
pGraph->Release();

// Run the graph
pControl->Run();

// Arbitrarily assumes 10000 millisecond timeout
pEvent->WaitForCompletion(10000, &lEventCode);

pControl->Stop();

pEvent->Release();
pControl->Release();

// cleanup

// Remove the cutlist from the graph
hr = pGraphBuilder->RemoveCutList(pVCutList);
hr = pGraphBuilder->RemoveCutList(pACutList);

pVElement1->Release();
pVElement2->Release();
pVElement3->Release();
pVElement4->Release();
pVElement5->Release();
pAElement1->Release();
pAElement2->Release();
pAElement3->Release();
pAElement4->Release();
pAElement5->Release();

pACutList->Release();
pVCutList->Release();


pAFileClip1->Release();
pVFileClip1->Release();

pGraphBuilder->Release();

CoUninitialize();

// Exit
PostMessage(WM_QUIT, 0, 0);

The example above obtains video and audio clips from the same file. The next example adds a user interface and error checking, and it is available in the DirectShow SDK.

Simple Cutlist (Simplecl) SDK Sample

The simple cutlist (Simplecl) sample from the DirectShow SDK demonstrates using cutlists. By default, the DirectShow setup program installs Simplecl in the DXMedia\Samples\DS\cutlist\simplecl directory. Simplecl provides a File Open dialog from which the user can chose a file to add to a cutlist. For each file, the user specifies a starting (trimin) position for the clip and an ending (trimout) position for the clip. For every AVI file specified it tries to add the first video stream and the first audio stream to its respective cutlist. The user must add at least two files, and then can run the filter graph and see the clips played sequentially.

The DirectShow SDK also includes a sample which reads a list of cuts from a text file and plays them, much like Simplecl does. That sample is called Cltext, and is installed in the DXMedia\Samples\DS\cutlist\cltext directory by default.

The following code excerpts are from the Simplecl.h and Simplecl.cpp sample files. The sample includes error checking.

Simplecl.h declares a few global variables, including a ClipDetails structure to manage the user's file and clip start and stop time choices, and a ClipCollection structure to group the clip details. It also defines a SCALE constant to scale all user-specified times in one second increments. The HELPER_RELEASE macro releases objects only if they exist, and then sets the object pointer to NULL to guard against releasing the same object multiple times. The following example contains fragments from Simplecl.h.


#define MAX_CLIPS 150
#define SCALE 10000000   // scale for 1 second of reference time

// clip (element) details
struct ClipDetails  
  {   
    TCHAR szFilename[MAX_PATH];   // name of file containing clip
    REFERENCE_TIME start;         // Start (Trim In) position of clip within file
    REFERENCE_TIME stop;          // Stop (Trim Out) position of clip within file
  }; 

// cutlist is a collection of clips (elements)
struct ClipCollection
  { 
    int nNumClips;
    ClipDetails List[MAX_CLIPS];
  }; 

#define HELPER_RELEASE(x) { if (x) x->Release(); x = NULL; }

ClipCollection  gTheSet;            // Cutlist 

The application initializes the user input structure as follows.


// ... in WinMain ...
ZeroMemory(&gTheList, sizeof gTheList);

Simplecl keeps track of the name of the media file the user chooses as the source of a clip, tracks the number of files chosen, and displays a dialog box for the user to input the start and stop times for each clip. The following code fragments relate to tracking the user input for clips.


// ... in WndMainProc ...

case IDM_ADDFILE:

  if (GetClipFileName(gTheSet.List[gTheSet.nNumClips].szFilename))

    { // Add file

      TCHAR szTitleBar[200];

      DialogBox(ghInst, MAKEINTRESOURCE(wDlgRes = IDD_MEDIATIMES), 
                                        ghApp, (DLGPROC)DialogProc);
      gTheSet.nNumClips = gTheSet.nNumClips + 1;
      wsprintf(szTitleBar, "SimpleCutList - %d clips(s) added.", 
               gTheSet.nNumClips);
      SetWindowText(ghApp, szTitleBar);

    } // Add file

.
.
.
// ... in DialogProc ...
case IDOKTIMES:

  gTheList.List[gTheSet.nNumClips].start = GetDlgItemInt(h, 
                                           IDC_TRIMIN2, NULL, FALSE);
  gTheList.List[gTheSet.nNumClips].stop = GetDlgItemInt(h, 
                                          IDC_TRIMOUT2, NULL, FALSE);

  EndDialog(h,1);
  break;

The real work of the Simplecl sample is in the SimpleCutList function. If the user has chosen more than one clip, and then chooses Run from the Cutlist menu, then Simplecl builds and plays the cutlist. The following code checks the number of clips chosen, and calls SimpleCutList if more than one clip was chosen.


case IDM_RUN:
  if (gTheSet.nNumClips > 1)
    SimpleCutList();
  else
    DialogBox(ghInst, MAKEINTRESOURCE(wDlgRes = IDD_LESSTHAN2), 
              ghApp, (DLGPROC)DialogProc);
  break;

Once the user has entered their file and clip choices, the SimpleCutList function does the cutlist work as follows.


  void SimpleCutList ()

    { // SimpleCutList //

      WCHAR wFile[MAX_PATH];  // file name

      // Initialize video and audio file clips and elements to NULL
      // so we can easily free objects later.
      for (int x = 0; x < MAX_CLIPS; ++x)

        { 
          pVidFileClip[x] = NULL; 
          pAudFileClip[x] = NULL; 
            pVidCLElem[x] = NULL; 
            pAudCLElem[x] = NULL; 
        };

      // Create cutlist graph builder object
      hr = CoCreateInstance(CLSID_CutListGraphBuilder, NULL, 
                            CLSCTX_INPROC, IID_ICutListGraphBuilder, 
                            (void**)&pCLGraphBuilder);

      if (FAILED(hr))
        { // CoCreateInstance of CutListGraphBuiler failed
          MessageBox(ghApp, 
                     "CoCreateInstance of CutListGraphBuiler failed",
                     APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // CoCreateInstance of CutListGraphBuiler failed

      // Create simple (standard) cutlist object for video
      hr = CoCreateInstance(CLSID_SimpleCutList, NULL, 
                            CLSCTX_INPROC, IID_IStandardCutList, 
                            (void**)&pVideoCL);

      if (FAILED(hr))
        { // CoCreateInstance of video SimpleCutList failed
          MessageBox(ghApp, 
                     "CoCreateInstance of video SimpleCutList failed",
                     APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // CoCreateInstance of video SimpleCutList failed

      // Create simple (standard) cutlist object for audio
      hr = CoCreateInstance(CLSID_SimpleCutList, NULL, 
                            CLSCTX_INPROC, IID_IStandardCutList, 
                            (void**)&pAudioCL);

      if (FAILED(hr))
        { // CoCreateInstance of audio SimpleCutList failed
          MessageBox(ghApp, 
                     "CoCreateInstance of audio SimpleCutList failed",
                     APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // CoCreateInstance of audio SimpleCutList failed

      // Create the individual clips and add them to the cutlist
      nVidElems = nAudElems = 0;
      for (x = 0; x < gTheSet.nNumClips; ++x)

        { // Individual clips

          MultiByteToWideChar(CP_ACP, 0, 
                              gTheSet.List[x].szFilename, 
                              -1, wFile, MAX_PATH );

          // Create a video clip object and give it the file and stream 
          // to read from.
          // SetFileAndStream will fail if we call it from a video clip 
          // object and the clip is not a video clip.
          hr = CoCreateInstance(CLSID_VideoFileClip, NULL, 
                                CLSCTX_INPROC, IID_IFileClip, 
                                (void**)&pVidFileClip[nVidElems]);

          hr = pVidFileClip[nVidElems]->SetFileAndStream(wFile, 0);

          if (SUCCEEDED(hr))

            { // Create video cut and add the clip (element) to the cutlist

              hr = pVidFileClip[nVidElems]->CreateCut(&pVidCLElem[nVidElems], 
                      gTheSet.List[x].start*SCALE, 
                      gTheSet.List[x].stop*SCALE, 
                      0, 
                      (gTheSet.List[x].stop-gTheSet.List[x].start)*SCALE, 
                      0);

              if (SUCCEEDED(hr))

                { // Add the element to the cutlist

                  hr = pVideoCL->AddElement(pVidCLElem[nVidElems], CL_DEFAULT_TIME, CL_DEFAULT_TIME);

                  if (FAILED(hr))
                    MessageBox(ghApp, "AddElement (video) failed!", APPLICATIONNAME, MB_OK);
                  else
                    ++nVidElems;

                } // Add the element to the cutlist

              else MessageBox(ghApp, "CreateCut (video) failed!", APPLICATIONNAME, MB_OK);

            } // Create video cut

          // Problems creating video stream
          else MessageBox(ghApp, "SetFileAndStream (video) failed!", APPLICATIONNAME, MB_OK);

          // Create an audio clip object and give it the file and stream 
          // to read from.
          // SetFileAndStream will fail if we call it from an audio clip 
          // object and the clip is not an audio clip
          hr = CoCreateInstance(CLSID_AudioFileClip, NULL, 
                                CLSCTX_INPROC, IID_IFileClip, 
                                (void**)&pAudFileClip[nAudElems]);

          hr = pAudFileClip[nAudElems]->SetFileAndStream(wFile, 0);

          if (SUCCEEDED(hr))

            { // Create audio cut and add the clip (element) to the cutlist

              hr = pAudFileClip[nAudElems]->CreateCut(&pAudCLElem[nAudElems], 
                      gTheSet.List[x].start*SCALE, 
                      gTheSet.List[x].stop*SCALE, 
                      0, 
                      (gTheSet.List[x].stop-gTheSet.List[x].start)*SCALE, 
                      0);

              if (SUCCEEDED(hr))

                { // Add the element to the cutlist

                  hr = pAudioCL->AddElement(pAudCLElem[nAudElems],
                                            CL_DEFAULT_TIME, 
                                            CL_DEFAULT_TIME);

                  if (SUCCEEDED(hr))
                    ++nAudElems;
                  else MessageBox(ghApp, "AddElement (audio) failed!", APPLICATIONNAME, MB_OK);

                } // Add the element to the cutlist

              else MessageBox(ghApp, "CreateCut (audio) failed!", APPLICATIONNAME, MB_OK);

            } // Create audio cut

          // Problems creating audio stream
          else MessageBox(ghApp, "SetFileAndStream (audio) failed!", 
                          APPLICATIONNAME, MB_OK);

        } // Individual clips

      // Add the video cutlist to the filter graph
      hr = pCLGraphBuilder->AddCutList(pVideoCL);

      if (FAILED(hr)) // AddCutList (video) failed
          MessageBox(ghApp, "AddCutList (video) failed", APPLICATIONNAME, MB_OK);

      // Add the audio cutlist to the filter graph
      hr = pCLGraphBuilder->AddCutList(pAudioCL);

      if (FAILED(hr)) // AddCutList (audio) failed
          MessageBox(ghApp, "AddCutList (audio) failed", APPLICATIONNAME, MB_OK);

      if ((!pVideoCL) && (!pAudioCL))

        { // Clean up

          TearDownTheGraph();
          return;

        } // Clean up

      // Let the filter graph manager construct the appropriate graph 
      // automatically
      hr = pCLGraphBuilder->Render();

      if (FAILED(hr))
        { // Problems rendering the graph
          if (!AMGetErrorText(hr, gszScratch, 2048))
            MessageBox(ghApp, "Problems rendering the graph!", APPLICATIONNAME, MB_OK);
          else 
            MessageBox(ghApp, gszScratch, APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // Problems rendering the graph

      // Retrieve the filter graph and useful interfaces
      hr = pCLGraphBuilder->GetFilterGraph(&pigb);

      if (FAILED(hr))
        { // Problems retrieving the graph pointer
          if (!AMGetErrorText(hr, gszScratch, 2048))
            MessageBox(ghApp, "Problems retrieving the graph pointer!", APPLICATIONNAME, MB_OK);
          else 
            MessageBox(ghApp, gszScratch, APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // Problems retrieving the graph pointer

      // QueryInterface for some basic interfaces
      pigb->QueryInterface(IID_IMediaControl, (void **)&pimc);
      pigb->QueryInterface(IID_IMediaEventEx, (void **)&pimex);
      pigb->QueryInterface(IID_IVideoWindow, (void **)&pivw);

      // Decrement the ref count on the filter graph
      HELPER_RELEASE(pigb);

      // Prepare to play in the main application window's client area

      RECT rc;
      GetClientRect(ghApp, &rc);
      hr = pivw->put_Owner((OAHWND)ghApp);
      hr = pivw->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS);
      hr = pivw->SetWindowPosition(rc.left, rc.top, rc.right, rc.bottom);

      // Have the graph signal event via window callbacks for performance
      pimex->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0);

      // Run the graph if RenderFile succeeded
      pimc->Run();

    } // SimpleCutList //

Simplecl's TearDownTheGraph function releases all objects and cleans up.


 void TearDownTheGraph (void)

    { // TearDownTheGraph //

      if (pimc)
        pimc->Stop();

      if (pivw)

        { // Hide the playback window first thing

          pivw->put_Visible(OAFALSE);
          pivw->put_Owner(NULL);

        } //

      HELPER_RELEASE(pimex);
      HELPER_RELEASE(pimc);
      HELPER_RELEASE(pivw);

      // Remove the video cutlist from the filter graph to free resources
      if (pCLGraphBuilder && pVideoCL)
        pCLGraphBuilder->RemoveCutList(pVideoCL);

      // Remove the audio cutlist from the filter graph to free resources
      if (pCLGraphBuilder && pAudioCL)
        pCLGraphBuilder->RemoveCutList(pAudioCL);

      for (int x = 0; x < nAudElems; ++x)

        { // Release audio objects

          HELPER_RELEASE(pAudCLElem[x]);
          HELPER_RELEASE(pAudFileClip[x]);

        } // Release audio objects

      for (x = 0; x < nVidElems; ++x)

        { // Release video objects

          HELPER_RELEASE(pVidCLElem[x]);
          HELPER_RELEASE(pVidFileClip[x]);

        } // Release video objects

      HELPER_RELEASE(pCLGraphBuilder);
      HELPER_RELEASE(pVideoCL);
      HELPER_RELEASE(pAudioCL);
      HELPER_RELEASE(pigb);

    } // TearDownTheGraph //

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.