媒体接收器
媒体接收器 是接收媒体数据的管道对象。 媒体接收器是一个或多个媒体流的目标。 媒体接收器分为两个常规类别:
呈现器是显示要播放的数据的媒体接收器。 增强的视频呈现器 (EVR) 显示视频帧,音频呈现器通过声音卡或其他音频设备播放音频流。
存档接收器是将数据写入文件或其他存储的媒体接收器。
它们之间的main区别在于,存档接收器不会以固定的播放速率使用数据。 相反,它会尽快写入接收的数据。
媒体接收器公开 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 以异步执行最终处理。 此方法遵循异步 回调方法中所述的标准 Media Foundation 异步模型。 媒体接收器可以假定客户端将调用 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 | 流接收器已停止。 (必选。) |
目前没有为媒体接收器定义常规用途事件。 某些媒体接收器可能会发送自定义事件。
相关主题