基本 MFT 处理模型

本主题介绍客户端如何使用 Media Foundation 转换 (MFT) 来处理数据。 客户端是直接调用 MFT 上方法的任何内容。 这可能是应用程序或 Media Foundation 管道。

如果你是以下主题,请阅读本主题:

  • 编写对一个或多个 MFT 进行直接调用的应用程序。
  • 编写自定义 MFT 并想要了解 MFT 的预期行为。

本主题介绍 同步 处理模型。 在此模型中,所有数据处理方法都会阻止,直到它们完成。 MFT 还可以支持 异步 模型,本主题中介绍了 异步 MFT

基本处理模型

创建 MFT

可通过多种方式创建 MFT:

  • 调用 MFTEnum 函数。
  • 调用 MFTEnumEx 函数。
  • 如果已知道 MFT 的 CLSID,只需调用 CoCreateInstance 即可。

某些 MFT 可能提供其他选项,例如专用创建函数。

获取流标识符

MFT 具有一个或多个 。 输入流接收输入数据,输出流生成输出数据。 流不表示为不同的对象。 相反,各种 MFT 方法采用流标识符作为参数。

某些 MFT 允许客户端添加或删除输入流。 在流式处理期间,MFT 可以添加或删除输出流。 (客户端无法添加或删除输出流。)

  1. (Optional.) 调用 IMFTransform::GetStreamLimits 以获取 MFT 可以支持的最小和最大流数。 如果最小值和最大值相同,则 MFT 具有固定数量的流。
  2. 调用 IMFTransform::GetStreamCount 以获取初始流数。
  3. 调用 IMFTransform::GetStreamID 以获取流标识符。 如果此方法返回E_NOTIMPL,则表示 MFT 具有固定数量的流,并且流标识符从零开始连续。
  4. (Optional.) 如果 MFT 没有固定数量的流,请调用 IMFTransform::AddInputStreams 以添加更多输入流,或 IMFTransform::D eleteInputStream 删除输入流。 (不能添加或删除输出流。)

设置媒体类型

