原文章
https://msdn.microsoft.com/en-us/library/windows/desktop/dd317909(v=vs.85).aspx
关于异步MFT About Asynchronous MFTs
当在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发送事件。
一般要求 General Requirements
本主题描述异步MFT如何不同于同步MFT。除非本主题中注明,否则两个处理模型是相同的。 (特别是,格式协商是相同的。)
异步MFT必须实现以下接口:
IMFTransform
IMFMediaEventGenerator
IMFShutdown
事件 Event
异步MFT使用以下事件来表示其数据处理状态:
Event | Description |
---|---|
METransformNeedInput |
当MFT可以接受更多输入时发送。 |
METransformHaveOutput |
当MFT输出时发送。 |
METransformDrainComplete |
当排空操作完成时发送。 参见排空。 |
METransformMarker |
标记处理时发送。 请参阅标记。 |
这些事件是在频带外的。重要的是理解在MFT的上下文中带内和带外事件之间的差异。
原始的MFT设计支持带内事件。 带内事件包含有关数据流的信息,例如有关格式更改的信息。 client通过调用IMFTransform :: ProcessEvent将带内事件发送到MFT。 MFT可以在ProcessOutput方法中将带内事件发送回client。 (具体来说,事件在MFT_OUTPUT_DATA_BUFFER结构的pEvents成员中传递。
-
MFT实现IMFMediaEventGenerator接口,如
媒体事件生成器
中所述。 -
client在MFT上为IMFMediaEventGenerator接口调用IUnknown :: QueryInterface。
异步MFT必须暴露此接口。 同步MFT不应暴露此接口。 -
client调用IMFMediaEventGenerator ::
BeginGetEvent和IMFMediaEventGenerator :: EndGetEvent从MFT接收带外事件。
输入处理 ProcessInput
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。
输出处理 ProcessOutput
IMFTransform :: ProcessOutput方法修改如下:
- 每当MFT输出时,它发送一个METransformHaveOutput事件。
- 对于每个METransformHaveOutput事件,客户端调用ProcessOutput。
实施注意事项:
- 如果客户端在任何其他时间调用ProcessOutput,该方法返回E_UNEXPECTED。
-
异步MFT不应该从ProcessOutput方法返回MF_E_TRANSFORM_NEED_MORE_INPUT。
如果MFT需要更多输入,它会发送一个METransformNeedInput事件。
排空 Draining
排空MFT导致MFT从已经发送的任何输入数据产生尽可能多的输出。 排空异步MFT的过程如下:
- client发送MFT_MESSAGE_COMMAND_DRAIN消息。
-
MFT继续发送METransformHaveOutput事件,直到它没有更多的数据要处理。
在此期间不会发送METransformNeedInput事件。 - 在MFT发送最后一个METransformHaveOutput事件之后,它发送一个METransformDrainComplete事件。
- 排空完成后,MFT不会发送另一个METransformNeedInput事件,直到它从客户端接收到MFT_MESSAGE_NOTIFY_START_OF_STREAM消息。
刷新 Flushing
client可以通过发送MFT_MESSAGE_COMMAND_FLUSH消息来刷新MFT。 MFT丢弃其保存的所有输入和输出样本。
MFT不会发送另一个METransformNeedInput事件,直到它从客户端接收到MFT_MESSAGE_NOTIFY_START_OF_STREAM消息。
标记 Markers
client可以通过发送MFT_MESSAGE_COMMAND_MARKER消息在流中标记一个点。 MFT响应如下:
- MFT从现有输入数据生成尽可能多的输出样本,为每个输出样本发送一个METransformHaveOutput事件。
- 在生成所有输出之后,MFT发送METransformMarker事件。此事件必须在所有METransformHaveOutput事件之后发送。
例如,假设解码器具有足够的输入数据以产生四个输出样本。如果客户端发送MFT_MESSAGE_COMMAND_MARKER消息,MFT将排队4个METransformHaveOutput事件(每个输出样本一个),随后是一个METransformMarker事件。
标记消息类似于排空消息。然而,排空被认为是流中的中断,而标记不是。排出和标记有以下差异。
排空:
- 排空时,MFT不发送METransformNeedInput事件。
- MFT丢弃不能用于创建输出样本的任何输入数据。
- 一些MFT在数据的结尾产生“尾部”。例如,混响或回声等音频效果会在输入数据停止后产生额外的数据。产生尾部的MFT在排空操作结束时应该这样做。
- 在MFT完成排空之后,它用MFSampleExtension_Discontinuity属性标记下一个输出样本,以指示流中的不连续性。
标记:
- MFT在发送标记事件之前继续发送METransformNeedInput事件。
- MFT不丢弃任何输入数据。如果有部分数据,应在标记点后处理。
- MFT不在标记点处产生尾部。
- MFT不在标记点之后设置不连续标记。
格式更改 Format Changes
异步MFT必须支持动态格式更改,如
Handling Stream Changes
中所述。
属性 Attributes
异步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 Unlocking Asynchronous MFTs
异步MFT与原始MFT数据处理模型不兼容。 为了防止异步MFT破坏现有应用程序,定义了以下机制:
- client在MFT上调用IMFTransform :: GetAttributes。
- client查询此MF_TRANSFORM_ASYNC属性。 对于异步MFT,此属性的值为TRUE。
- 要解锁MFT,client必须将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 Shutting Down the MFT
异步MFT必须实现IMFShutdown接口。
-
Shutdown
:MFT必须关闭其事件队列。 如果使用标准事件队列,请调用IMFMediaEventQueue :: Shutdown。
可选地,MFT可以释放其他资源。 客户端在调用Shutdown后不得使用MFT。 -
GetShutdownStatus
:在调用关闭之后,MFT应该返回pStatus参数中的值MFSHUTDOWN_COMPLETED。
它不应返回值MFSHUTDOWN_INITIATED。
注册和枚举 Registration and Enumeration
要注册异步MFT,请调用MFTRegister函数,并在Flags参数中设置MFT_ENUM_FLAG_ASYNCMFT标志。 (以前此标志已保留。)
要枚举异步MFT,请调用MFTEnumEx函数并在Flags参数中设置MFT_ENUM_FLAG_ASYNCMFT标志。 为了向后兼容,MFTEnum函数不枚举异步MFT。 否则,在用户计算机上安装异步MFT可能会破坏现有应用程序。
相关内容 Related topics