编写媒体基础MFT组件

翻译:a1875566250 @ CSDN博客,转载请注明出处。

- 基本MFT处理模型 -
原文: http://msdn.microsoft.com/en-us/library/windows/desktop/aa965264%28v=vs.85%29.aspx

本主题适用于:
1、应用程序希望直接使用MFT对象;
2、应用程序编写自定义的MFT组件实现对某些解码格式的支持。

本主题并不适用于异步MFT模型(Asynchronous MFTs)。

【创建MFT对象】
你可以使用以下的方法创建MFT组件:
● 使用 MFTEnum 函数。
● 使用 MFTEnumEx 函数。

如果你知道某个MFT组件的CLSID,可以直接使用CoCreateInstance函数创建IMFTransform对象。
或者你通过其他某个方法\导出函数\对象得到IMFTransform对象。

【取得流标识符】
MFT组件可以处理一个或多个流,输入的流接收输入的数据,输出的流生成输出的数据。每个流并不表示它是固定明确的。
某些MFT组件还允许客户添加\删除输入流,在处理期间,MFT组件也可以自己动态的添加\删除输出流。

1、(可选)第一步你可以调用IMFTransform::GetStreamLimits方法取得最大和最小的流数量,如果最大最小的流数量都是相等的(比如都为1),则此次MFT处理的流是恒定数目的。
2、使用IMFTransform::GetStreamCount取得输入和输出的流数目。
3、使用IMFTransform::GetStreamIDs传入索引取得每个流的ID,如果此方法不实现,则流使用0开始的下标作为ID。
4、(可选)如果在第一步,你知道你的允许多流输入输出,则客户可以使用IMFTransform::AddInputStreams方法添加新的输入流,或者使用IMFTransform::DeleteInputStream删除一个输入流,但是客户不能添加\删除输出流,这是MFT本身控制的。

【设置媒体类型描述】
在取得流的定义后,MFT可以开始处理输入和输出,在处理输入之前,客户必需给某一个流设置输入(Input)的媒体类型。
MFT组件也可以允许客户先设置输出(Output)类型再提供输入类型,或者MFT强制要求客户先设置输出类型,都是可以的。
MFT组件也可以提供默认的的匹配输入、输出类型。

1、(可选)调用IMFTransform::GetInputAvailableType取得MFT组件默认提供的可用输入类型,此方法返回MFT组件最希望你提供的输入类型。
 - 如果此方法返回MF_E_TRANSFORM_TYPE_NOT_SET,则你必需先设置输出类型,Skip到第3步;
 - 如果此方法返回E_NOTIMPL,则MFT不提供最匹配的输入类型,继续执行第2步。

2、调用IMFTransform::SetInputType设置输入的类型,你可以使用在第1步中获取的到的最匹配的输入类型,也可以自己创建自定义的类型对象(但是MFT可能不支持),如果此方法返回MF_E_TRANSFORM_TYPE_NOT_SET,则你需要先执行第3步。

3、(可选)调用IMFTransform::GetOutputAvailableType取得MFT组件默认提供的可用输出类型,此方法返回MFT组件最好的输出类型。
 - 如果此方法返回MF_E_TRANSFORM_TYPE_NOT_SET,则你必需先设置输入类型,回到第1步;
 - 如果此方法返回E_NOTIMPL,则MFT不提供最好的输出类型,继续执行第4步。

4、调用IMFTransform::SetOutputType设置输出类型,你可以使用在第3步中获取到的最匹配的输出类型,也可以自己创建自定义的类型(可能不支持)。

5、按照第1-4步,循环设置每个流的输入类型。

【取得流的输入\输出信息】
在设置输入类型后,你则可以使用IMFTransform::GetInputStreamInfo取得流的输入信息,使用IMFTransform::GetOutputStreamInfo取得流的输出信息,根据信息配置接下来的处理方式。

【处理数据】
MFT设计为可靠的状态主机模型,它并不使用任何回调来通知客户端。

1、调用IMFTransform::ProcessMessage方法,发送MFT_MESSAGE_NOTIFY_BEGIN_STREAMING消息,这个消息表示希望MFT现在开始申请处理资源,准备开始处理流数据。
2、调用IMFTransform::ProcessInput方法,给流提供输入的样本数据。
3、(可选)调用IMFTransform::GetOutputStatus方法,此方法可以查询MFT在处理输入后,是否已经成功生成可以输出的数据,有以下情况:
 - 如果其返回S_OK,并且pdwFlags含有MFT_OUTPUT_STATUS_SAMPLE_READY,则执行第4步;
 - 如果其返回S_OK,并且pdwFlags不包含MFT_OUTPUT_STATUS_SAMPLE_READY,则表示需要更多的数据,返回第2步;
 - 如果其返回E_NOTIMPL,则执行第4步。

4、调用IMFTransform::ProcessOutput取得输出的数据:
 - 如果其返回MF_E_TRANSFORM_NEED_MORE_INPUT,则MFT目前需要更多的输入数据,继续返回第2步;
 - 如果其返回MF_E_TRANSFORM_STREAM_CHANGE,则在本次处理输入后,流的数据类型已经改变,客户需要重新查询流定义,设置输出类型,更多信息请查看ProcessOutput方法文档。
5、不断进行IMFTransform::ProcessOutput操作,直到第2步交进去的输出流已经被全部处理完成。

