Media foundation transforms (MFTs)的异步数据处理

  • Post author:
  • Post category:其他


原文章

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方法修改如下:

  1. 每当MFT输出时,它发送一个METransformHaveOutput事件。
  2. 对于每个METransformHaveOutput事件,客户端调用ProcessOutput。

实施注意事项:

  • 如果客户端在任何其他时间调用ProcessOutput,该方法返回E_UNEXPECTED。
  • 异步MFT不应该从ProcessOutput方法返回MF_E_TRANSFORM_NEED_MORE_INPUT。

    如果MFT需要更多输入,它会发送一个METransformNeedInput事件。

排空 Draining

排空MFT导致MFT从已经发送的任何输入数据产生尽可能多的输出。 排空异步MFT的过程如下:

  1. client发送MFT_MESSAGE_COMMAND_DRAIN消息。
  2. MFT继续发送METransformHaveOutput事件,直到它没有更多的数据要处理。

    在此期间不会发送METransformNeedInput事件。
  3. 在MFT发送最后一个METransformHaveOutput事件之后,它发送一个METransformDrainComplete事件。
  4. 排空完成后,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响应如下:

  1. MFT从现有输入数据生成尽可能多的输出样本,为每个输出样本发送一个METransformHaveOutput事件。
  2. 在生成所有输出之后,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可能会破坏现有应用程序。


Media Foundation Transforms