开发者

How can I ensure the correct frame rate when recording an animation using DirectShow?

开发者 https://www.devze.com 2023-03-05 08:47 出处:网络
I am attempting to record an animation (computer graphics, not video) to a WMV file using DirectShow.The setup is:

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 fr开发者_C百科ames 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!


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消