异步 MRT
本主题介绍媒体基础转换 (MRT) 的异步数据处理。
注意
本主题适用于 Windows 7 或更高版本。
关于异步 MRT
在 Windows Vista 中引入 MCT 时,该 API 设计用于 同步 数据处理。 在该模型中,MFT 始终在等待获取输入或等待生成输出。
考虑一个典型的视频解码器。 为了获取解码的帧,客户端调用 IMFTransform::P rocessOutput。 如果解码器有足够的数据来解码帧,则当 MFT 解码帧时 ,ProcessOutput 会阻止。 否则, ProcessOutput 返回 MF_E_TRANSFORM_NEED_MORE_INPUT,指示客户端应调用 IMFTransform::P rocessInput。
如果解码器在一个线程上执行其所有解码操作,则此模型非常有效。 但假设解码器使用多个线程并行解码帧。 为了获得最佳性能,解码器应在解码线程空闲时接收新输入。 但是,线程完成解码操作的速率与客户端对 ProcessInput 和 ProcessOutput 的调用不完全一致,导致线程等待工作。
Windows 7 为 MCT 引入了事件驱动的 异步 处理。 在此模型中,每当 MFT 需要输入或具有输出时,它就会向客户端发送事件。
一般要求
本主题介绍异步 MFT 与同步 MFT 的区别。 除非本主题中另有说明,否则这两个处理模型是相同的。 特别是 (,格式协商是相同的。)
异步 MFT 必须实现以下接口:
事件
异步 MFT 使用以下事件来指示其数据处理状态:
事件 | 说明 |
---|---|
METransformNeedInput | 当 MFT 可以接受更多输入时发送。 |
METransformHaveOutput | 在 MFT 具有输出时发送。 |
METransformDrainComplete | 在排出操作完成时发送。 请参阅 清空。 |
METransformMarker | 处理标记时发送。 请参阅 标记。 |
这些事件在带外发送。 了解 MFT 上下文中带内事件和带外事件之间的差异非常重要。
原始 MFT 设计支持 带内 事件。 带内事件包含有关数据流的信息,例如有关格式更改的信息。 客户端通过调用 IMFTransform::P rocessEvent 将带内事件发送到 MFT。 MFT 可以在 ProcessOutput 方法中将带内事件发送回客户端。 (具体而言,事件在 MFT_OUTPUT_DATA_BUFFER structure 的 pEvents 成员中传达。)
MFT 通过 IMFMediaEventGenerator 接口发送带外事件,如下所示:
- MFT 实现 IMFMediaEventGenerator 接口,如 媒体事件生成器中所述。
- 客户端在 MFT 上为 IMFMediaEventGenerator 接口调用 IUnknown::QueryInterface。 异步 MFT 必须公开此接口。 同步 MRT 不应公开此接口。
- 客户端调用 IMFMediaEventGenerator::BeginGetEvent 和 IMFMediaEventGenerator::EndGetEvent 以接收来自 MFT 的带外事件。
ProcessInput
IMFTransform::P rocessInput 方法的修改如下:
- 当流式处理启动时,客户端发送 MFT_MESSAGE_NOTIFY_START_OF_STREAM 消息。
- 在流式处理期间,MFT 通过发送 METransformNeedInput 事件来请求数据。 事件数据是流标识符。
- 对于每个 METransformNeedInput 事件,客户端为指定的流调用 ProcessInput 。
- 在流式处理结束时,客户端可能会使用MFT_MESSAGE_NOTIFY_END_OF_STREAM消息调用 ProcessMessage。
实现说明:
- MFT 在收到MFT_MESSAGE_NOTIFY_START_OF_STREAM消息之前,不得发送任何 METransformNeedInput 事件。
- 在流式处理期间,MFT 可能随时发送 METransformNeedInput 事件。
- MFT 应维护挂起的 METransformNeedInput 事件的计数。 对不对应于 METransformNeedInput 事件的任何 ProcessInput 调用都必须返回 MF_E_NOTACCEPTING。
- 当 MFT 收到 MFT_MESSAGE_NOTIFY_END_OF_STREAM 消息时,它会将挂起的 METransformNeedInput 事件的计数重置为零。
- MFT 在收到MFT_MESSAGE_NOTIFY_END_OF_STREAM消息后不得发送任何 METransformNeedInput 事件。
- 如果在MFT_MESSAGE_NOTIFY_START_OF_STREAM之前或MFT_MESSAGE_NOTIFY_END_OF_STREAM之后调用 ProcessInput,则该方法必须返回MF_E_NOTACCEPTING。
ProcessOutput
IMFTransform::P rocessOutput 方法的修改如下:
- 每当 MFT 具有输出时,它就会发送 METransformHaveOutput 事件。
- 对于每个 METransformHaveOutput 事件,客户端调用 ProcessOutput。
实现说明:
- 如果客户端在任何其他时间调用 ProcessOutput ,该方法将返回 E_UNEXPECTED。
- 异步 MFT 绝不应从 ProcessOutput 方法返回MF_E_TRANSFORM_NEED_MORE_INPUT。 如果 MFT 需要更多输入,它将发送 METransformNeedInput 事件。
正在清空
清空 MFT 会导致 MFT 从已发送的任何输入数据中生成尽可能多的输出。 清空异步 MFT 的工作原理如下:
- 客户端发送 MFT_MESSAGE_COMMAND_DRAIN 消息。
- MFT 继续发送 METransformHaveOutput 事件,直到没有更多要处理的数据。 在此期间,它不会发送 METransformNeedInput 事件。
- MFT 发送最后一个 METransformHaveOutput 事件后,会发送 METransformDrainComplete 事件。
清空完成后,MFT 不会发送另一个 METransformNeedInput 事件,直到收到来自客户端 的MFT_MESSAGE_NOTIFY_START_OF_STREAM 消息。
冲洗
客户端可以通过发送 MFT_MESSAGE_COMMAND_FLUSH 消息来刷新 MFT。 MFT 会删除它保存的所有输入和输出样本。
MFT 在收到来自客户端的MFT_MESSAGE_NOTIFY_START_OF_STREAM消息之前,不会发送另一个 METransformNeedInput 事件。
标记
客户端可以通过发送 MFT_MESSAGE_COMMAND_MARKER 消息来标记流中的点。 MFT 的响应如下所示:
- MFT 会从现有输入数据生成尽可能多的输出样本,为每个输出样本发送 METransformHaveOutput 事件。
- 生成所有输出后,MFT 将发送 METransformMarker 事件。 必须在所有 METransformHaveOutput 事件之后发送此事件。
例如,假设解码器有足够的输入数据来生成四个输出样本。 如果客户端发送 MFT_MESSAGE_COMMAND_MARKER 消息,MFT 会将四个 METransformHaveOutput 事件排入队列, (每个输出示例) 一个,后跟 一个 METransformMarker 事件。
标记消息类似于排出消息。 但是,排水被视为流中的中断,而标记则不被视为中断。 排水和标记具有以下差异。
排水:
- 排出时,MFT 不会发送 METransformNeedInput 事件。
- MFT 会丢弃任何不能用于创建输出示例的输入数据。
- 某些 MMFT 在数据末尾生成“尾部”。 例如,音频效果(如混响或回声)在输入数据停止后会生成额外的数据。 生成尾部的 MFT 应在排水操作结束时执行此操作。
- MFT 完成清空后,它会使用 MFSampleExtension_Discontinuity 属性标记下一个输出示例,以指示流中的不连续。
标记:
- MFT 在发送标记事件之前继续发送 METransformNeedInput 事件。
- MFT 不会放弃任何输入数据。 如果有部分数据,则应在标记点之后进行处理。
- MFT 不会在标记点生成尾部。
- MFT 不会在标记点之后设置不连续标志。
格式更改
异步 MFT 必须支持动态格式更改,如 处理流更改中所述。
特性
异步 MFT 必须实现 IMFTransform::GetAttributes 方法才能返回有效的属性存储。 以下属性适用于异步 MCT:
Attribute | 说明 |
---|---|
MF_TRANSFORM_ASYNC | MFT 必须将此属性设置为 TRUE (1) 。 客户端可以查询此属性,以发现 MFT 是否为异步。 |
MF_TRANSFORM_ASYNC_UNLOCK | |
MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE | MFT 必须将此属性设置为 TRUE (1) 。 客户端可以假定已设置此属性。 |
解锁异步 MCT
异步 MFT 与原始 MFT 数据处理模型不兼容。 为了防止异步 MCT 破坏现有应用程序,定义了以下机制:
- 客户端在 MFT 上调用 IMFTransform::GetAttributes 。
客户端查询此 MF_TRANSFORM_ASYNC 属性的 。 对于异步 MFT,此属性的值为 “TRUE”。
若要解锁 MFT,客户端必须将 MF_TRANSFORM_ASYNC_UNLOCK 属性设置为 “TRUE”。
在客户端解锁 MFT 之前,所有 IMFTransform 方法都应返回 MF_E_TRANSFORM_ASYNC_LOCKED,但以下情况除外:
- IMFTransform::GetAttributes (所有异步 MRT)
- IMFTransform::GetInputAvailableType (所有异步 MRT)
- IMFTransform::GetOutputCurrentType (编码器仅)
- IMFTransform::SetOutputType (编码器仅)
- IMFTransform::GetStreamCount (所有异步 MRT)
- IMFTransform::GetStreamIDs (所有异步 MRT)
以下代码演示如何解锁异步 MFT:
HRESULT UnlockAsyncMFT(IMFTransform *pMFT)
{
IMFAttributes *pAttributes = NULL;
HRESULT hr = hr = pMFT->GetAttributes(&pAttributes);
if (SUCCEEDED(hr))
{
hr = pAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
pAttributes->Release();
}
return hr;
}
关闭 MFT
异步 MCT 必须实现 IMFShutdown 接口。
- 关闭:MFT 必须关闭其事件队列。 如果使用标准事件队列,请调用 IMFMediaEventQueue::Shutdown。 (可选)MFT 可能会释放其他资源。 客户端在调用 Shutdown 后不得使用 MFT。
- GetShutdownStatus:调用 Shutdown 后,MFT 应返回 pStatus 参数中的值MFSHUTDOWN_COMPLETED。 它不应返回 MFSHUTDOWN_INITIATED的值。
注册和枚举
若要注册异步 MFT,请调用 MFTRegister 函数并在 Flags 参数中设置MFT_ENUM_FLAG_ASYNCMFT标志。 (以前保留此标志。)
若要枚举异步 MFT,请调用 MFTEnumEx 函数并在 Flags 参数中设置 MFT_ENUM_FLAG_ASYNCMFT 标志。 为了向后兼容, MFTEnum 函数不枚举异步 MMT。 否则,在用户计算机上安装异步 MFT 可能会中断现有应用程序。
相关主题