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

Delphi 使用DirectShow录制动画时,如何确保正确的帧速率?,delphi,directshow,Delphi,Directshow,我正在尝试使用DirectShow将动画(计算机图形,而不是视频)录制到WMV文件。设置为: 使用内存中位图保存动画帧的推送源。每次调用FillBuffer()时,位图的数据都会复制到样本中,并在样本上加上开始时间(帧编号*帧长度)和持续时间(帧长度)的时间戳。在过滤器中,帧速率设置为每秒10帧 ASF编写器过滤器。我有一个自定义配置文件,将视频设置为每秒10帧。它是一个仅用于视频的过滤器,因此没有音频 管脚连接,当图形运行时,将创建一个wmv文件。但是 问题在于DirectShow似乎正在

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

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

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

管脚连接,当图形运行时,将创建一个wmv文件。但是

问题在于DirectShow似乎正在以大于10 FPS的速率从推送源推送数据。因此,生成的wmv虽然可以播放并包含正确的动画(以及报告正确的FPS),但播放动画的速度太慢,因为在录制过程中向视频添加了太多帧。也就是说,每秒10帧的10秒视频应该只有100帧,但视频中填充了大约500帧,导致视频长度为50秒

我最初尝试的解决方案只是通过添加sleep()使FillBuffer()调用慢了1/10秒。这确实或多或少起到了作用。但这似乎有点骇人听闻,我怀疑这在更高的FPS下是否有效

所以我想知道是否有更好的方法。事实上,我想有更好的方法,我只是错过了。或者我只是需要改进推送源中FillBuffer()的延迟方式,并使用更好的计时机制


如有任何建议,将不胜感激

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

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

如果它对你不起作用,你可以选择iReferenceLock,但我认为这太过分了

因此:

编辑:


请记住:
iwmwitter
是不区分时间的,只要您为它提供了正确的时间戳样本。

我使用线程来实现这一点。主线程将位图添加到列表中,记录器线程从该列表中获取位图

主线

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

  • 请求帧时,从渲染列表中拾取并删除位图。如果列表为空,请等待

您需要一个线程安全结构(如TThreadList)来保存位图。这有点棘手,但您当前的方法肯定会解决计时问题。

我是.NET的人,但希望您能从我的代码片段中理解我的意思。谢谢。最后,我使用QueryPerformanceCounter()来获得比仅使用sleep()更精确的计时(您注意到它并不精确)。它主要记录最后一帧的时钟时间,当再次调用FillBuffer()时,会将其暂停适当的时间。生成的WMV文件具有正确的持续时间。您可以使用DateTime,这并不重要。重要的是你要保持绝对的速度,而不是相对于最后一帧。
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);
}