使用 DirectShow 录制动画时如何确保正确的帧速率?
我正在尝试使用 DirectShow 将动画(计算机图形,而不是视频)录制到 WMV 文件。设置是:
使用保存动画帧的内存位图的推送源。每次调用 FillBuffer() 时,位图的数据都会复制到样本中,并且样本会带有开始时间(帧编号 * 帧长度)和持续时间(帧长度)的时间戳。过滤器中的帧速率设置为每秒 10 帧。
ASF Writer 过滤器。我有一个自定义配置文件,将视频设置为每秒 10 帧。它是纯视频过滤器,因此没有音频。
引脚连接,当图形运行时,会创建一个 wmv 文件。但是...
问题是 DirectShow 似乎正在以大于 10 FPS 的速率从推送源推送数据。因此,生成的 wmv 虽然可播放并包含正确的动画(以及报告正确的 FPS),但由于在录制期间向视频添加了太多帧,因此播放动画的速度太慢。也就是说,10 FPS 的 10 秒视频应该只有 100 帧,但视频中塞入了大约 500 帧,导致视频长度为 50 秒。
我最初尝试的解决方案只是通过添加 1/10 秒的 sleep() 来减慢 FillBuffer() 调用。这确实或多或少起到了作用。但这看起来很黑客,我怀疑这在更高的 FPS 下是否能正常工作。
所以我想知道是否有更好的方法来做到这一点。事实上,我假设有更好的方法,但我只是错过了。或者我只需要改进 Push Source 中 FillBuffer() 延迟的方式并使用更好的计时机制?
任何建议将不胜感激!
I am attempting to record an animation (computer graphics, not video) to a WMV file using DirectShow. The setup is:
A Push Source that uses an in-memory bitmap holding the animation frame. Each time FillBuffer() is called, the bitmap's data is copied over into the sample, and the sample is timestamped with a start time (frame number * frame length) and duration (frame length). The frame rate is set to 10 frames per second in the filter.
An ASF Writer filter. I have a custom profile file that sets the video to 10 frames per second. Its a video-only filter, so there's no audio.
The pins connect, and when the graph is run, a wmv file is created. But...
The problem is it appears DirectShow is pushing data from the Push Source at a rate greater than 10 FPS. So the resultant wmv, while playable and containing the correct animation (as well as reporting the correct FPS), plays the animation back several times too slowly because too many frames were added to the video during recording. That is, a 10 second video at 10 FPS should only have 100 frames, but about 500 are being stuffed into the video, resulting in the video being 50 seconds long.
My initial attempt at a solution was just to slow down the FillBuffer() call by adding a sleep() for 1/10th second. And that indeed does more or less work. But it seems hackish, and I question whether that would work well at higher FPS.
So I'm wondering if there's a better way to do this. Actually, I'm assuming there's a better way and I'm just missing it. Or do I just need to smarten up the manner in which FillBuffer() in the Push Source is delayed and use a better timing mechanism?
Any suggestions would be greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我用线程来做这个。主线程将位图添加到列表中,记录器线程从该列表中获取位图。
主线程
渲染线程
您需要一个线程安全结构(例如 TThreadList)来保存位图。正确执行有点棘手,但您当前的方法肯定会导致计时问题。
I do this with threads. The main thread is adding bitmaps to a list and the recorder thread takes bitmaps from that list.
Main thread
Render thread
You need a threadsafe structure such as TThreadList to hold the bitmaps. It's a bit tricky to get right but your current approach is guaranteed to give to timing problems.
为了测试整个事情,我正在为我的记录器应用程序(www.videophill.com)做正确的事情。
我正在使用
Sleep()
方法来延迟帧,但我非常小心地确保帧的时间戳是正确的。另外,当Sleep()
从一帧到另一帧时,请尝试使用“绝对”时间差,因为Sleep(100)
将休眠大约 100 毫秒,而不是正好 100 毫秒。如果它不适合你,你可以随时使用 IReferenceClock,但我认为这太过分了。
所以:
编辑:
请记住:只要您提供带有正确时间戳的样本,
IWMWritter
就对时间不敏感。I am doing just the right thing for my recorder application (www.videophill.com) for purposes of testing the whole thing.
I am using
Sleep()
method to delay the frames, but am taking great care to ensure that timestamps of the frames are correct. Also, whenSleep()
ing from frame to frame, please try to use 'absolute' time differences, becauseSleep(100)
will sleep about 100ms, not exactly 100ms.If it won't work for you, you can always go for IReferenceClock, but I think that's overkill here.
So:
EDIT:
Keep in mind:
IWMWritter
is time insensitive as long as you feed it with SAMPLES that are properly time-stamped.