媒体接收器

媒体接收器 是接收媒体数据的管道对象。 媒体接收器是一个或多个媒体流的目标。 媒体接收器分为两个常规类别:

  • 呈现器 是一个媒体接收器,它提供用于播放的数据。 增强的视频呈现器(EVR)显示视频帧,音频呈现器通过声卡或其他音频设备播放音频流。

  • 存档接收器 是将数据写入文件或其他存储的媒体接收器。

它们之间的主要区别在于,存档接收器不会以固定的播放速率使用数据。 相反,它会尽快写入它接收的数据。

媒体接收器公开 IMFMediaSink 接口。 每个媒体接收器包含一个或多个 流接收器。 每个流接收器接收来自一个流的数据。 流接收器公开 IMFStreamSink 接口。 通常,应用程序不会直接创建媒体接收器。 相反,应用程序会创建一个或多个激活对象,媒体会话使用该对象来创建接收器。 接收器上的所有其他作均由媒体会话处理,应用程序不会在媒体接收器或任何流接收器上调用任何方法。 有关激活对象的详细信息,请参阅 激活对象

如果要编写自定义媒体接收器,或者想要在没有媒体会话的情况下直接使用媒体接收器,则应阅读本主题的其余部分。

流接收器

媒体接收器可以具有固定数量的流接收器,也可以支持添加和删除流接收器。 如果它具有固定数量的流接收器,IMFMediaSink::GetCharacteristics 方法将返回MEDIASINK_FIXED_STREAMS标志。 否则,可以添加和删除流接收器。 若要添加新的流接收器,请调用 IMFMediaSink::AddStreamSink。 若要删除流接收器,请调用 IMFMediaSink::RemoveStreamSink。 MEDIASINK_FIXED_STREAMS标志指示媒体接收器不支持这两种方法。 (它可能支持配置流数的其他方法,例如,在创建接收器时设置初始化参数。流接收器列表已排序。 若要按索引值枚举它们,请调用 IMFMediaSink::GetStreamSinkByIndex 方法。

流接收器还具有标识符。 流标识符在媒体接收器中是唯一的,但不必是连续的。 根据媒体接收器,流标识符可能具有与内容相关的一些含义。 例如,存档接收器可能会将流标识符写入文件头。 否则,它们是任意的。 若要按标识符获取流接收器,请调用 IMFMediaSink::GetStreamSinkById

演示文稿时钟

媒体接收器使用样本的速率由 演示文稿时钟控制。 媒体会话通过调用媒体接收器的 IMFMediaSink::SetPresentationClock 方法,选择演示文稿时钟并将其设置在媒体接收器上。 必须先在媒体接收器上设置演示时钟,然后才能开始流式处理。 每个媒体接收器都需要一个演示时钟才能运行。 媒体接收器将演示文稿时钟用于两个目的:

  • 在流式处理启动或停止时接收通知。 媒体接收器通过 IMFClockStateSink 接口接收这些通知,所有媒体接收器都必须实现该接口。

  • 确定何时应呈现样本。 当媒体接收器收到新示例时,它会从示例获取时间戳,并尝试在该演示时呈现示例。

演示文稿时钟从另一个名为 演示文稿时间源的对象派生其时钟时间。 呈现时间源公开 IMFPresentationTimeSource 接口。 某些媒体接收器可以访问准确的时钟,因此它们会公开此接口。 这意味着媒体接收器可能会根据自己的时钟提供的时间计划样本。 但是,媒体接收器不能假定这是这种情况。 无论演示文稿时钟是由媒体接收器本身还是由其他时钟驱动,它都必须始终使用演示文稿时钟的时间。

如果媒体接收器不能将速率与时钟匹配,则 GetCharacteristics 方法返回MEDIASINK_CANNOT_MATCH_CLOCK标志。 如果存在此标志,并且演示时钟使用不同的演示文稿时间源,则媒体接收器的性能可能很差。 例如,在播放期间,它可能会出现问题。

无速率 接收器是一个媒体接收器,它忽略样本上的时间戳,并在每个样本到达后立即使用数据。 无速率媒体接收器从 GetCharacteristics 方法返回MEDIASINK_RATELESS标志。 通常,此标志适用于存档接收器。 如果管道中的每个媒体接收器都是无速率的,则媒体会话使用特殊的无速率表示时钟。 此时钟的运行速度与接收器使用样本一样快。

流格式

在媒体接收器接收样本之前,客户端必须在流接收器上设置媒体类型。 若要设置媒体类型,请调用流接收器的 IMFStreamSink::GetMediaTypeHandler 方法。 此方法返回指向 IMFMediaTypeHandler 接口的指针。 使用此接口可获取首选媒体类型列表、获取当前媒体类型并设置媒体类型。

若要获取首选媒体类型的列表,请调用 IMFMediaTypeHandler::GetMediaTypeByIndex。 首选类型应作为客户端提示。 列表可能不完整或包含部分媒体类型。 部分媒体类型是没有描述有效格式所需的所有属性的媒体类型。 例如,部分视频类型可以指定颜色空间和位深度,但不能指定图像宽度或高度。 部分音频类型可以指定压缩格式和采样率,但不能指定音频通道的数量。

若要获取流接收器的当前媒体类型,请调用 IMFMediaTypeHandler::GetCurrentMediaType。 首次创建流接收器时,它可能已设置默认媒体类型,或者客户端设置一个媒体类型之前可能没有媒体类型。

若要设置媒体类型,请调用 IMFMediaTypeHandler::SetCurrentMediaType。 某些流接收器可能不支持在设置后更改类型。 因此,在设置媒体类型之前测试媒体类型非常有用。 若要测试媒体接收器是否接受媒体类型(未设置类型),请调用 IMFMediaTypeHandler::IsMediaTypeSupported

数据流

媒体接收器使用 拉取模型,这意味着流接收器会根据需要请求数据。 客户端应及时响应,以避免出现任何故障。

某些媒体接收器支持 预注册。 预注册是在演示时钟开始之前向媒体接收器提供数据的过程。 如果媒体接收器支持预注册,媒体接收器将公开 IMFMediaSinkPreroll 接口,GetCharacteristics 方法返回MEDIASINK_CAN_PREROLL标志。 预注册可确保媒体接收器准备好在演示时钟启动时显示第一个示例。 建议客户端始终在媒体接收器支持时预注册,因为它可以防止播放期间出现故障或间隙。

数据流到媒体接收器的工作原理如下:

  1. 客户端设置媒体类型和演示时钟。 媒体接收器将自身注册到演示时钟,以接收有关时钟状态更改的通知。
  2. (可选)客户端查询 IMFMediaSinkPreroll。 如果媒体接收器公开此接口,客户端将调用 IMFMediaSinkPreroll::NotifyPreroll。 否则,客户端将跳到步骤 5。
  3. 每个流接收器发送一个或多个 MEStreamSinkRequestSample 事件。 为了响应其中每个事件,客户端获取该流的下一个数据示例,并调用 IMFStreamSink::P rocessSample
  4. 当每个流接收器接收足够的预注册数据时,它会发送 MEStreamSinkPrerolled 事件。
  5. 客户端调用 IMFPresentationClock::Start 来启动演示时钟。
  6. 演示文稿时钟通过调用 IMFClockStateSink::OnClockStart通知媒体接收器时钟正在启动。
  7. 若要获取更多数据,每个流接收器都会发送 MEStreamSinkRequestSample 事件。 为了响应其中每个事件,客户端将获取下一个示例并调用 ProcessSample。 此步骤将重复,直到演示文稿结束。

大多数媒体接收器异步处理样本,因此流接收器可以一次发送多个示例请求。

在流式处理期间,客户端可以随时调用 IMFStreamSink::P laceMarkerIMFStreamSink::Flush。 下一部分将介绍标记。 刷新会导致流接收器删除已排队但尚未呈现的任何样本。

标记

标记为客户端提供了一种方法来指示流中的特定点。 标记由以下信息组成:

  • 标记类型,定义为 MFSTREAMSINK_MARKER_TYPE 枚举的成员。
  • 与标记关联的数据。 数据的含义取决于标记类型。 某些标记类型没有数据。
  • 客户端自己使用的可选数据。

若要放置标记,客户端调用 IMFStreamSink::P laceMarker。 流接收器完成处理在 PlaceMarker 调用之前接收的任何示例,然后发送 MEStreamSinkMarker 事件。

大多数媒体接收器保留挂起的样本队列,它们以异步方式进行处理。 标记事件必须通过示例处理进行序列化,因此媒体接收器应将标记放置在同一队列中。 例如,假设客户端进行以下方法调用:

  1. ProcessSample (示例 #1)
  2. ProcessSample (示例 #2)
  3. PlaceMarker (标记 #1)
  4. ProcessSample (示例 #3)
  5. PlaceMarker (标记 #2)

在此示例中,流接收器必须在处理示例 #2 后为标记 #1 发送 MEStreamSinkMarker 事件,并在处理示例 #3 之后发送标记 #2 的事件。

如果客户端刷新流接收器,流接收器将立即处理队列中的任何标记。 它将状态代码设置为对这些事件E_ABORT。

某些标记包含与媒体接收器相关的信息:

  • MFSTREAMSINK_MARKER_TICK:指示流中存在差距。 下一个示例将是不连续的。
  • MFSTREAMSINK_MARKER_ENDOFSEGMENT:指示流的段或结尾。 下一个示例(如果有)可能是不连续的。
  • MFSTREAMSINK_MARKER_EVENT:包含事件。 根据事件类型和媒体接收器的实现,媒体接收器可以处理事件或忽略它。

状态更改

媒体接收器通过媒体接收器的 IMFClockStateSink 接口通知演示文稿时钟的状态更改。 当客户端设置演示时钟时,媒体接收器会调用 IMFPresentationClock::AddClockStateSink 来注册来自时钟的通知。 下表总结了媒体接收器在响应时钟状态更改时的行为方式。

时钟状态更改 来样加工 标记处理
OnClockStart 处理时间戳等于或晚于时钟开始时间的示例。 处理标记之前收到的所有样本时,发送 MEStreamSinkMarker 事件。
OnClockPause 暂停时,媒体接收器可能会失败 ProcessSample
如果媒体接收器在暂停时接受样本,则必须将它们排队,直到时钟重新启动。 暂停时不要处理任何排队的样本。
如果有排队的样本,请将标记置于同一队列中。 时钟重启时发送标记事件。
否则,请立即发送标记事件。
OnClockRestart 处理暂停时排队的任何示例,然后将 OnClockStart处理。 为排队标记发送 MEStreamSinkMarker 事件(使用示例处理进行序列化),然后处理与 onClockStart相同。
OnClockStop 删除所有排队的样本。 对 ProcessSample 的进一步调用可能会失败。 发送排队的标记事件。 在后续调用 PlaceMarker时,立即发送标记事件。

 

此外,流接收器在完成状态转换后必须发送以下事件:

敲定

某些媒体接收器需要在交付最后一个示例后执行额外的处理步骤。 通常,此要求适用于存档接收器,这些接收器必须将标头或索引写入文件中。 如果媒体接收器需要任何最终处理,它将公开 IMFFinalizableMediaSink 接口。

客户端传递最后一个示例后,客户端会查询此接口。 如果媒体接收器支持接口,客户端将调用 IMFFinalizableMediaSink::BeginFinalize 异步执行最终处理。 此方法遵循标准媒体基础异步模型,异步回调方法中所述。 媒体接收器可以假定客户端将调用 beginFinalize 。 未能调用 BeginFinalize 可能会导致错误创作的文件。

关闭

当客户端使用媒体接收器完成后,客户端将调用 IMFMediaSink::Shutdown。 在此方法中,媒体接收器应中断任何循环引用计数。 通常,媒体接收器和流接收器之间将有循环引用。

如果使用事件队列帮助程序对象实现 IMFMediaEventGenerator,请对事件队列调用 IMFMediaEventQueue::Shutdown。 此方法关闭事件队列,并指示当前正在等待事件的任何调用方。

关闭后,媒体接收器上的所有方法都会返回MF_E_SHUTDOWN,但 IUnknown 方法除外。

媒体接收器接口

下表列出了媒体接收器可以通过 QueryInterface公开的标准接口。 媒体接收器还可以公开自定义接口。

接口 描述
IMFMediaSink 媒体接收器的主接口。 (必需。
IMFClockStateSink 用于在演示文稿时钟更改状态时通知媒体接收器。 (必需。
IMFFinalizableMediaSink 如果媒体接收器必须执行最终处理步骤,则实现。 (可选)。)
IMFGetService 如果媒体接收器公开任何服务接口,则实现。 (可选)。)
IMFMediaEventGenerator 如果媒体接收器发送任何事件,则实现。 (可选)。)
IMFMediaSinkPreroll 如果媒体接收器支持预注册,则实现。 (可选)。)
IMFPresentationTimeSource 如果媒体接收器可以提供演示时钟的时间源,则实现。 (可选)。)
IMFQualityAdvise 如果媒体接收器可以调整播放质量,则实现。 (可选)。)

 

(可选)媒体接收器可以实现以下接口即服务。

服务接口 描述
IMFRateSupport 报告支持播放速率的范围。

 

有关服务接口和 IMFGetService的详细信息,请参阅 服务接口

流接收器接口

流接收器必须通过 QueryInterface公开以下接口。

接口 描述
IMFStreamSink 流接收器的主接口。 (必需。
IMFMediaEventGenerator 队列事件。 IMFStreamSink 接口继承此接口。 (必需。

 

目前没有为流接收器定义服务接口。

流接收器事件

下表列出了为泛型流接收器定义的事件。 流接收器还可以发送此处未列出的自定义事件。

事件 描述
MEStreamSinkFormatChanged 流接收器的媒体类型不再有效。 (可选)。)
MEStreamSinkMarker 已处理标记。 (必需。
MEStreamSinkPaused 流接收器已暂停。 (必需。
MEStreamSinkPrerolled 预注册已完成。 (可选)。)
MEStreamSinkRateChanged 流接收器已更改播放速率。 (可选)。)
MEStreamSinkRequestSample 请求一个新示例。 (必需。
MEStreamSinkScrubSampleComplete 清理请求已完成。 (可选)。)
MEStreamSinkStarted 流接收器已启动。 (必需。
MEStreamSinkStopped 流接收器已停止。 (必需。

 

目前没有为媒体接收器定义常规用途事件。 某些媒体接收器可能会发送自定义事件。

Media Foundation Pipeline

Media Foundation 体系结构