异步 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

如果解码器在一个线程上执行其所有解码作,则此模型可以正常工作。 但假设解码器使用多个线程并行解码帧。 为了获得最佳性能,每当解码线程处于空闲状态时,解码器都应接收新输入。 但是线程完成解码作的速率与客户端调用 ProcessInputProcessOutput不完全一致,从而导致线程等待工作。

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 接口发送带外事件,如下所示:

  1. MFT 实现 IMFMediaEventGenerator 接口,如 媒体事件生成器中所述。
  2. 客户端在 MFT 上为 IMFMediaEventGenerator 接口调用 IUnknown::QueryInterface。 异步 MFT 必须公开此接口。 同步 MFT 不应公开此接口。
  3. 客户端调用 IMFMediaEventGenerator::BeginGetEventIMFMediaEventGenerator::EndGetEvent 从 MFT 接收带外事件。

ProcessInput

IMFTransform::P rocessInput 方法修改如下:

  1. 流式处理启动时,客户端将发送 MFT_MESSAGE_NOTIFY_START_OF_STREAM 消息。
  2. 在流式处理期间,MFT 通过发送 METransformNeedInput 事件来请求数据。 事件数据是流标识符。
  3. 对于每个 METransformNeedInput 事件,客户端针对指定的流调用 ProcessInput
  4. 在流式处理结束时,客户端可以使用 MFT_MESSAGE_NOTIFY_END_OF_STREAM 消息调用 ProcessMessage

实现说明:

ProcessOutput

IMFTransform::P rocessOutput 方法修改如下:

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

实现说明:

  • 如果客户端在任何其他时间调用 ProcessOutput,该方法将返回 E_UNEXPECTED
  • 异步 MFT 绝不应从 ProcessOutput 方法返回 MF_E_TRANSFORM_NEED_MORE_INPUT。 如果 MFT 需要更多输入,它将发送 METransformNeedInput 事件。

排水

清空 MFT 会导致 MFT 从已发送的任何输入数据生成尽可能多的输出。 清空异步 MFT 的工作原理如下:

  1. 客户端发送 MFT_MESSAGE_COMMAND_DRAIN 消息。
  2. MFT 继续发送 METransformHaveOutput 事件,直到它没有更多要处理的数据。 在此期间,它不会发送 METransformNeedInput 事件。
  3. 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 响应如下:

  1. MFT 从现有输入数据生成尽可能多的输出示例,为每个输出示例发送 METransformHaveOutput 事件。
  2. 生成所有输出后,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,但有以下例外:

以下代码演示如何解锁异步 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 可能会中断现有应用程序。

媒体基础转换