6、发送MFT_MESSAGE_NOTIFY_END_OF_STREAM消息。
7、发送MFT_MESSAGE_COMMAND_DRAIN消息。
8、此时MFT可能还有一些数据残渣可以拿到,调用IMFTransform::ProcessOutput尝试取得数据残渣,直到此方法返回MF_E_TRANSFORM_NEED_MORE_INPUT,则回到第二步继续这样的循环。

在每次调用ProcessInput后,客户端都应该尝试调用ProcessOutput(除非MFT在GetOutputStatus明确表明需要更多输入)。
输入N个样本,ProcessOutput可能只产生一个样本,也可能输入1个样本,ProcessOutput可以有多个样本,客户端最好的做法是循环调用ProcessOutput方法直到其表示需要更多输入数据后退出。

并且,MFT的ProcessInput和ProcessOutput是可以允许交替调用的,比如,客户端可以这样调用:
ProcessInput->ProcessInput->ProcessOutput->ProcessInput。
如果MFT是线性的输入\输出模型,即输入了,必需输出后才能接受新的输入,在输入后,继续调用ProcessInput可以返回MF_E_NOTACCEPTING。

我们描述的以上步骤,客户端可能并不一定按照我们写的步骤来,请注意。
在任何使用,客户端都可能调用MFT的某些方法,比如GetInputCurrentType、GetOutputStreamInfo,客户端也可能在任意时候试图更改MFT中的媒体类型,如果MFT不支持,则应该返回错误,总之,MFT应该承担尽可能少的操作,比如记录自己被客户端调用的顺序等。

下面这张图描述了一个标准的流程:

【刷新MFT流】
当MFT中可能有输入的数据或者准备输出的时候,客户端可以发送MFT_MESSAGE_COMMAND_FLUSH消息表示让MFT丢弃这些数据,也就是清空缓存的意思。

【取得残渣】
MFT可以有内部的缓冲,如果客户端希望把所有缓冲起来的数据全部输出,则发送MFT_MESSAGE_COMMAND_DRAIN后,执行ProcessOutput,直到其返回MF_E_TRANSFORM_NEED_MORE_INPUT。

【样本中断】
为了处理IPB帧的问题,当样本表示自己是中断样本时,MFT应该自动丢弃前面所有缓冲的残渣,开始新一轮的处理。

- 时间戳和样本长度 -
原文: http://msdn.microsoft.com/en-us/library/windows/desktop/dd940438%28v=vs.85%29.aspx

这个主题描述了MFT应该如何处理时间戳的问题。
每个MFT都应该准确的为它输出的样本打上时间戳和长度。对于一个简单的MFT来说,它的一个输入样本就等于一个输出样本,则只是把时间戳进行简单的复制即可,但是还是有很多MFT的情况比这个复杂,不管如何,所有MFT都应该遵循以下的基本原则:
● 如果MFT输出的数据是(解码后)未压缩的数据,则MFT必需给样本打上PTS,尽可能的给样本打上持续时间,这样能保证Renderer准确的安排绘制\输出。
● 输入给MFT的PTS和持续时间,在输出样本后,尽可能的跟输入的相同。
● MFT如果进行块性处理,比如把输入的数据打分为几个块进行输出,则输出的PTS和持续时间都应该从输入的第一个样本的时间戳开始计算。
● 如果输入的样本有持续时间,则应该保留这个时间交为输出,如果没有,则MFT尽可能的计算输出样本的持续时间。
● 计算出来的持续时间不应该进行四舍五入,而永远保证为使用rounded down(向下舍入)原则。

【解码器】
因为解码器转换压缩数据到未压缩数据,解码器在输出的时候有义务保证输出的样本的PTS和持续时间是正确的,并且如果输入的压缩样本的时间戳可能是错误的,则在输出后,解码器应该把时间戳调整为正确的。

对于视频,如果持续时间对于输出的压缩格式不可用,解码器应计算的持续时间为帧率的倒数,换算为100纳秒单位后向下舍入。
对于音频,如果持续时间对于输出的压缩格式不可用,解码器应计算的持续时间作为音频采样率乘以在输出缓冲的样本数的倒数,换算为100纳秒单位后向下舍入。

如果输入的样本没有有效的时间戳,MFT可以忽略此样本的处理、或者忽略输出,也可以不打上时间戳来进行输出。

【音频解码器】
因为Renderer通过音频时间戳来进行同步,则音频解码器在输出的时候原则上都应该为输出的样本打上持续时间戳,以保证Renderer的时钟同步不会出现问题。
● 因为输入的样本缓存可能包括一个或更多的样本数据,如果它输入的是一个完整的包,则在输出时的时间戳应该等于输入的时间戳,不过在某些有延迟的解码器,比如AC3解码器其延迟256个PCM样本,在48kHz的采样率的情况下,它延迟5.33ms,(256 / 48000),于是,如果输入样本的时间戳是1000ms,则输出样本的时间戳应该是1000 - 5.33 = 994.66ms。
● 如果输入的样本是N个包合并在一起的,比如一个输入的缓冲区里面,可以有2-3个包,每个包可以解码出1024个样本,并且MFT如果是合并这些包作为一个样本输出的,则PTS是第一个包的PTS,而持续时间应该是包的持续时间累加起来的和。
● 下一个输出样本的PTS应该从上一个输出样本的PTS来计算,以保证其没有空隙。
● 如果某些输入的样本因为特殊的原因没有PTS,则解码器在输出时必需打上PTS,以保证他们没有空隙。
● 如果输入的样本本身就是有空隙的,则解码器不应该去修复这些空隙,而是直接传递。

你可能感兴趣的:(编写媒体基础MFT组件)