(DirectX系列09)DirectShow EDS应用编码分析
DES (DirectShow Editing Services),是一套基于DirectShow核心框架的编程接口。DES的出现,简化了视频编辑任务,弥补了DirectShow对于媒体文件非线性编辑支持的先天性不足。但是,就技术本身而言,DES并没有超越DirectShow Filter架构,而只是DirectShow Filter的一种增强应用。
本章通过DirectX 下timelinetest为例子详细介绍Eds应用编码实现,其中主要包括创建事件线对象、在轨道上加入音频过度、在轨道上加入视频过度、使用控制引擎等。
创建事件线对象
这是在所有操作之前必须要做的事情,同样也贯穿这个编码过程,如下代码;
hr = CoCreateInstance(
CLSID_AMTimeline,
NULL,
CLSCTX_INPROC_SERVER,
IID_IAMTimeline,
(void**) &pTimeline
);
if(FAILED( hr )) {
Err(_T("Could not create timeline"));
return hr;
}
创建组,并标记为音频组或视频组
DirectShow 通过CreateEmptyNode来创建EDS组,并采用SetMediaType函数来标识音视频组,设置为MEDIATYPE_Video标识为视频组,设置为MEDIATYPE_Audio为音频组,如下代码;
// 创建视频组
hr = pTimeline->CreateEmptyNode( &pVideoGroupObj, TIMELINE_MAJOR_TYPE_GROUP );
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 );
// 创建音频组
hr = pTimeline->CreateEmptyNode( &pAudioGroupObj, TIMELINE_MAJOR_TYPE_GROUP );
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 );
在轨道上加入视频过渡处理,并实现过渡子对象的设置
CComPtr< IAMTimelineObj > pTrack2Obj;
hr = pTimeline->CreateEmptyNode( &pTrack2Obj, TIMELINE_MAJOR_TYPE_TRACK );
if(FAILED( hr ))
{
Err(_T("Could not create second track"));
return hr;
}
hr = pRootComp->VTrackInsBefore( pTrack2Obj, -1 );
if(FAILED( hr ))
{
Err(_T("Could not insert second track"));
return hr;
}
TLStart = 0 * UNITS;
TLStop = 8 * UNITS;
MediaStart = 0 * UNITS;
MediaStop = 8 * UNITS;
(void)StringCchCopyW( pClipname, NUMELMS(pClipname), T2W( tBasePath ) );
(void)StringCchCatW( pClipname, NUMELMS(pClipname), L"///0" );
(void)StringCchCatW( pClipname, NUMELMS(pClipname),wszVideo2Name );
// create the timeline source
//
CComPtr<IAMTimelineObj> 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
REFERENCE_TIME TransStart = 0 * UNITS;
REFERENCE_TIME TransStop = 4 * UNITS;
// create the timeline effect
//
CComPtr<IAMTimelineObj> 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 ) );
在轨道中加入声音效果
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 ) );
控制引擎的使用
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 == (HRESULT) 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 == (HRESULT) 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 );