原文章 https://msdn.microsoft.com/en-us/library/windows/desktop/dd317909(v=vs.85).aspx
当在Windows Vista中引入MFT时,其API被设计用于同步数据处理。在这种模型中,MFT总是等待获得输入,或者等待产生输出。
考虑到一个典型的视频解码器。要获得解码的帧,client调用IMFTransform :: ProcessOutput。如果解码器具有足够的数据来解码帧,则在MFT对帧进行解码的同时,处理输出块。否则,ProcessOutput返回MF_E_TRANSFORM_NEED_MORE_INPUT,指示client应调用IMFTransform :: ProcessInput。
如果解码器在一个线程上执行所有的解码操作,这个模型工作良好。但是假设解码器使用多个线程来并行解码帧。为了获得最佳性能,解码器应当在解码线程变得空闲时接收新输入。但线程完成解码操作的速率不会与客户端对ProcessInput和ProcessOutput的调用完全一致,导致线程等待工作。
Windows 7为MFT引入了事件驱动的异步处理。在此模型中,每当MFT需要输入或具有输出时,它向client发送事件。
本主题描述异步MFT如何不同于同步MFT。除非本主题中注明,否则两个处理模型是相同的。 (特别是,格式协商是相同的。)
异步MFT必须实现以下接口:
IMFTransform
IMFMediaEventGenerator
IMFShutdown
异步MFT使用以下事件来表示其数据处理状态:
Event | Description |
---|---|
METransformNeedInput | 当MFT可以接受更多输入时发送。 |
METransformHaveOutput | 当MFT输出时发送。 |
METransformDrainComplete | 当排空操作完成时发送。 参见排空。 |
METransformMarker | 标记处理时发送。 请参阅标记。 |
这些事件是在频带外的。重要的是理解在MFT的上下文中带内和带外事件之间的差异。
原始的MFT设计支持带内事件。 带内事件包含有关数据流的信息,例如有关格式更改的信息。 client通过调用IMFTransform :: ProcessEvent将带内事件发送到MFT。 MFT可以在ProcessOutput方法中将带内事件发送回client。 (具体来说,事件在MFT_OUTPUT_DATA_BUFFER结构的pEvents成员中传递。
IMFTransform :: ProcessInput方法修改如下:
- 流开始时,client发送MFT_MESSAGE_NOTIFY_START_OF_STREAM消息。
- 在流式传输期间,MFT通过发送一个METransformNeedInput事件来请求数据。 事件数据是流标识符。
- 对于每个METransformNeedInput事件,client为指定的流调用ProcessInput。
- 在流式处理结束时,client可以使用MFT_MESSAGE_NOTIFY_END_OF_STREAM消息调用ProcessMessage。
实施注意事项:
- MFT不能发送任何METransformNeedInput事件,直到它收到MFT_MESSAGE_NOTIFY_START_OF_STREAM消息。
- 在流传输期间,MFT可以随时发送METransformNeedInput事件。MFT应该维护等待中的METransformNeedInput事件的计数。
任何对不与METransformNeedInput事件对应的ProcessInput的调用必须返回MF_E_NOTACCEPTING。
- 当MFT接收到MFT_MESSAGE_NOTIFY_END_OF_STREAM消息时,它将将待处理的METransformNeedInput事件的计数重置为零。在接收到MFT_MESSAGE_NOTIFY_END_OF_STREAM消息后,MFT不得发送任何METransformNeedInput事件。
- 如果在MFT_MESSAGE_NOTIFY_START_OF_STREAM之前或在MFT_MESSAGE_NOTIFY_END_OF_STREAM之后调用ProcessInput,则该方法必须返回MF_E_NOTACCEPTING。
IMFTransform :: ProcessOutput方法修改如下:
实施注意事项:
排空MFT导致MFT从已经发送的任何输入数据产生尽可能多的输出。 排空异步MFT的过程如下:
client可以通过发送MFT_MESSAGE_COMMAND_FLUSH消息来刷新MFT。 MFT丢弃其保存的所有输入和输出样本。
MFT不会发送另一个METransformNeedInput事件,直到它从客户端接收到MFT_MESSAGE_NOTIFY_START_OF_STREAM消息。
client可以通过发送MFT_MESSAGE_COMMAND_MARKER消息在流中标记一个点。 MFT响应如下:
例如,假设解码器具有足够的输入数据以产生四个输出样本。如果客户端发送MFT_MESSAGE_COMMAND_MARKER消息,MFT将排队4个METransformHaveOutput事件(每个输出样本一个),随后是一个METransformMarker事件。
标记消息类似于排空消息。然而,排空被认为是流中的中断,而标记不是。排出和标记有以下差异。
排空:
标记:
异步MFT必须支持动态格式更改,如Handling Stream Changes中所述。
异步MFT必须实现IMFTransform :: GetAttributes方法以返回有效的属性。 以下属性适用于异步MFT:
Attribute | Description |
---|---|
MF_TRANSFORM_ASYNC | MFT必须将此属性设置为TRUE(1)。 client可以查询此属性以发现MFT是否是异步的。 |
MF_TRANSFORM_ASYNC_UNLOCK | |
MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE | MFT必须将此属性设置为TRUE(1)。 client可以假定该属性已设置。 |
异步MFT与原始MFT数据处理模型不兼容。 为了防止异步MFT破坏现有应用程序,定义了以下机制:
在客户端解锁MFT之前,所有IMFTransform方法应返回MF_E_TRANSFORM_ASYNC_LOCKED,但以下情况除外:
以下代码显示如何解锁异步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必须实现IMFShutdown接口。
要注册异步MFT,请调用MFTRegister函数,并在Flags参数中设置MFT_ENUM_FLAG_ASYNCMFT标志。 (以前此标志已保留。)
要枚举异步MFT,请调用MFTEnumEx函数并在Flags参数中设置MFT_ENUM_FLAG_ASYNCMFT标志。 为了向后兼容,MFTEnum函数不枚举异步MFT。 否则,在用户计算机上安装异步MFT可能会破坏现有应用程序。
Media Foundation Transforms