DirectShow入门之Directshow的基本技巧 5

 

DirectShow入门之Directshow的基本技巧

2006-10-13 07:00作者:智慧的鱼出处:天极开发责任编辑:方舟

3、查找媒体类型

  每个pin都支持一个IPin::EnumMediaTypes方法,可以来枚举pin支持的媒体类型。它返回一个IEnumMediaTypes接口,这个接口的方法IEnumMediaTypes::Next返回一个指向AM_MEDIA_TYPE类型的指针。可以参考上面的代码来遍历pin所支持的媒体类型。

  Seeking Filter graph

  主要讲述了如何在一个媒体数据流中定位,任意指定开始播放的位置。 

  1、检查是否支持seek

  Directshow通过IMediaSeeking接口支持seeking。Filter graph管理器支持这个接口,但是实际seeking的功能是有graph中的filter来实现的。

  有一些数据是不能seek的,例如,你不可能seek从照相机中采集的活动的视频流。如果一个数据流可以被seek,但是,seek的类型还分以下几种类型,可以给你的数据流选择一种

  1) 定位到数据流中的一个绝对位置

  2) 返回数据流的持续时间

  3) 返回数据流中的当前播放位置

  4) 回放。

  IMediaSeeking接口定义了一套标志AM_SEEKING_SEEKING_CAPABILITIES,用来描述可能支持的seek功能。

typedef enum AM_SEEKING_SeekingCapabilities {
 AM_SEEKING_CanSeekAbsolute = 0x1,
 AM_SEEKING_CanSeekForwards = 0x2,
 AM_SEEKING_CanSeekBackwards = 0x4,
 AM_SEEKING_CanGetCurrentPos = 0x8,
 AM_SEEKING_CanGetStopPos = 0x10,
 AM_SEEKING_CanGetDuration = 0x20,
 AM_SEEKING_CanPlayBackwards = 0x40,
 AM_SEEKING_CanDoSegments = 0x80,
 AM_SEEKING_Source = 0x100
} AM_SEEKING_SEEKING_CAPABILITIES;

  可以通过IMediaSeeking::GetCapabilities查看数据流支持的seek能力都有哪些。应用程序可以采取 &测试每一项。例如,下面的代码检查了graph是否可以seek 一个任意的位置

DWORD dwCap = 0;
HRESULT hr = pSeek->GetCapabilities(&dwCap);
if (AM_SEEKING_CanSeekAbsolute & dwCap)
{
 // Graph can seek to absolute positions.
}

  2、Setting and Retrieving the Position

  Filter graph包含两个位置,当前位置和停止位置,定义如下:

  1) 当前位置,当一个graph正处于运行的时候,当前位置就是当前的回放位置,相对于开始的位置而言。如果graph处于停止或者暂停状态的时候,当前位置就是数据流下次开始播放的位置点。

  2) 停止位置,停止位置就是数据流将要停止的位置,当一个graph到达一个停止位置时,将没有数据流,filter graph管理器将会发送一个EC_COMPLETE事件。

  可以通过IMediaSeeking::GetPositions方法可以获取这些位置值。返回值都是相对于原始的开始位置。

  通过IMediaSeeking::SetPositions方法可以seek一个新的位置,见下面:

#define ONE_SECOND 10000000
REFERENCE_TIME rtNow = 2 * ONE_SECOND, 
rtStop = 5 * ONE_SECOND;
hr = pSeek->SetPositions(
&rtNow, AM_SEEKING_AbsolutePositioning, 
&rtStop, AM_SEEKING_AbsolutePositioning
);

  注:1秒是10,000,000参考时间单位。为了方便,这个例子将这个值定义为ONE_SECOND,如果你使用的dshow的基类,常量CUITS的值和这个值相等。

  RtNow参数指定新的当前位置,第二个参数用来标示如何来定位rtNow参数。在这个例子中,AM_SEEKING_AbsolutePositioning 标志表示rtNow指定的位置是一个绝对的位置。RtStop参数指定了停止时间,最后一个参数也指定了绝对位置。

  如果想指定一个相对的位置,可以指定一个AM_SEEKING_RelativePositioning参数,为了设置这个位置不能改变,可以指定一个AM_SEEKING_NoPositioning参数。此时,参考时间应该设置为NULL。下面的例子将位置向前seek 10秒,然后停止位置不变。

