This article summarizes the steps necessary to create and play a cutlist. It also lists the limitations of the current cutlist implementation and provides sample code for using cutlists in an application. For an introduction to cutlists, see What Are Cutlists?.
This article contains the following sections.
To use cutlists, you must first explicitly include the Cutlist.h header file in your project. Then you can create cutlist objects and use the interfaces they expose. The following list summarizes the steps for creating and playing back cutlists in your application. Creating a Video Cutlist presents the example code from this section as one unit rather than many separate fragments.
CoCreateInstance((REFCLSID)CLSID_SimpleCutList, NULL, CLSCTX_INPROC, (REFIID)IID_IStandardCutList,(void**)&pVCutList);
CoCreateInstance((REFCLSID)CLSID_VideoFileClip, NULL, CLSCTX_INPROC, (REFIID)IID_IFileClip, (void**)&pVFileClip);
hr = pVFileClip->SetFileAndStream(L"jupiter.avi", 0);
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 5 through 10 of Jupiter.avi and the second element plays second 0 through 5 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 must always 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.
CoCreateInstance((REFCLSID)CLSID_CutListGraphBuilder,NULL,CLSCTX_INPROC, (REFIID)IID_ICutListGraphBuilder,(void**)&pGraphBuilder); . . . // Give the cutlist to the graph builder. hr = pGraphBuilder->AddCutList(pVCutList, NULL); // 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();
To add an element to an existing cutlist after the graph has been rendered, you must call ICutListGraphBuilder::RemoveCutList to remove the cutlist from the filter graph, then call IStandardCutList::AddElement to add the new cutlist element, then call ICutListGraphBuilder::AddCutList to add the cutlist to the filter graph, and then call ICutListGraphBuilder::Render to create the final cutlist filter graph.
See the cutlist examples later in this article for more complete sample code illustrating these steps.
The following list discusses limitations that you should be aware of when using the current DirectShow cutlist implementation.
For video cutlists, this means that 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 cutlist's media type, and the media type required for all other clips you add to the cutlist. The IStandardCutList::AddElement method returns an invalid media type error (VFW_E_INVALIDMEDIATYPE) if you try to add a clip of a different media type to an existing cutlist.
The following code creates and plays a cutlist consisting of two video clips from one AVI file. It plays seconds 5 through 10 of the file followed by seconds 0 through 5. The code fragment contains no error checking for the sake of brevity. For an example that performs error checking, see Cutlist Sample Code (Simplecl).
HRESULT hr; ICutListGraphBuilder *pGraphBuilder; IMediaControl *pControl; IMediaEvent *pEvent; IGraphBuilder *pGraph; IStandardCutList *pVCutList; IFileClip *pVFileClip; IAMCutListElement *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, NULL); // 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 preceding example uses video only. The example in the next section uses both audio and video.
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 Cutlist Sample Code (Simplecl) for an example that performs error checking.
HRESULT hr; ICutListGraphBuilder *pGraphBuilder; IMediaControl *pControl; IMediaEvent *pEvent; IGraphBuilder *pGraph; IStandardCutList *pVCutList, *pACutList; IFileClip *pAFileClip1; IFileClip *pVFileClip1; IAMCutListElement *pVElement1; IAMCutListElement *pVElement2; IAMCutListElement *pVElement3; IAMCutListElement *pVElement4; IAMCutListElement *pVElement5; IAMCutListElement *pAElement1; IAMCutListElement *pAElement2; IAMCutListElement *pAElement3; IAMCutListElement *pAElement4; IAMCutListElement *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 file name 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, NULL); hr = pGraphBuilder->AddCutList(pACutList, NULL); // 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 preceding example 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 Microsoft® DirectX® Media SDK.
To add an element to an existing cutlist after the graph has been rendered, you must call ICutListGraphBuilder::RemoveCutList to remove the cutlist from the filter graph, then call IStandardCutList::AddElement to add the new cutlist element, then call ICutListGraphBuilder::AddCutList to add the cutlist to the filter graph. Then call ICutListGraphBuilder::Render to create the final cutlist filter graph.
The Simplecl Sample (Cutlist Application) (Simplecl) from the DirectX Media SDK demonstrates how to create and play back cutlists. By default, the DirectShow setup program installs Simplecl in the DXMedia\Samples\Multimedia\DShow\Src\Cutlist\Simplecl directory. Simplecl provides a File Open dialog box 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, the sample 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 DirectX Media SDK also includes a sample that reads a list of cuts from a text file and plays them, much like Simplecl does. That sample, Cltext, is installed in the DXMedia\Samples\Multimedia\DShow\Src\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(&gTheSet, sizeof gTheSet);
Simplecl keeps track of the name of the media file that the user chooses as the source of a clip, tracks the number of files chosen, and displays a dialog box for the user to enter 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;
After the user has entered the file and clip choices, the SimpleCutList function creates and plays the cutlist as follows:
void SimpleCutList () { // SimpleCutList // WCHAR wFile[MAX_PATH]; // file name // Initialize video and audio file clips and elements to NULL // so you 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 you 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 (SUCCEEDED(hr)) ++nVidElems; else { // AddElement failed so release associated objects. HELPER_RELEASE(pVidCLElem[nVidElems]); HELPER_RELEASE(pVidFileClip[nVidElems]); MessageBox(ghApp, "AddElement (video) failed!", APPLICATIONNAME, MB_OK); } // AddElement failed so release associated objects. } // Add the element to the cutlist. else MessageBox(ghApp, "CreateCut (video) failed!", APPLICATIONNAME, MB_OK); } // Create video cut. else { // Problems creating video stream HELPER_RELEASE(pVidFileClip[nVidElems]); MessageBox(ghApp, "SetFileAndStream (video) failed!", APPLICATIONNAME, MB_OK); } // Problems creating video stream // Create an audio clip object and give it the file and stream // to read from. // SetFileAndStream will fail if you 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 { // AddElement failed so release associated objects. HELPER_RELEASE(pAudCLElem[nAudElems]); HELPER_RELEASE(pAudFileClip[nAudElems]); MessageBox(ghApp, "AddElement (audio) failed!", APPLICATIONNAME, MB_OK); } // AddElement failed so release associated objects. } // Add the element to the cutlist. else MessageBox(ghApp, "CreateCut (audio) failed!", APPLICATIONNAME, MB_OK); } // Create audio cut. // Problems creating audio stream else { // Problems creating audio stream HELPER_RELEASE(pAudFileClip[nAudElems]); MessageBox(ghApp, "SetFileAndStream (audio) failed!", APPLICATIONNAME, MB_OK); } // Problems creating audio stream } // Individual clips // Add the video cutlist to the filter graph. hr = pCLGraphBuilder->AddCutList(pVideoCL, NULL); 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, NULL); 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. pigb->Release(); // 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 as follows.
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(pVideoCL); HELPER_RELEASE(pAudioCL); HELPER_RELEASE(pCLGraphBuilder); } // TearDownTheGraph //
Top of Page
© 2000 Microsoft and/or its suppliers. All rights reserved. Terms of Use.