使用DirectShow录制动画时,如何确保正确的帧速率?

时间:2011-05-13 06:55:37

标签: delphi directshow

我正在尝试使用DirectShow将动画(计算机图形,而不是视频)录制到WMV文件。设置是:

  • 使用保存动画帧的内存位图的推送源。每次调用FillBuffer()时,都会将位图的数据复制到样本中,并使用开始时间(帧编号*帧长度)和持续时间(帧长度)对样本加时间戳。滤波器中的帧速率设置为每秒10帧。

  • ASF Writer过滤器。我有一个自定义配置文件,将视频设置为每秒10帧。它只是一个视频过滤器,因此没有音频。

引脚连接,运行图表时,会创建一个wmv文件。但...

问题是看起来DirectShow正以大于10 FPS的速率从推送源推送数据。因此得到的wmv虽然可播放并包含正确的动画(以及报告正确的FPS),但播放动画的次数太慢,因为在录制期间视频中添加了太多帧。也就是说,10 FPS的10秒视频应该只有100帧,但大约有500帧被填充到视频中,导致视频长达50秒。

我最初尝试解决方案只是通过添加sleep()1/10秒来减慢FillBuffer()调用。这确实或多或少都有效。但它似乎是hackish,我怀疑这是否适用于更高的FPS。

所以我想知道是否有更好的方法来做到这一点。实际上,我假设有更好的方法,我只是错过了它。或者我是否只需要改善推送源中FillBuffer()延迟的方式并使用更好的计时机制?

任何建议都将不胜感激!

2 个答案:

答案 0 :(得分:2)

我用线程做这个。主线程是将位图添加到列表中,记录器线程从该列表中获取位图。

主线程

  • 在时间T为您的图形设置动画并渲染位图
  • 将位图添加到renderlist。如果列表已满(比如超过8帧),请等待。这样你就不会使用太多的内存。
  • 使用对应于所需帧速率的deltatime提前T

渲染线程

  • 请求框架时,从渲染列表中选择并删除位图。如果列表为空,请等待。

您需要一个线程安全结构(如TThreadList)来保存位图。要做到这一点有点棘手,但目前的方法可以保证解决时间问题。

答案 1 :(得分:1)

我正在为我的录音机应用程序(www.videophill.com)做正确的事情,以测试整个事情。

我正在使用Sleep()方法来延迟帧,但我要非常小心地确保帧的时间戳是正确的。此外,当Sleep()逐帧时,请尝试使用“绝对”时差,因为Sleep(100)将睡眠约100毫秒,而不是100毫秒。

如果它对您不起作用,您可以随时使用IReferenceClock,但我认为这样做太过分了。

所以:

DateTime start=DateTime.Now;
int frameCounter=0;
while (wePush)
{
    FillDataBuffer(...);
    frameCounter++;
    DateTime nextFrameTime=start.AddMilliseconds(frameCounter*100);
    int delay=(nextFrameTime-DateTime.Now).TotalMilliseconds;
    Sleep(delay);
}

编辑:

请注意:IWMWritter对时间不敏感,只要您使用带有正确时间戳的SAMPLES来提供它。