演示文稿时钟

演示文稿时钟是一个对象,用于生成演示文稿的时钟时间。 演示时钟报告的时间称为 演示时间。 演示文稿中的所有流都与演示时间同步。 演示时钟公开以下接口。

接口 说明
IMFPresentationClock 用于使用演示时钟的主要接口。
IMFRateControl 控制时钟速率。
IMFTimer 提供计时器回调。
IMFShutdown 关闭演示时钟。

 

媒体接收器使用呈现时间来计划何时呈现示例。 每当媒体接收器收到新样本时,它就会从样本中获取时间戳,并在指示的时间或尽可能接近该时间呈现样本。 由于拓扑中的所有媒体接收器共享相同的演示时钟,因此会同步多个流 (,例如音频和视频) 。 媒体源和转换不使用演示时钟,因为它们不安排何时提供示例。 相反,每当管道请求新示例时,它们就会生成示例。

如果使用媒体会话进行播放,媒体会话将处理有关创建演示文稿时钟、选择时间源和通知媒体接收器的所有详细信息。 应用程序可能会在播放期间使用演示时钟来获取当前演示时间,但否则不会在演示文稿时钟上调用任何方法。

时钟时间和时钟状态

若要从演示时钟获取最新的时钟时间,请调用 IMFPresentationClock::GetTime。 时钟时间始终以 100 纳秒为单位,因此一秒为 10,000,000 (10^7) 刻度。 这对应于 10 MHz 的频率。

演示时钟有三种状态:“正在运行”、“已暂停”和“已停止”。

默认情况下,时钟以 1.0 的速率前进,这意味着每 100 纳秒 1 个时钟周期。 若要更改时钟前进的速率,请查询 IMFRateControl 接口的表示时钟,并调用 IMFRateControl::SetRate

对象可以接收状态更改通知, (包括从演示时钟) 速率更改。 若要接收通知,请实现 IMFClockStateSink 接口,并在演示时钟上调用 IMFPresentationClock::AddClockStateSink 。 在关闭之前,请调用 IMFPresentationClock::RemoveClockStateSink 以取消注册对象。 媒体接收器使用此机制从时钟接收通知。

演示时间

媒体接收器尝试计划每个样本,以便样本在正确的时间或尽可能接近正确的时间呈现。 以下定义适用:

  • 演示时间。 应呈现示例的时间。 时间以 100 纳秒为单位提供。
  • 媒体时间。 相对于内容开始的时间。 例如,如果视频文件的长度为 10 秒,则文件中间的点的媒体时间为 5 秒。
  • 时间戳。 媒体示例上标记的时间。 若要获取时间戳,请调用 IMFSample::GetSampleTime。 当媒体源生成示例时,它将时间戳设置为等于媒体时间。 媒体会话将时间戳转换为演示时间。

默认情况下,媒体时间和演示时间是相同的,例如,如果视频帧在源文件中出现 5 秒,则媒体时间和演示时间均为 5 秒。 如果使用 Sequencer Source,则计时模型会稍微复杂一些,以实现段之间的平滑转换。 有关 sequencer 源的计时模型的详细信息,请参阅 序列呈现时间

媒体源始终将时间戳设置为等于媒体时间。 如果演示时间与媒体时间不一致,媒体会话将转换媒体样本上的时间戳。 接收器收到样本时,样本的时间戳已转换为演示时间。 接收器根据演示时钟的当前时间计划示例。 (无速率接收器是一个例外,因为它们会忽略呈现时钟。)

如果应用程序查找到新位置,媒体会话会在指定的查找时间重启演示时钟。 例如,如果应用程序在文件中查找 5 秒的位置,则媒体会话会在 5 秒处启动时钟。 如果查找时间不落在关键帧边界上,则媒体源可能会提供具有略早一点时间戳的样本。 这是必需的,以便解码器可以解码所有帧。 媒体会话在样本到达媒体接收器之前删除或剪裁样本,以匹配请求的查找时间。 例如,如果查找时间为 5 秒,则第一个音频样本可能从 4.5 秒开始。 媒体会话将从第一个解码的音频样本中剪裁前 0.5 秒。

创建演示文稿时钟

若要创建演示文稿时钟,请调用 MFCreatePresentationClock。 若要关闭时钟,请查询 IMFShutdown 接口并调用 IMFShutdown::ShutdownMFCreatePresentationClock 的调用方负责调用 Shutdown;在大多数情况下,这是媒体会话,而不是应用程序。

演示时间源

尽管其名称如此,但表示时钟实际上并不实现时钟。 相反,它从另一个对象(称为 呈现时间源)获取时钟时间。 时间源可以是生成准确时钟周期并公开 IMFPresentationTimeSource 接口的任何对象。 下图显示了此过程。

显示演示时钟与演示时间源之间的关系的关系图

首次创建演示文稿时钟时,它没有时间源。 若要设置时间源,请使用指向时间源的 IMFPresentationTimeSource 接口的指针调用 IMFPresentationClock::SetTimeSource。 时间源支持与演示时钟相同的状态, (运行、暂停和停止) ,并且必须实现 IMFClockStateSink 接口。 演示时钟使用此接口通知时间源何时更改状态。 这样,时间源会提供时钟周期,但演示时钟会启动时钟中的状态更改。

某些媒体接收器可以访问准确的时钟,因此会公开 IMFPresentationTimeSource 接口。 具体而言,音频呈现器可以使用声音卡的频率作为时钟。 在音频播放中,音频呈现器充当时间源非常有用,以便将视频同步到音频播放速率。 这通常比尝试将音频与外部时钟匹配的结果更好。

Media Foundation 还提供基于系统时钟的演示时间源。 若要创建此对象,请调用 MFCreateSystemTimeSource。 当没有媒体接收器提供时间源时,可以使用系统时间源。

一般情况下,媒体接收器必须使用提供给它的演示时钟,而不管演示文稿时钟使用哪个时间源。 即使媒体接收器实现了 IMFPresentationTimeSource,此规则也适用。 如果演示时钟使用其他某个时间源,媒体接收器必须遵循该时间源,而不是它自己的内部时钟。

媒体接收器不遵循演示时钟有两种情况:

  • 某些媒体接收器是 无速率的。 如果媒体接收器是无速率的,它将尽可能快地使用样本,而无需根据演示时钟安排样本。 通常,无速率接收器将数据写入文件,因此最好尽快完成操作。 无速率接收器在其 IMFMediaSink::GetCharacteristics 方法中返回MEDIASINK_RATELESS标志。 当拓扑中的所有接收器都是无速率的时,媒体会话会尽快通过管道推送数据。

  • 某些媒体接收器无法将速率与除自身以外的时间源匹配。 如果是这样,接收器将在其 GetCharacteristics 方法中返回MEDIASINK_CANNOT_MATCH_CLOCK标志。 管道仍然可以使用其他时间源,但结果将低于最佳状态。 接收器可能会落后并导致播放过程中出现故障。

Media Foundation 平台 API