异步 MFT
本主题介绍媒体基础转换的异步数据处理(MFT)。
注意
本主题适用于 Windows 7 或更高版本。
关于异步 MFT
在 Windows Vista 中引入 MFT 时,API 旨在 同步 数据处理。 在该模型中,MFT 始终等待获取输入或等待生成输出。
请考虑典型的视频解码器。 若要获取解码的帧,客户端调用 IMFTransform::P rocessOutput。 如果解码器有足够的数据来解码帧,则在 MFT 解码帧时,ProcessOutput 块。 否则,ProcessOutput 返回 MF_E_TRANSFORM_NEED_MORE_INPUT,指示客户端应调用 IMFTransform::P rocessInput。
如果解码器在一个线程上执行其所有解码作,则此模型可以正常工作。 但假设解码器使用多个线程并行解码帧。 为了获得最佳性能,每当解码线程处于空闲状态时,解码器都应接收新输入。 但是线程完成解码作的速率与客户端调用 ProcessInput 和 ProcessOutput不完全一致,从而导致线程等待工作。
Windows 7 引入了事件驱动、异步 处理 MFT。 在此模型中,每当 MFT 需要输入或具有输出时,它将事件发送到客户端。
常规要求
本主题介绍异步 MFT 与同步 MFT 有何不同。 除了本主题中所述的情况外,这两个处理模型是相同的。 (特别是格式协商是相同的。
异步 MFT 必须实现以下接口:
事件
异步 MFT 使用以下事件来指示其数据处理状态:
事件 | 描述 |
---|---|
METransformNeedInput | 当 MFT 可以接受更多输入时发送。 |
METransformHaveOutput | 当 MFT 具有输出时发送。 |
METransformDrainComplete | 当排水作完成时发送。 请参阅 清空。 |
METransformMarker | 在处理标记时发送。 请参阅 标记。 |
这些事件在带外发送。 请务必了解 MFT 上下文中的带内事件与带外事件之间的差异。
原始 MFT 设计支持 带内 事件。 带内事件包含有关数据流的信息,例如有关格式更改的信息。 客户端通过调用 IMFTransform::P rocessEvent将带内事件发送到 MFT。 MFT 可以在 ProcessOutput 方法中将带内事件发送回客户端。 (具体而言,事件在 pEventsMFT_OUTPUT_DATA_BUFFER 结构的成员中传达。
MFT 通过 IMFMediaEventGenerator 接口发送带外事件,如下所示:
- MFT 实现 IMFMediaEventGenerator 接口,如 媒体事件生成器中所述。
- 客户端在 MFT 上为 IMFMediaEventGenerator 接口调用 IUnknown::QueryInterface。 异步 MFT 必须公开此接口。 同步 MFT 不应公开此接口。
- 客户端调用 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 放弃任何无法用于创建输出示例的输入数据。
- 某些 MFT 在数据末尾生成“tail”。 例如,音频效果(如混响或回声)会在输入数据停止后产生额外的数据。 生成尾部的 MFT 应在排水作结束时执行此作。
- MFT 完成清空后,它会使用 MFSampleExtension_Discontinuity 属性标记下一个输出示例,以指示流中的不连续性。
标记:
- MFT 在发送标记事件之前继续发送 METransformNeedInput 事件。
- MFT 不会放弃任何输入数据。 如果存在部分数据,则应在标记点之后对其进行处理。
- MFT 不会在标记点生成尾部。
- MFT 不会在标记点之后设置不连续标志。
格式更改
异步 MFT 必须支持动态格式更改,如 处理流更改中所述。
属性
异步 MFT 必须实现 IMFTransform::GetAttributes 方法才能返回有效的属性存储。 以下属性适用于异步 MFT:
属性 | 描述 |
---|---|
MF_TRANSFORM_ASYNC | MFT 必须将此属性设置为 TRUE (1)。 客户端可以查询此属性,以发现 MFT 是否是异步的。 |
MF_TRANSFORM_ASYNC_UNLOCK | |
MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE | MFT 必须将此属性设置为 TRUE (1)。 客户端可以假定已设置此属性。 |
解锁异步 MFT
异步 MFT 与原始 MFT 数据处理模型不兼容。 为了防止异步 MFT 中断现有应用程序,定义了以下机制:
- 客户端在 MFT 上调用 IMFTransform::GetAttributes。
客户端查询此 MF_TRANSFORM_ASYNC 属性。 对于异步 MFT,此属性的值为“TRUE”。
若要解锁 MFT,客户端必须将 MF_TRANSFORM_ASYNC_UNLOCK 属性设置为“TRUE”。
在客户端解锁 MFT 之前,所有 IMFTransform 方法都应返回 MF_E_TRANSFORM_ASYNC_LOCKED,但有以下例外:
- IMFTransform::GetAttributes(所有异步 MFT)
- IMFTransform::GetInputAvailableType(所有异步 MFT)
- IMFTransform::GetOutputCurrentType(仅编码器)
- IMFTransform::SetOutputType(仅编码器)
- IMFTransform::GetStreamCount(所有异步 MFT)
- IMFTransform::GetStreamIDs(所有异步 MFT)
以下代码演示如何解锁异步 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
异步 MFT 必须实现 IMFShutdown 接口。
- 关闭:MFT 必须关闭其事件队列。 如果使用标准事件队列,请调用 IMFMediaEventQueue::Shutdown。 (可选)MFT 可能会释放其他资源。 在调用 关闭后,客户端不得使用 MFT。
- GetShutdownStatus:调用 关闭 后,MFT 应返回 pStatus 参数中的值 MFSHUTDOWN_COMPLETED。 它不应返回值 MFSHUTDOWN_INITIATED。
注册和枚举
若要注册异步 MFT,请调用 MFTRegister 函数,并在 Flags 参数中设置 MFT_ENUM_FLAG_ASYNCMFT 标志。 (以前这个标志是保留的。
若要枚举异步 MFT,请调用 MFTEnumEx 函数,并在 Flags 参数中设置 MFT_ENUM_FLAG_ASYNCMFT 标志。 为了向后兼容,MFTEnum 函数不枚举异步 MFT。 否则,在用户的计算机上安装异步 MFT 可能会中断现有应用程序。
相关主题