C# 将视频渲染到可能具有不一致帧速率的文件
我从一个源(可以被认为是一个黑匣子)获取原始视频帧的速率可能不一致。我正在试着把视频输入到磁盘上。我正在用一台福格的C# 将视频渲染到可能具有不一致帧速率的文件,c#,video,aforge,C#,Video,Aforge,我从一个源(可以被认为是一个黑匣子)获取原始视频帧的速率可能不一致。我正在试着把视频输入到磁盘上。我正在用一台福格的录像机这样做,并且正在向MP4文件写入数据 但是,我接收帧的速率不一致导致视频速度加快。似乎我只能创建具有固定帧速率的视频文件,即使源没有固定帧速率 这在渲染到屏幕时不是问题,因为我们可以尽可能快地渲染。写入文件时我不能这样做,因为回放文件将以固定的帧速率播放 有什么解决办法?输出不必是相同的视频格式,只要以后有合理的转换方法(不必是实时的)。视频提要可能很长,所以我不能将所有内容
录像机
这样做,并且正在向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());
}
}
其中,我们的黑框调用
开始
、结束
、和绘制
方法。我在Draw
中的当前检查可以防止我们绘制得太快,但无法处理绘制得太慢的情况。结果是WriteVideoFrame
过载,函数的一个变体是。正如您所猜测的,时间戳用于使帧在视频中的特定时间出现
因此,通过跟踪实时,我们可以将每一帧设置为使用它应该在视频中的时间。当然,如果渲染速度不够快,视频质量会更差,但这解决了当前的问题
以下是我用于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?
成员。我也遇到了这个问题。在我的例子中,我正在用一个RGE模拟闭路电视系统。中央电视台在录制的时间上应该是准确的,所以我在这方面面临着一个很大的困境。这是我在这本书中使用的作品
首先,声明一个Timespan,它将作为录制的基础。您需要在开始录制时设置此选项。该值是您开始录制的时间。为了得到这个答案,我们将其称为tmspartrecording
然后在捕获设备的新帧事件中:
var currentTime = DateTime.Now.TimeOfDay;
// this will get the elapse time between
// the current time from the time you start your recording
TimeSpan elapse = currentTime - tmspStartRecording;
writer.WriteVideoFrame((Bitmap)image.Clone(),elapse);
别忘了设置开始时间跨度的值,好吗?如果您使用的是高帧率-
秒表-它可能值得使用,而不是DateTime
减法。除了我(当时出于任何原因)之外,这与我使用的几乎相同通过从DateTime中减去刻度来获得时间跨度。现在
而不是直接减去刻度(这会导致时间跨度
。虽然我很确定您不能使用时间跨度
。如果您的录制时间超过午夜,则会失败。应该只使用日期时间。现在
。我只使用25 fps,即使我的网络摄像头可以处理。应用程序无法足够快地处理该帧。在我的情况下,我会强制使用在午夜时重置保存,然后初始化一个新的。增加一个RGE的帧进程的一个好方法是为编写器创建一个新的单独线程。