在 MFT 处理数据之前,客户端必须为 MFT 的每个流设置媒体类型。 MFT 可能需要客户端在设置输出类型之前设置输入类型,或者可能需要先) 输出类型 (相反的顺序。 某些 MFT 对订单没有要求。

MFT 可以为流提供首选媒体类型列表。 此外,MFT 还可以通过将此信息添加到注册表来指示它们支持的常规格式。

若要设置媒体类型,请执行以下操作:

  1. (Optional.) 对于每个输入流,请调用 IMFTransform::GetInputAvailableType 以获取该流的首选类型列表。
    • 如果此方法返回MF_E_TRANSFORM_TYPE_NOT_SET,则必须首先设置输出类型;跳到步骤 3。
    • 如果该方法返回E_NOTIMPL,则 MFT 没有首选输入类型的列表;跳到步骤 2。
  2. 对于每个输入流,请调用 IMFTransform::SetInputType 来设置输入类型。 可以使用步骤 1 中的媒体类型,也可以使用描述输入数据的类型。 如果任何流返回MF_E_TRANSFORM_TYPE_NOT_SET,请跳到步骤 3。
  3. (Optional.) 对于每个输出流,请调用 IMFTransform::GetOutputAvailableType 以获取该流的首选类型列表。
    • 如果此方法返回MF_E_TRANSFORM_TYPE_NOT_SET,则必须首先设置输入类型;返回到步骤 1。
    • 如果任何流返回E_NOTIMPL,则 MFT 没有首选输出类型的列表;跳到步骤 4。
  4. 对于每个输出流,调用 IMFTransform::SetOutputType 来设置输出类型。 可以使用步骤 3 中的媒体类型,也可以使用描述所需输出格式的类型。
  5. 如果任何输入流没有媒体类型,请返回到步骤 1。

获取缓冲区要求

客户端设置媒体类型后,应获取每个流的缓冲区要求:

处理数据

MFT 设计为可靠的状态机。 它不会向客户端发出任何调用。

  1. 使用MFT_MESSAGE_NOTIFY_BEGIN_STREAMING消息调用 IMFTransform::P rocessMessage。 此消息请求 MFT 在流式处理期间分配它所需的任何资源。
  2. 在至少一个输入流上调用 IMFTransform::P rocessInput ,将输入样本传递到 MFT。
  3. (Optional.) 调用 IMFTransform::GetOutputStatus 以查询 MFT 是否可以生成输出示例。 如果该方法返回S_OK,请检查 pdwFlags 参数。 如果 pdwFlags 包含 MFT_OUTPUT_STATUS_SAMPLE_READY 标志,请转到步骤 4。 如果 pdwFlags 为零,请返回到步骤 2。 如果该方法返回E_NOTIMPL,请转到步骤 4。
  4. 调用 IMFTransform::P rocessOutput 获取输出数据。
    • 如果该方法返回 MF_E_TRANSFORM_NEED_MORE_INPUT,则表示 MFT 需要更多输入数据;返回到步骤 2。
    • 如果该方法返回 MF_E_TRANSFORM_STREAM_CHANGE,则表示输出流数已更改,或输出格式已更改。 客户端可能需要查询新的流标识符或设置新的媒体类型。 有关详细信息,请参阅 ProcessOutput 的文档。
  5. 如果仍有要处理的输入数据,请转到步骤 2。 如果 MFT 已使用所有可用的输入数据,请转到步骤 6。
  6. 使用MFT_MESSAGE_NOTIFY_END_OF_STREAM消息调用 ProcessMessage
  7. 使用MFT_MESSAGE_COMMAND_DRAIN消息调用 ProcessMessage
  8. 调用 ProcessOutput 以获取剩余输出。 重复此步骤,直到方法返回 MF_E_TRANSFORM_NEED_MORE_INPUT。 此返回值指示所有输出都已从 MFT 中排出。 (不要将此视为错误条件。)

此处所述的序列在 MFT 中保留尽可能少的数据。 每次调用 ProcessInput 后,客户端都会尝试获取输出。 可能需要多个输入样本才能生成一个输出示例,或者单个输入示例可能会生成多个输出示例。 客户端的最佳行为是从 MFT 拉取输出样本,直到 MFT 需要更多输入。

但是,MFT 应能够处理客户端的不同方法调用顺序。 例如,客户端可能只是在对 ProcessInputProcessOutput 的调用之间交替。 MFT 应通过从 ProcessInput 返回MF_E_NOTACCEPTING来限制其获取的输入量,只要它有一些输出才能生成。

此处介绍的方法调用顺序不是唯一有效的事件序列。 例如,步骤 3 和 4 假定客户端以输入类型开头,然后尝试输出类型。 客户端还可以反转此顺序,并从输出类型开始。 在任一情况下,如果 MFT 需要相反的顺序,则应返回错误代码MF_E_TRANSFORM_TYPE_NOT_SET。

客户端可以在流式处理期间随时调用信息方法,例如 GetInputCurrentTypeGetOutputStreamInfo。 客户端还可以随时尝试更改媒体类型。 如果这不是有效的操作,MFT 应返回错误代码。 简言之,MFT 应该很少考虑操作顺序,而不是调用本身记录的内容。

下图显示了本主题中所述过程的流程图。

flow chart that leads from get stream identifiers through loops that set input types, get input, and process output

基本模型的扩展

(可选)MFT 可以支持基本流式处理模型的一些扩展。

  • 延迟读取流。 如果 IMFTransform::GetOutputStreamInfo 方法返回输出流的 MFT_OUTPUT_STREAM_LAZY_READ 标志,则客户端不必从该输出流收集数据。 MFT 继续接受输入,有时 MFT 将放弃该流的输出数据。 如果所有输出流都有此标志,则 MFT 永远不会接受输入。 一个示例可能是可视化转换,其中客户端仅在具有备用 CPU 周期来绘制可视化效果时才获取输出。
  • 可丢弃的流。 如果 GetOutputStreamInfo 方法返回输出流的 MFT_OUTPUT_STREAM_DISCARDABLE 标志,客户端可以请求 MFT 放弃输出,但除非请求,否则 MFT 不会放弃任何输出。 当 MFT 达到其最大输入缓冲区时,客户端必须收集一些输出数据或请求 MFT 放弃输出。
  • 可选流。 如果 GetOutputStreamInfo 方法返回输出流的 MFT_OUTPUT_STREAM_OPTIONAL 标志,或者 IMFTransform::GetInputStreamInfo 方法返回输入流的 MFT_INPUT_STREAM_OPTIONAL 标志,则该流是可选的。 客户端不必在流上设置媒体类型。 如果客户端未设置类型,则会取消选择流。 已取消选择的输出流不生成示例,客户端在调用 ProcessOutput 时不会为流提供缓冲区。 已取消选择的输入流不接受输入数据。 MFT 可以将其所有输入和输出流标记为可选。 但是,预计必须至少选择一个输入和一个输出才能使 MFT 正常工作。
  • 异步处理。 异步处理模型是在 Windows 7 中引入的。 本主题介绍了 异步 MFT

IMF2DBuffer

如果 MFT 处理未压缩的视频数据,则应使用 IMF2DBuffer 接口操作示例缓冲区。 若要获取此接口,请查询任何输入或输出缓冲区上的 IMFMediaBuffer 接口。 当该接口可用时未使用此接口可能会导致其他缓冲区副本。 若要正确使用此接口,当 IMF2DBuffer 可用时,转换不应使用 IMFMediaBuffer 接口锁定缓冲区。

有关处理视频数据的详细信息,请参阅 未压缩的视频缓冲区

刷新 MFT

刷新 MFT 会导致 MFT 放弃其所有输入数据。 这可能会导致输出流中断。 当客户端不关心丢失数据时,客户端通常会先刷新 MFT,然后再在输入流中寻求新点或切换到新的输入流。

若要刷新 MFT,请使用MFT_MESSAGE_COMMAND_FLUSH消息调用 IMFTransform::P rocessMessage

清空 MFT

清空 MFT 会导致 MFT 从已发送的任何输入数据生成尽可能多的输出。 如果 MFT 无法从可用输入生成完整的输出示例,它将删除输入数据。 客户端通常会在到达源流的末尾或紧邻源流中的格式更改之前清空 MFT。 若要清空 MFT,请执行以下操作:

  1. 使用MFT_MESSAGE_COMMAND_DRAIN消息调用 ProcessMessage。 此消息通知 MFT,它应从已发送的输入数据中传递尽可能多的输出数据。
  2. 调用 ProcessOutput 以获取输出数据,直到方法返回 MF_E_TRANSFORM_NEED_MORE_INPUT

虽然 MFT 正在耗尽,但它不会接受任何更多的输入。

示例属性

输入样本可能具有必须复制到相应输出样本的属性。

对于具有一个输入和输出的 MFT,可以使用以下常规规则:

  • 如果每个输入示例只生成一个输出示例,则可以让客户端复制属性。 使 MFPKEY_EXATTRIBUTE_SUPPORTED 属性保持未设置。
  • 如果输入样本和输出样本之间没有一对一对应关系,MFT 必须确定输出样本的正确属性。 将 MFPKEY_EXATTRIBUTE_SUPPORTED 属性设置为 VARIANT_TRUE

中断

中断是音频或视频流的中断。 中断可能是由于网络连接上的数据包丢弃、文件数据损坏、从一个源流切换到另一个源流或各种其他原因造成的。 不连续性是通过在中断后在第一个样本上设置 MFSampleExtension_Discontinuity 属性来发出信号。 不能在样本中间发出不连续信号。 因此,任何不连续的数据都应以单独的样本发送。

某些转换(尤其是处理未压缩的数据(如音频和视频效果)的转换在处理输入数据时应忽略不连续。 这些 MFT 通常设计为处理连续数据,并且应将接收的任何数据视为连续数据,即使在中断之后也是如此。

如果 MFT 忽略输入数据的不连续性,则如果输出样本与输入样本具有相同的时间戳,它仍应在输出样本上设置不连续标志。 但是,如果输出示例具有不同的时间戳,则 MFT 不应传播不连续性。 (在某些音频重新采样器中,例如,) 流中错误位置的不连续性比不连续更差。

大多数解码器不能忽略不连续,因为不连续会影响下一个示例的解释。 使用帧间压缩(如 MPEG-2)的任何编码技术都属于此类别。 某些编码方案仅使用帧内压缩,例如 DV 和 MJPEG。 这些解码器可以安全地忽略不连续。

响应不连续的转换通常应在不连续之前输出尽可能多的数据,并丢弃其余数据。 应处理具有不连续标志的输入示例,就像它是流中的第一个示例一样。 (此行为与为 MFT_MESSAGE_COMMAND_DRAIN message.) 指定的内容匹配,但确切的详细信息将取决于媒体格式。

如果解码器不执行任何操作来缓解中断,则应将不连续标志复制到输出数据。 完全与压缩数据一起使用的 Demultiplexers 和其他 MFT 必须将任何不连续内容复制到其输出流中。 否则,下游组件可能无法正确解码压缩的数据。 通常,传递下游的不连续性几乎总是正确,除非 MFT 包含显式代码来平滑中断。

动态格式更改

在流式处理期间,格式可能会更改。 例如,纵横比可以在视频流中间更改。

有关 MFT 如何处理流更改的详细信息,请参阅 处理流更改

流事件

若要将事件发送到 MFT,请调用 IMFTransform::P rocessEvent。 如果该方法返回 MF_S_TRANSFORM_DO_NOT_PROPAGATE_EVENT,MFT 将在后续调用 ProcessOutput 时将事件返回到调用方。 如果该方法返回任何其他 HRESULT 值,MFT 不会将事件返回到 ProcessOutput 中的客户端。 在这种情况下,客户端负责将下游事件传播到管道中的下一个组件(如果适用)。 有关详细信息,请参阅 IMFTransform::P rocessOutput

媒体基础转换