REFERENCE_TIME rtNow = 10 * ONE_SECOND;
hr = pSeek->SetPositions(
 &rtNow, AM_SEEKING_RelativePositioning, 
 NULL, AM_SEEKING_NoPositioning
);

  3、Setting the Playback Rate

  调用IMediaSeeking::SetRate方法可以改变回放的速率。通过将新的速率设置成原来速率的倍数就可以设置新的速率,例如,pSeek->SetRate(2.0),将新的速率设置为原来速率的两倍。比率大于1说明回放的速度比原来的大,如果介于0和1之间,就比正常的速度慢。

  如果我们不考虑回放速率,当前位置和停止位置相对于开始位置都是不变的。举个例子,如果我们有一个可以播放20秒的文件,将当前时间设置为10秒就会将播放位置设置到中间,如果播放的速率提高要原来的2倍,如果停止时间是20秒,你将播放位置设置到原来的10秒处,结果现在只能播放5秒了,因为速度提高了两倍。

  4、Time Formats For Seek Commands

  IMediaSeeking接口中的许多函数的参数都要求指定一个位置值,比如当前位置,或者停止位置,缺省的情况下这些参数是以of 100 nanoseconds为时间单位的,称为参考时间,任何支持seek的filter必须支持按参考时间来进行定位。一些filter也支持采取其他时间单位进行定位。例如,根据指定的桢的数量,或在数据流偏移的字节数进行定位。 

  这种用来定位的时间单位称为时间格式,采用一个GUID来标示。Directshow定义了一系列的时间格式,详细地可以参考SDK。第三方也可以定义自己的时间格式。

  为了确定graph中的当前的filter是否支持特定的时间格式,可以调用
IMediaSeeking::IsFormatSupported方法,如果filter支持该时间格式,该函数返回ok否则返回false或者一个错误码。如果filter支持某种指定的时间格式,可以调用IMediaSeeking::SetTimeFormat方法切换到其他的时间格式。如果SetTimeFormat方法成功,下面的seek命令就要使用新的时间格式。

  下面的代码检查graph是否支持用桢的数量进行定位,如果支持,定位到第20桢。

hr = pSeek->IsFormatSupported(&TIME_FORMAT_FRAME);
if (hr == S_OK)
{
 hr = pSeek->SetTimeFormat(&TIME_FORMAT_FRAME);
 if (SUCCEEDED(hr))
 {
  // Seek to frame number 20.
  LONGLONG rtNow = 20;
  hr = pSeek->SetPositions(&rtNow, AM_SEEKING_AbsolutePositioning,0, AM_SEEKING_NoPositioning);
 }
}

  6、如何设置Graph时钟(Setting Graph Clock)

  当你构建了一个graph后,graph管理器会自动地给你的graph选择一个参考时钟的。Graph中的所有filter都同步于时钟。特别的,Renderer filter还要根据参考时钟的时间来决定每一个sample的Presentation 时间。

  通常的情况下,应用程序是没有必要重新设置graph管理器选择好的参考时钟的。但是,如果你想修改参考时钟,你可以通过graph管理器提供的IMediaFilter::SetSyncSource方法来重新设置参考时钟。这个方法的参数是一个时钟的IReferenceClock接口指针。可以在graph停止的时候调用这个函数,下面的例子演示了如何指定一个时钟

IGraphBuilder *pGraph = 0;
IReferenceClock *pClock = 0;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
IID_IGraphBuilder, (void **)&pGraph);
// Build the graph.
pGraph->RenderFile(L"C://Example.avi", 0);
// Create your clock.
hr = CreateMyPrivateClock(&pClock);
if (SUCCEEDED(hr))
{
 // Set the graph clock.
 IMediaFilter *pMediaFilter = 0;
 pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);
 pMediaFilter->SetSyncSource(pClock);
 pClock->Release();
 pMediaFilter->Release();
}

  这段代码假定CreateMyPrivateClock 是应用程序定义的一个函数,用来创建一个时钟,然后返回一个IReferenceClock接口。

  你也可以在graph没有设置时钟的情况下运行graph。当SetSyncSource 函数的参数为NULL的时候就给graph设置了一个空的参考时钟。如果graph没有时钟,graph将运行的快许多。因为renderer 不用再按照sample的presentation 时间了,只要sample到达了renderer filter,就可以立即被提交。所以,当你想处理数据尽可能快,而不是还要考虑预览的实际时间,你就可以给graph设置一个空的时间。

你可能感兴趣的:(DirectShow入门之Directshow的基本技巧 5)