媒体接收器
媒体接收器 是接收媒体数据的管道对象。 媒体接收器是一个或多个媒体流的目标。 媒体接收器分为两个常规类别:
呈现器 是一个媒体接收器,它提供用于播放的数据。 增强的视频呈现器(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标志。 预注册可确保媒体接收器准备好在演示时钟启动时显示第一个示例。 建议客户端始终在媒体接收器支持时预注册,因为它可以防止播放期间出现故障或间隙。
数据流到媒体接收器的工作原理如下:
- 客户端设置媒体类型和演示时钟。 媒体接收器将自身注册到演示时钟,以接收有关时钟状态更改的通知。
- (可选)客户端查询 IMFMediaSinkPreroll。 如果媒体接收器公开此接口,客户端将调用 IMFMediaSinkPreroll::NotifyPreroll。 否则,客户端将跳到步骤 5。
- 每个流接收器发送一个或多个 MEStreamSinkRequestSample 事件。 为了响应其中每个事件,客户端获取该流的下一个数据示例,并调用 IMFStreamSink::P rocessSample。
- 当每个流接收器接收足够的预注册数据时,它会发送 MEStreamSinkPrerolled 事件。
- 客户端调用 IMFPresentationClock::Start 来启动演示时钟。
- 演示文稿时钟通过调用 IMFClockStateSink::OnClockStart通知媒体接收器时钟正在启动。
- 若要获取更多数据,每个流接收器都会发送 MEStreamSinkRequestSample 事件。 为了响应其中每个事件,客户端将获取下一个示例并调用 ProcessSample。 此步骤将重复,直到演示文稿结束。
大多数媒体接收器异步处理样本,因此流接收器可以一次发送多个示例请求。
在流式处理期间,客户端可以随时调用 IMFStreamSink::P laceMarker 和 IMFStreamSink::Flush。 下一部分将介绍标记。 刷新会导致流接收器删除已排队但尚未呈现的任何样本。
标记
标记为客户端提供了一种方法来指示流中的特定点。 标记由以下信息组成:
- 标记类型,定义为 MFSTREAMSINK_MARKER_TYPE 枚举的成员。
- 与标记关联的数据。 数据的含义取决于标记类型。 某些标记类型没有数据。
- 客户端自己使用的可选数据。
若要放置标记,客户端调用 IMFStreamSink::P laceMarker。 流接收器完成处理在 PlaceMarker 调用之前接收的任何示例,然后发送 MEStreamSinkMarker 事件。
大多数媒体接收器保留挂起的样本队列,它们以异步方式进行处理。 标记事件必须通过示例处理进行序列化,因此媒体接收器应将标记放置在同一队列中。 例如,假设客户端进行以下方法调用:
- ProcessSample (示例 #1)
- ProcessSample (示例 #2)
- PlaceMarker (标记 #1)
- ProcessSample (示例 #3)
- 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时,立即发送标记事件。 |
此外,流接收器在完成状态转换后必须发送以下事件:
- OnClockStart,OnClockRestart:MEStreamSinkStarted 事件
- OnClockPause:MEStreamSinkPaused 事件
- OnClockStop:MEStreamSinkStopped 事件
敲定
某些媒体接收器需要在交付最后一个示例后执行额外的处理步骤。 通常,此要求适用于存档接收器,这些接收器必须将标头或索引写入文件中。 如果媒体接收器需要任何最终处理,它将公开 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 | 流接收器已停止。 (必需。 |
目前没有为媒体接收器定义常规用途事件。 某些媒体接收器可能会发送自定义事件。
相关主题