演示文稿时钟

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

接口 描述
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,则计时模型会稍微复杂一些,以便在段之间实现平滑转换。 有关序列器源的计时模型的详细信息,请参阅 序列呈现时间

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

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

创建演示文稿时钟

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

演示文稿时间源

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

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

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

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

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

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

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

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

  • 某些媒体接收器无法将速率与时间源匹配,而不是自己。 如果是这样,接收器在其 GetCharacteristics 方法中返回MEDIASINK_CANNOT_MATCH_CLOCK标志。 管道仍然可以使用另一个时间源,但结果将低于最佳状态。 接收器可能会落后,并在播放期间造成故障。

媒体基础平台 API