//------------------------------------------------------------------------------ // File: TimelineTest.cpp // // Desc: DirectShow sample code - creates two video tracks, using a // transition from the A track to the B track, and then back again. // It demonstrates how to add a transition to the timeline using a // CLSID. // // Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ //---------------------------------------------------------------------------- // This example creates 2 (video) tracks with a transition from the A track to // the B track and then from B back to A. It also crossfades two audio tracks // from A to B. This sample also demonstrates how to add a transition by CLSID // to the timeline and how to vary volume envelope properties by the // property setter. // // Note: Error checking is handled mostly with ASSERTs in order to keep // the sample as uncluttered as possible. In some cases where several // related function calls are made together, the HRESULT value returned from // the functions will be bitwise OR'ed with other HRESULT values. This value // will eventually be tested for failure using the FAILED macro. //---------------------------------------------------------------------------- #include "stdafx.h" #include #include #include #include #ifdef STRICT #undef STRICT #endif #include // // Conditional compilation flags to enable/disable effects // //#define CUTS_ONLY // define this to show an example of calling ConnectFrontEnd( ) // a second time on the render engine and having it rerender // the graph #define DO_RECONNECT // define this to do an audio crossfade #define DO_CROSSFADE // define this to only render a portion of the full timeline #define DO_RENDERRANGE // define this to enable transitions #define DO_TRANSITION // // Global data // // Use bitmaps and audio files known to install with the DirectX 8 SDK WCHAR * wszVideo1Name = L"dx5_logo.bmp"; WCHAR * wszVideo2Name = L"env3.bmp"; WCHAR * wszAudio1Name = L"track2.mp3"; WCHAR * wszAudio2Name = L"track3.mp3"; WCHAR * wszTitle = L"Timeline Test Sample"; // // Function prototypes // int TimelineTest( ); void Err(TCHAR *szErr); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // init the ole32 libraries // CoInitialize( NULL ); // // NOTE: There is no user interaction with this sample. // It will run to completion and exit. // TimelineTest(); CoUninitialize( ); return 0; } void Err(TCHAR *szErr) { MessageBox(NULL, szErr, TEXT("Error"), MB_ICONEXCLAMATION); } int TimelineTest( ) { USES_CONVERSION; HRESULT hr; // use the ATL libraries to do automatic reference counting on pointers // CComPtr< IRenderEngine > pRenderEngine; CComPtr< IGraphBuilder > pGraph; CComPtr< IVideoWindow > pVidWindow; CComPtr< IMediaEvent > pEvent; CComPtr< IAMTimeline > pTimeline; CComPtr< IAMTimelineObj > pVideoGroupObj; CComPtr< IAMTimelineObj > pAudioGroupObj; CComPtr< IMediaControl > pControl; CComPtr< IMediaSeeking > pSeeking; //-------------------------------------------- // make the timeline //-------------------------------------------- hr = CoCreateInstance( CLSID_AMTimeline, NULL, CLSCTX_INPROC_SERVER, IID_IAMTimeline, (void**) &pTimeline ); if(FAILED( hr )) { Err(_T("Could not create timeline")); return hr; } //-------------------------------------------- // make the root group/composition //-------------------------------------------- hr = pTimeline->CreateEmptyNode( &pVideoGroupObj, TIMELINE_MAJOR_TYPE_GROUP ); if(FAILED( hr )) { Err(_T("Could not create empty node")); return hr; } CComQIPtr< IAMTimelineGroup, &IID_IAMTimelineGroup > pVideoGroup( pVideoGroupObj ); CMediaType VideoGroupType; // all we set is the major type. The group will automatically use other defaults VideoGroupType.SetType( &MEDIATYPE_Video ); hr = pVideoGroup->SetMediaType( &VideoGroupType ); if(FAILED( hr )) { Err(_T("Could not set media type")); return hr; } //-------------------------------------------- // add the group to the timeline //-------------------------------------------- hr = pTimeline->AddGroup( pVideoGroupObj ); if(FAILED( hr )) { Err(_T("Could not add video group")); return hr; } //-------------------------------------------- // make a track //-------------------------------------------- CComPtr< IAMTimelineObj > pTrack1Obj; hr = pTimeline->CreateEmptyNode( &pTrack1Obj, TIMELINE_MAJOR_TYPE_TRACK ); if(FAILED( hr )) { Err(_T("Could not create empty track")); return hr; } //-------------------------------------------- // tell the composition about the track //-------------------------------------------- CComQIPtr< IAMTimelineComp, &IID_IAMTimelineComp > pRootComp( pVideoGroupObj ); hr = pRootComp->VTrackInsBefore( pTrack1Obj, -1 ); if(FAILED( hr )) { Err(_T("Could not insert track")); return hr; } //-------------------------------------------- // create a source from 0 to 8 seconds //-------------------------------------------- REFERENCE_TIME TLStart = 0 * UNITS; REFERENCE_TIME TLStop = 8 * UNITS; // you can set these if you want to other numbers, and the video will // speed up or slow down if the duration isn't the same as the timeline's. REFERENCE_TIME MediaStart = 0 * UNITS; REFERENCE_TIME MediaStop = 8 * UNITS; WCHAR pClipname[256]; TCHAR * tBasePath = (TCHAR *) DXUtil_GetDXSDKMediaPath( ); wcscpy( pClipname, T2W( tBasePath ) ); wcscat( pClipname, L"\\" ); wcscat( pClipname, wszVideo1Name ); // create the timeline source // CComPtr pSource1Obj; hr = pTimeline->CreateEmptyNode( &pSource1Obj, TIMELINE_MAJOR_TYPE_SOURCE ); if(FAILED( hr )) { Err(_T("Could not create the timeline source")); return hr; } // set up source right // hr = pSource1Obj->SetStartStop( TLStart, TLStop ); CComQIPtr< IAMTimelineSrc, &IID_IAMTimelineSrc > pSource1Src( pSource1Obj ); hr |= pSource1Src->SetMediaTimes( MediaStart, MediaStop ); hr |= pSource1Src->SetMediaName( pClipname ); if(FAILED( hr )) { Err(_T("Could not configure media source")); return E_FAIL; } //-------------------------------------------- // tell the track about the source //-------------------------------------------- CComQIPtr< IAMTimelineTrack, &IID_IAMTimelineTrack > pTrack1( pTrack1Obj ); hr = pTrack1->SrcAdd( pSource1Obj ); if(FAILED( hr )) { Err(_T("Could not add source to track")); return hr; } //-------------------------------------------- // make another track //-------------------------------------------- CComPtr< IAMTimelineObj > pTrack2Obj; hr = pTimeline->CreateEmptyNode( &pTrack2Obj, TIMELINE_MAJOR_TYPE_TRACK ); if(FAILED( hr )) { Err(_T("Could not create second track")); return hr; } //-------------------------------------------- // tell the composition about the track //-------------------------------------------- hr = pRootComp->VTrackInsBefore( pTrack2Obj, -1 ); if(FAILED( hr )) { Err(_T("Could not insert second track")); return hr; } //-------------------------------------------- // create a source for the 2nd track //-------------------------------------------- TLStart = 0 * UNITS; TLStop = 8 * UNITS; MediaStart = 0 * UNITS; MediaStop = 8 * UNITS; wcscpy( pClipname, T2W( tBasePath ) ); wcscat( pClipname, L"\\" ); wcscat( pClipname, wszVideo2Name ); // create the timeline source // CComPtr pSource2Obj; hr = pTimeline->CreateEmptyNode( &pSource2Obj, TIMELINE_MAJOR_TYPE_SOURCE ); if(FAILED( hr )) { Err(_T("Could not create the second timeline source")); return hr; } // set up source right // hr = pSource2Obj->SetStartStop( TLStart, TLStop ); CComQIPtr< IAMTimelineSrc, &IID_IAMTimelineSrc > pSource2Src( pSource2Obj ); hr |= pSource2Src->SetMediaTimes( MediaStart, MediaStop ); hr |= pSource2Src->SetMediaName( pClipname ); if(FAILED( hr )) { Err(_T("Could not configure second media source")); return E_FAIL; } //-------------------------------------------- // tell the track about the source //-------------------------------------------- CComQIPtr< IAMTimelineTrack, &IID_IAMTimelineTrack > pTrack2( pTrack2Obj ); hr = pTrack2->SrcAdd( pSource2Obj ); if(FAILED( hr )) { Err(_T("Could not add second track")); return hr; } CComQIPtr< IAMTimelineTransable, &IID_IAMTimelineTransable > pTransable( pTrack2 ); #ifdef DO_TRANSITION //--------------------------------------------- // create an transition on the track from A 2 B //--------------------------------------------- REFERENCE_TIME TransStart = 0 * UNITS; REFERENCE_TIME TransStop = 4 * UNITS; // create the timeline effect // CComPtr pTrackTransObj; hr = pTimeline->CreateEmptyNode( &pTrackTransObj, TIMELINE_MAJOR_TYPE_TRANSITION ); if(FAILED( hr )) { Err(_T("Could not create transition effect")); return hr; } //-------------------------------------------- // set up filter right //-------------------------------------------- // we set the CLSID of the DXT to use instead of a pointer to the // actual object. We let the DXT have it's default properties. // hr = pTrackTransObj->SetSubObjectGUID( CLSID_DxtJpeg ); hr |= pTrackTransObj->SetStartStop( TransStart, TransStop ); CComQIPtr< IAMTimelineTrans, &IID_IAMTimelineTrans > pTrackTrans( pTrackTransObj ); hr |= pTransable->TransAdd( pTrackTransObj ); if(FAILED( hr )) { Err(_T("Could not configure transition object")); return E_FAIL; } #ifdef CUTS_ONLY //--------------------------------------------- // turn the transition into a cut by doing this //--------------------------------------------- hr = pTrackTrans->SetCutsOnly( TRUE ); if(FAILED( hr )) { Err(_T("Could not SetCutsOnly to TRUE")); return hr; } #endif // CUTS_ONLY //--------------------------------------------- // create an transition on the track from B 2 A //--------------------------------------------- TransStart = 4 * UNITS; TransStop = 8 * UNITS; // create the timeline effect // pTrackTransObj.Release( ); hr = pTimeline->CreateEmptyNode( &pTrackTransObj, TIMELINE_MAJOR_TYPE_TRANSITION ); ASSERT( !FAILED( hr ) ); // set up filter right // hr = pTrackTransObj->SetSubObjectGUID( CLSID_DxtJpeg ); hr |= pTrackTransObj->SetStartStop( TransStart, TransStop ); pTrackTrans = pTrackTransObj; hr |= pTrackTrans->SetSwapInputs( TRUE ); hr |= pTransable->TransAdd( pTrackTransObj ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // set a property on the transition //-------------------------------------------- CComPtr< IPropertySetter > pTransSetter; pTransSetter.CoCreateInstance( CLSID_PropertySetter ); DEXTER_PARAM Param; CComBSTR ParamName( "MaskNum" ); // the property name Param.Name = ParamName; Param.nValues = 1; // how many values we want to set DEXTER_VALUE Value; memset( &Value, 0, sizeof( Value ) ); VariantClear( &Value.v ); V_I4( &Value.v ) = 128; // mask number 128 V_VT( &Value.v ) = VT_I4; // integer hr = pTransSetter->AddProp( Param, &Value ); hr |= pTrackTransObj->SetPropertySetter( pTransSetter ); ASSERT( !FAILED( hr ) ); // pTransSetter will be auto-freed by COM #endif // DO_TRANSITION #ifdef CUTS_ONLY //--------------------------------------------- // turn the transition into a cut by doing this //--------------------------------------------- hr = pTrackTrans->SetCutsOnly( TRUE ); ASSERT( !FAILED( hr ) ); #endif //-------------------------------------------- // make the root audio group/composition //-------------------------------------------- hr = pTimeline->CreateEmptyNode( &pAudioGroupObj, TIMELINE_MAJOR_TYPE_GROUP ); ASSERT( !FAILED( hr ) ); CComQIPtr< IAMTimelineGroup, &IID_IAMTimelineGroup > pAudioGroup( pAudioGroupObj ); CMediaType AudioGroupType; // all we set is the major type. The group will automatically use other defaults AudioGroupType.SetType( &MEDIATYPE_Audio ); hr = pAudioGroup->SetMediaType( &AudioGroupType ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // add the group to the timeline //-------------------------------------------- hr = pTimeline->AddGroup( pAudioGroupObj ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // make a track //-------------------------------------------- CComPtr< IAMTimelineObj > pTrack3Obj; hr = pTimeline->CreateEmptyNode( &pTrack3Obj, TIMELINE_MAJOR_TYPE_TRACK ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // tell the composition about the track //-------------------------------------------- CComQIPtr< IAMTimelineComp, &IID_IAMTimelineComp > pAudioComp( pAudioGroupObj ); hr = pAudioComp->VTrackInsBefore( pTrack3Obj, -1 ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // create a source //-------------------------------------------- TLStart = 0 * UNITS; TLStop = 6 * UNITS; // you can set these if you want to other numbers, the video will speed up // or slow down if the duration isn't the same at the timeline's. MediaStart = 0 * UNITS; MediaStop = 6 * UNITS; wcscpy( pClipname, T2W( tBasePath ) ); wcscat( pClipname, L"\\" ); wcscat( pClipname, wszAudio1Name ); // create the timeline source // CComPtr pSource3Obj; hr = pTimeline->CreateEmptyNode( &pSource3Obj, TIMELINE_MAJOR_TYPE_SOURCE ); ASSERT( !FAILED( hr ) ); // set up source right // hr = pSource3Obj->SetStartStop( TLStart, TLStop ); CComQIPtr< IAMTimelineSrc, &IID_IAMTimelineSrc > pSource3Src( pSource3Obj ); hr |= pSource3Src->SetMediaTimes( MediaStart, MediaStop ); hr |= pSource3Src->SetMediaName( pClipname ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // tell the track about the source //-------------------------------------------- CComQIPtr< IAMTimelineTrack, &IID_IAMTimelineTrack > pTrack3( pTrack3Obj ); hr = pTrack3->SrcAdd( pSource3Obj ); ASSERT( !FAILED( hr ) ); #ifdef DO_CROSSFADE //-------------------------------------------- // put a volume effect on the source //-------------------------------------------- TLStart = 4 * UNITS; TLStop = 6 * UNITS; CComPtr< IAMTimelineObj > pTrack3FxObj; hr = pTimeline->CreateEmptyNode( &pTrack3FxObj, TIMELINE_MAJOR_TYPE_EFFECT ); ASSERT( !FAILED( hr ) ); // set up effect right // hr = pTrack3FxObj->SetStartStop( TLStart, TLStop ); hr |= pTrack3FxObj->SetSubObjectGUID( CLSID_AudMixer ); ASSERT( !FAILED( hr ) ); // add the effect // CComQIPtr< IAMTimelineEffectable , &IID_IAMTimelineEffectable > pTrack3Fable( pTrack3 ); hr = pTrack3Fable->EffectInsBefore( pTrack3FxObj, -1 ); ASSERT( !FAILED( hr ) ); //------------------------------------------------ // set the the volume envelope on the audio source //------------------------------------------------ CComPtr< IPropertySetter > pVolSetter; pVolSetter.CoCreateInstance( CLSID_PropertySetter ); CComBSTR VolParamName( "Vol" ); // the property name Param.Name = VolParamName; Param.nValues = 2; // how many values we want to set DEXTER_VALUE AudioValue[2]; memset( &AudioValue[0], 0, sizeof( Value ) ); VariantClear( &AudioValue[0].v ); V_R8( &AudioValue[0].v ) = 1.0; V_VT( &AudioValue[0].v ) = VT_R8; AudioValue[0].rt = 0 * UNITS; memset( &AudioValue[1], 0, sizeof( Value ) ); VariantClear( &AudioValue[1].v ); V_R8( &AudioValue[1].v ) = 0.0; V_VT( &AudioValue[1].v ) = VT_R8; AudioValue[1].rt = 2 * UNITS; AudioValue[1].dwInterp = DEXTERF_INTERPOLATE; hr = pVolSetter->AddProp( Param, AudioValue ); hr |= pTrack3FxObj->SetPropertySetter( pVolSetter ); ASSERT( !FAILED( hr ) ); pVolSetter.Release( ); #endif // DO_CROSSFADE //-------------------------------------------- // make another track //-------------------------------------------- CComPtr< IAMTimelineObj > pTrack4Obj; hr = pTimeline->CreateEmptyNode( &pTrack4Obj, TIMELINE_MAJOR_TYPE_TRACK ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // tell the composition about the track //-------------------------------------------- hr = pAudioComp->VTrackInsBefore( pTrack4Obj, -1 ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // create a source for the 2nd track //-------------------------------------------- TLStart = 4 * UNITS; TLStop = 8 * UNITS; MediaStart = 0 * UNITS; MediaStop = 4 * UNITS; wcscpy( pClipname, T2W( tBasePath ) ); wcscat( pClipname, L"\\" ); wcscat( pClipname, wszAudio2Name ); // create the timeline source // CComPtr pSource4Obj; hr = pTimeline->CreateEmptyNode( &pSource4Obj, TIMELINE_MAJOR_TYPE_SOURCE ); ASSERT( !FAILED( hr ) ); // set up source right // hr = pSource4Obj->SetStartStop( TLStart, TLStop ); CComQIPtr< IAMTimelineSrc, &IID_IAMTimelineSrc > pSource4Src( pSource4Obj ); hr |= pSource4Src->SetMediaTimes( MediaStart, MediaStop ); hr |= pSource4Src->SetMediaName( pClipname ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // tell the track about the source //-------------------------------------------- CComQIPtr< IAMTimelineTrack, &IID_IAMTimelineTrack > pTrack4( pTrack4Obj ); hr = pTrack4->SrcAdd( pSource4Obj ); ASSERT( !FAILED( hr ) ); #ifdef DO_CROSSFADE //-------------------------------------------- // put a volume effect on the source //-------------------------------------------- TLStart = 4 * UNITS; TLStop = 6 * UNITS; CComPtr< IAMTimelineObj > pTrack4FxObj; hr = pTimeline->CreateEmptyNode( &pTrack4FxObj, TIMELINE_MAJOR_TYPE_EFFECT ); ASSERT( !FAILED( hr ) ); // set up effect riht // hr = pTrack4FxObj->SetStartStop( TLStart, TLStop ); hr |= pTrack4FxObj->SetSubObjectGUID( CLSID_AudMixer ); ASSERT( !FAILED( hr ) ); // add the effect // CComQIPtr< IAMTimelineEffectable , &IID_IAMTimelineEffectable > pTrack4Fable( pTrack4 ); hr = pTrack4Fable->EffectInsBefore( pTrack4FxObj, -1 ); ASSERT( !FAILED( hr ) ); //------------------------------------------------ // set the the volume envelope on the audio source //------------------------------------------------ pVolSetter.CoCreateInstance( CLSID_PropertySetter ); memset( &AudioValue[0], 0, sizeof( Value ) ); VariantClear( &AudioValue[0].v ); V_R8( &AudioValue[0].v ) = 0.0; V_VT( &AudioValue[0].v ) = VT_R8; AudioValue[0].rt = 0 * UNITS; memset( &AudioValue[1], 0, sizeof( Value ) ); VariantClear( &AudioValue[1].v ); V_R8( &AudioValue[1].v ) = 1.0; V_VT( &AudioValue[1].v ) = VT_R8; AudioValue[1].rt = 2 * UNITS; AudioValue[1].dwInterp = DEXTERF_INTERPOLATE; hr = pVolSetter->AddProp( Param, AudioValue ); hr |= pTrack4FxObj->SetPropertySetter( pVolSetter ); ASSERT( !FAILED( hr ) ); // pVolSetter will be auto-freed by COM #endif // DO_CROSSFADE //---------------------------------------------- // make sure files are in their correct location //---------------------------------------------- hr = pTimeline->ValidateSourceNames( SFN_VALIDATEF_CHECK | SFN_VALIDATEF_POPUP | SFN_VALIDATEF_REPLACE, NULL, 0 ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // create the render engine //-------------------------------------------- hr = CoCreateInstance( CLSID_RenderEngine, NULL, CLSCTX_INPROC_SERVER, IID_IRenderEngine, (void**) &pRenderEngine ); ASSERT( !FAILED( hr ) ); // tell the render engine about the timeline it should look at // hr = pRenderEngine->SetTimelineObject( pTimeline ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // connect up the front end, then the back end //-------------------------------------------- hr = pRenderEngine->ConnectFrontEnd( ); hr |= pRenderEngine->RenderOutputPins( ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // get a bunch of pointers, then run the graph //-------------------------------------------- hr = pRenderEngine->GetFilterGraph( &pGraph ); hr |= pGraph->QueryInterface( IID_IMediaEvent, (void**) &pEvent ); hr |= pGraph->QueryInterface( IID_IMediaControl, (void**) &pControl ); hr |= pGraph->QueryInterface( IID_IMediaSeeking, (void**) &pSeeking ); hr |= pGraph->QueryInterface( IID_IVideoWindow, (void**) &pVidWindow ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // give the main window a meaningful title //-------------------------------------------- BSTR bstrCaption; WriteBSTR(&bstrCaption, wszTitle); hr = pVidWindow->put_Caption(bstrCaption); FreeBSTR(&bstrCaption); //-------------------------------------------- // since no user interaction is allowed, remove // system menu and maximize/minimize buttons //-------------------------------------------- long lStyle=0; hr = pVidWindow->get_WindowStyle(&lStyle); lStyle &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU); hr = pVidWindow->put_WindowStyle(lStyle); //-------------------------------------------- // run it //-------------------------------------------- hr = pControl->Run( ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // wait for it //-------------------------------------------- long EventCode = 0; hr = pEvent->WaitForCompletion( -1, &EventCode ); ASSERT( !FAILED( hr ) ); REFERENCE_TIME Start = 0; #ifdef DO_RENDERRANGE // seek the timeline back to 0 // hr = pSeeking->SetPositions( &Start, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning ); ASSERT( !FAILED( hr ) ); hr = pRenderEngine->SetRenderRange2( 2.0, 6.0 ); ASSERT( !FAILED( hr ) ); //------------------------------------------------------ // connect up the front end, then the back end if needed //------------------------------------------------------ hr = pRenderEngine->ConnectFrontEnd( ); ASSERT( !FAILED( hr ) ); if(hr == S_WARN_OUTPUTRESET) { hr |= pRenderEngine->RenderOutputPins( ); } ASSERT( !FAILED( hr ) ); //-------------------------------------------- // run it //-------------------------------------------- hr = pControl->Run( ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // wait for it //-------------------------------------------- hr = pEvent->WaitForCompletion( -1, &EventCode ); ASSERT( !FAILED( hr ) ); #endif // DO_RENDERRANGE #ifdef DO_RECONNECT //--------------------------------------------- // make a change to the timeline, however small //--------------------------------------------- CComPtr< IAMTimelineObj > pTransObj; REFERENCE_TIME InOut = -1; pTransable->GetNextTrans( &pTransObj, &InOut ); CComQIPtr< IAMTimelineTrans, &IID_IAMTimelineTrans > pTrans( pTransObj ); pTrans->SetCutsOnly( TRUE ); pTransObj.Release( ); pTrans.Release( ); hr = pTransable->GetNextTrans( &pTransObj, &InOut ); pTrans = pTransObj; hr |= pTrans->SetCutsOnly( TRUE ); ASSERT( !FAILED( hr ) ); // seek the timeline back to 0 // hr = pSeeking->SetPositions( &Start, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning ); ASSERT( !FAILED( hr ) ); //------------------------------------------------------ // connect up the front end, then the back end if needed //------------------------------------------------------ hr = pRenderEngine->ConnectFrontEnd( ); if(hr == S_WARN_OUTPUTRESET) { hr |= pRenderEngine->RenderOutputPins( ); } ASSERT( !FAILED( hr ) ); //-------------------------------------------- // run it //-------------------------------------------- hr = pControl->Run( ); ASSERT( !FAILED( hr ) ); //-------------------------------------------- // wait for it //-------------------------------------------- hr = pEvent->WaitForCompletion( -1, &EventCode ); ASSERT( !FAILED( hr ) ); #endif // DO_RECONNECT return 0; }