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

发布于 2024-11-07 03:31:54 字数 716 浏览 3 评论 0原文

我正在尝试使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情释 2024-11-14 03:31:54

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

主线程

  • 在时间 T 对图形进行动画处理并渲染位图
  • 将位图添加到渲染列表。如果列表已满(例如超过 8 帧),请等待。这样您就不会使用太多内存。
  • 使用与所需帧速率相对应的增量时间提前 T

渲染线程

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

您需要一个线程安全结构(例如 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

  • Animate your graphics at time T and render bitmap
  • Add bitmap to renderlist. If list is full (say more than 8 frames) wait. This is so you won't use too much memory.
  • Advance T with deltatime corresponding to desired framerate

Render thread

  • When a frame is requested, pick and remove a bitmap from the renderlist. If list is empty wait.

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.

独木成林 2024-11-14 03:31:54

为了测试整个事情,我正在为我的记录器应用程序(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 就对时间不敏感。

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, when Sleep()ing from frame to frame, please try to use 'absolute' time differences, because Sleep(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:

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);
}

EDIT:

Keep in mind: IWMWritter is time insensitive as long as you feed it with SAMPLES that are properly time-stamped.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文