我以不一致的速率从源(可视为黑盒)获取原始视频帧。我正在尝试将视频源录制到磁盘。我正在使用 AForge 的 VideoRecorder
这样做,并且正在写入 MP4 文件。
但是,我接收帧的速率不一致导致视频显示速度加快。似乎我只能创建具有固定帧速率的视频文件,即使源没有固定帧速率。
渲染到屏幕时这不是问题,因为我们可以尽可能快地渲染。我无法在写入文件时执行此操作,因为播放文件时会以固定帧速率播放。
有哪些解决方案?输出不必是相同的视频格式,只要有一些合理的方法可以稍后转换它(不一定是实时的)。视频源可能会很长,所以我不能将所有内容都存储在内存中,然后再进行编码。
我的代码目前看起来是这样的:
VideoFileWriter writer = new VideoFileWriter();
Stopwatch stopwatch = new Stopwatch();
public override void Start() {
writer.Open("output.mp4", videoWidth, videoHeight, frameRate, AForge.Video.FFMPEG.VideoCodec.MPEG4);
stopwatch.Start();
}
public override void End() {
writer.Close();
}
public override void Draw(Frame frame) {
double elapsedTimeInSeconds = stopwatch.ElapsedTicks / (double) Stopwatch.Frequency;
double timeBetweenFramesInSeconds = 1.0 / FrameRate;
if (elapsedTimeInSeconds >= timeBetweenFramesInSeconds) {
stopwatch.Restart();
writer.WriteVideoFrame(frame.ToBitmap());
}
}
我们的黑盒调用Start
、End
和Draw
方法的地方。我在 Draw
中进行的当前检查可防止我们绘制得太快,但不会采取任何措施来处理绘制太慢的情况。
最佳答案
原来 WriteVideoFrame
被重载了,函数的一个变体是 WriteVideoFrame(Bitmap frame, TimeSpan timestamp)
.您可以猜到,时间戳用于使帧出现在视频中的特定时间。
因此,通过跟踪实时时间,我们可以将每一帧设置为使用它应该在视频中出现的时间。当然,如果渲染速度不够快,视频质量会变差,但这可以解决手头的问题。
这是我用于Draw
函数的代码:
// We can provide a frame offset so that each frame has a time that it's supposed to be
// seen at. This ensures that the video looks correct if the render rate is lower than
// the frame rate, since these times will be used (it'll be choppy, but at least it'll
// be paced correctly -- necessary so that sound won't go out of sync).
long currentTick = DateTime.Now.Ticks;
StartTick = StartTick ?? currentTick;
var frameOffset = new TimeSpan(currentTick - StartTick.Value);
// Figure out if we need to render this frame to the file (ie, has enough time passed
// that this frame will be in the file?). This prevents us from going over the
// desired frame rate and improves performance (assuming that we can go over the frame
// rate).
double elapsedTimeInSeconds = stopwatch.ElapsedTicks / (double) Stopwatch.Frequency;
double timeBetweenFramesInSeconds = 1.0 / FrameRate;
if (elapsedTimeInSeconds >= timeBetweenFramesInSeconds)
{
stopwatch.Restart();
Writer.WriteVideoFrame(frame.ToBitmap(), frameOffset);
}
StartTick
是对象的 long?
成员。
关于c# - 将视频渲染到帧速率可能不一致的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30225716/