我正在尝试使用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()延迟的方式并使用更好的计时机制?
任何建议都将不胜感激!
答案 0 :(得分:2)
我用线程做这个。主线程是将位图添加到列表中,记录器线程从该列表中获取位图。
主线程
渲染线程
您需要一个线程安全结构(如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来提供它。