我从源(可被视为黑盒子)获取原始视频帧的速率可能不一致。我正在尝试将视频源录制到磁盘上。我正在与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
中进行的当前检查阻止我们绘制太快,但是没有做任何事情来处理绘制太慢的情况。
答案 0 :(得分:3)
结果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?
成员。
答案 1 :(得分:1)
我也遇到过这个问题。在我的情况下,我使用Aforge模仿CCTV系统。中央电视台在录制时应该准确,所以我面临着一个很大的困境。这是我在这里使用的工作。
首先,声明一个Timespan,它将成为录制的基础。您需要在开始录制意义时设置此项。此值是您开始录制的时间。为了这个答案,让我们称之为tmspStartRecording
然后在捕获设备的新帧事件中:
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);
不要忘记设置启动Timespan的值,好吗?