内部时钟ReferenceTime
Filter Graph 管理器的一个功能是将图形中的所有filter
同步到同一时钟(称为 ReferenceTime
)。
任何公开 IReferenceClock 接口的对象都可以充当ReferenceTime
。 ReferenceTime
可能由 DirectShow filter
提供,通常是音频呈现器,它有权访问硬件计时器。 作为回退,Filter Graph 管理器可以使用系统时间。
从名义上说,ReferenceTime
以 100 纳秒的间隔测量时间,尽管时钟的实际准确度可能较低。 若要检索时钟的当前时间,请调用 IReferenceClock::GetTime 方法。 时钟的基线(开始计数的时间)取决于实现,因此 GetTime 返回的值本质上没有意义。 重要的是图形开始运行时的增量。
尽管ReferenceTime
的准确性可能会有所不同,但 GetTime 方法返回的时间保证单调增加。 换句话说,时钟时间永远不会倒退。 如果ReferenceTime
正在从硬件源生成时钟时间,并且硬件时钟向后跳 (例如,如果对时钟) 进行调整, 则 GetTime 方法应继续返回上次报告的时间,直到硬件时钟赶上为止。 有关详细信息,请参阅 CBaseReferenceClock 类。
默认的ReferenceTime
来源(不重要)
filter
图形管理器在图形运行时自动选择ReferenceTime
。 它使用以下算法来选择时钟:
- 如果应用程序选择了一个时钟 (请参阅下面) ,请使用该时钟。
- 如果图形包含支持 IReferenceClock 的实时源
filter
,请使用该filter
。 有关实时源的定义,请参阅 实时源。 - 如果图形不包含任何实时源
filter
,请在图形中使用支持 IReferenceClock 的任何filter
,从呈现器开始,上游工作。 首选连接的filter
,而不是未连接的filter
。 - 如果图形呈现音频流,则算法中的此步骤通常会选择音频呈现器
filter
。 - 如果没有
filter
提供合适的时钟,请使用基于系统时间的系统ReferenceTime
。
设置ReferenceTime
(不重要)
应用程序可以通过在 Filter Graph Manager 上调用 IMediaFilter::SetSyncSource 方法来选择时钟。 仅当有特定原因首选另一个时钟时,才应执行此操作。
可以通过调用值为 NULL 的 SetSyncSource 来指示 Filter Graph 管理器不要使用ReferenceTime
。 例如,可以执行此操作以尽快处理MediaSample
。 若要还原默认ReferenceTime
,请在 Filter Graph 管理器上调用 IFilterGraph::SetDefaultSyncSource 方法。
每当ReferenceTime
更改时,Filter Graph 管理器都会通过调用其 IMediaFilter::SetSyncSource 方法通知每个filter
。 应用程序绝不应在filter
上调用此方法。
引用时间和流时间
DirectShow 定义了两个相关的时钟时间:引用时间和流时间。引用时间 是ReferenceTime
返回的绝对时间。 (请参阅 ReferenceTime
.)。流时间 相对于图形上次开始运行的时间进行定义。
- 当图形运行时,流时间等于引用时间减去开始时间。
- 当图形暂停时,流时间将停留在暂停的流时间。
- 执行查找操作后,流时间将重置为零。
- 当图形停止时,流时间未定义。
- 当
MediaSample
具有时间戳 t 时,这意味着应在流时间 t 呈现该MediaSample
。 因此,流时间也称为 演示时间。
当应用程序调用 IMediaControl::Run 来运行filter
图时,Filter Graph 管理器在每个filter
上调用 IMediaFilter::Run 。 为了补偿filter
开始运行所花费的轻微时间,Filter Graph 管理器在将来会稍微指定一个启动时间。
时间戳
时间戳采用流时间
时间戳定义MediaSample
的开始和完成时间,以流时间度量。 时间戳有时称为 呈现时间。 阅读本文的其余部分时,请务必记住,并非所有格式都以相同的方式使用时间戳。 例如,并非所有 MPEG 样本都带有时间戳。 在 MPEG filter
图中,时间戳不会应用于每个帧,直到它们从解码器输出。
当呈现器filter
收到MediaSample
时,它会根据时间戳计划呈现。 如果样本迟到或没有时间戳,filter
将立即呈现样本。 否则,filter
将等到MediaSample
的开始时间,然后才呈现MediaSample
。 (它通过调用 IReferenceClock::AdviseTime 方法等待开始时间。)
源filter
和分析程序filter
负责为其处理的样本设置正确的时间戳。 请遵循以下准则。
- 文件播放:第一个
MediaSample
带有时间戳,开始时间为零。 后续时间戳由样本长度和播放速率决定,后者本身由文件格式决定。 分析文件的filter
负责计算正确的时间戳, (例如 AVI 拆分器) 。 - 视频和音频捕获:每个样本都标有时间戳,其开始时间等于捕获时的流时间,但需要注意预览引脚中的视频帧 (而不是捕获引脚) 没有时间戳。 由于图形延迟,带有捕获时间标记的视频帧将始终晚于视频呈现器到达。 这可能会导致呈现器在试图进行质量控制时删除帧。 有关质量控制的信息,请参阅 质量控制管理。
- 音频捕获:音频捕获
filter
使用自己的一组缓冲区,这些缓冲区与音频驱动程序使用的缓冲区是分开的。 音频驱动程序按固定间隔填充捕获filter
的缓冲区。 间隔取决于驱动程序,但通常不超过 10 毫秒。 音频样本上的时间戳反映了驱动程序填充音频捕获filter
缓冲区的时间。 这些时间可能略有不准确,尤其是在应用程序使用非常小的缓冲区时。 但是,媒体时间将准确反映缓冲区中的音频样本数。 - 复用
filter
:根据输出格式,多路复用filter
可能需要生成时间戳,也可能不需要生成时间戳。 例如,AVI 文件格式使用没有时间戳的固定帧速率,因此 AVI Muxfilter
假定样本在大约正确的时间到达。 但是,如果传入时间戳显示大于一帧的间隙,AVI 复用器会写入一个索引条目,其大小为零,以指示丢弃的帧。 在文件播放时,会在运行时生成新的时间戳,如前所述。
若要在MediaSample
上设置时间戳,请调用 IMediaSample::SetTime 方法。
MediaTime
(可选)filter
还可以指定样本的 媒体时间 。 在视频流中,媒体时间表示帧数。 在音频流中,媒体时间表示数据包中的样本数。 例如,如果每个数据包包含 44.1 千赫 (kHz) 音频的 1 秒,则第一个数据包的媒体开始时间为 0,媒体停止时间为 44100。 在可查找流中,媒体时间始终相对于流的开始时间。 例如,假设你从 15-fps 视频流的开始时间开始 2 秒。 搜寻后的第一个MediaSample
的时间戳为零,但媒体时间为 30。
呈现器和复用器filter
可以使用媒体时间,通过检查间隙来确定帧或样本是否已丢弃。 但是,不需要filter
来设置媒体时间。 若要在MediaSample
上设置媒体时间,请调用 IMediaSample::SetMediaTime 方法。
实时源(也称为 推送源)实时接收数据。
MediaSample
包括视频捕获和网络广播。 通常,实时源无法控制数据的到达速率。如果存在以下任一情况,则filter
被视为实时源:
-
filter
从 IAMFilterMiscFlags::GetMiscFlags 方法返回AM_FILTER_MISC_FLAGS_IS_SOURCE标志,并且至少一个其输出引脚公开 IAMPushSource 接口。 -
filter
公开 IKsPropertySet 接口,并具有捕获引脚 (PIN_CATEGORY_CAPTURE) 。 有关详细信息 ,请参阅固定属性集 。
如果实时源filter
提供时钟,filter
关系图管理器在选择图形ReferenceTime
时会首选该时钟。 有关详细信息 ,请参阅ReferenceTime
。
延迟
filter
的延迟是filter
处理样本所需的时间。 对于实时源,延迟取决于用于保存样本的缓冲区的大小。 例如,假设filter
图的延迟为 33 毫秒 (毫秒的视频源) ,音频源的延迟为 500 毫秒。 在匹配的音频样本到达音频呈现器之前,每个视频帧到达视频呈现器大约 470 毫秒。 除非图形补偿差异,否则音频和视频不会同步。
可以通过 IAMPushSource 接口同步实时源。 Filter Graph 管理器不会同步实时源,除非应用程序通过调用 IAMGraphStreams::SyncUsingStreamOffset 方法启用同步。 如果启用了同步,filter
关系图管理器将查询 IAMPushSource 的每个源filter
。 如果filter
支持 IAMPushSource,filter
关系图管理器会调用 IAMLatency::GetLatency 来检索filter
的预期延迟。 (IAMPushSource 接口从组合的延迟值继承 IAMLatency.) ,filter
关系图管理器确定图中的最大预期延迟。 然后,它调用 IAMPushSource::SetStreamOffset 为每个源filter
提供流偏移量,该filter
会将该偏移量添加到它生成的时间戳中。
此方法主要用于实时预览版。 但请注意,实时捕获设备上的预览固定 ((如相机) )不会在它提供的MediaSample
上设置时间戳。 因此,若要将此方法用于实时捕获设备,必须从捕获引脚预览。 有关详细信息,请参阅 DirectShow 视频捕获filter
。
目前,VFW 捕获filter
和音频捕获filter
支持 IAMPushSource 接口。
速率匹配
如果呈现器filter
使用一个ReferenceTime
计划样本,但源filter
使用不同的时钟生成样本,则播放时可能会出现故障。 呈现器运行速度可能比源快,从而导致数据间隙。 或者,它的运行速度可能比源慢,导致样本“堆起来”,直到图在某个时候会丢弃样本。 通常,实时源无法控制其生产速率,因此呈现器应将速率与源匹配。
目前,只有音频呈现器执行速率匹配,因为音频播放中的故障比视频中的故障更明显。 若要执行速率匹配,音频呈现器必须选择与速率匹配的内容。 它使用以下算法:
- 如果图形未使用
ReferenceTime
,则音频呈现器不会尝试匹配速率。 (每当图形没有ReferenceTime
时,样本始终在到达时立即呈现。) 否则,如果有图形的ReferenceTime
,音频呈现器将使用前面所述的条件检查是否存在实时源上游。 否则,音频呈现器与速率不匹配。 - 如果存在实时源上游,并且该源在其输出引脚上公开 IAMPushSource 接口,则音频呈现器将调用 IAMPushSource::GetPushSourceFlags。 它会查找以下标志之一:
- AM_PUSHSOURCECAPS_INTERNAL_RM。 此标志表示源
filter
具有自己的速率匹配机制,因此音频呈现器不匹配速率。 - AM_PUSHSOURCECAPS_NOT_LIVE。 此标志意味着源
filter
不是真正的实时源,即使它公开了 IAMPushSource 接口。 因此,音频呈现器与速率不匹配。 - AM_PUSHSOURCECAPS_PRIVATE_CLOCK。 此标志表示源
filter
使用专用时钟生成时间戳。 在这种情况下,音频呈现器将速率与时间戳匹配。 (如果MediaSample
没有时间戳,呈现器将忽略此标志。)
- AM_PUSHSOURCECAPS_INTERNAL_RM。 此标志表示源
- 如果 GetPushSourceFlags 未返回任何标志 (零) ,则音频呈现器的行为取决于图形时钟以及样本是否具有时间戳:
- 如果音频呈现器不是图形时钟,并且样本具有时间戳,则音频呈现器会将速率与时间戳匹配。
- 如果样本没有时间戳,音频呈现器会尝试匹配传入音频数据的速率。
- 如果音频呈现器是图形时钟,它将尝试匹配传入的数据速率。
最后一种情况的原因如下:如果音频呈现器是ReferenceTime
,并且源filter
使用相同的时钟来生成时间戳,则音频呈现器无法将速率与时间戳匹配。 如果确实如此,实际上它会尝试将速率与自身匹配,这可能导致时钟偏移。 因此,在这种情况下,呈现器与传入音频数据的速率匹配。