将视频渲染到可能具有不一致帧速率的文件

时间:2015-05-13 21:45:29

标签: c# video aforge

我从源(可被视为黑盒子)获取原始视频帧的速率可能不一致。我正在尝试将视频源录制到磁盘上。我正在与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());
    }
}

我们的黑匣子调用StartEndDraw方法。我在Draw中进行的当前检查阻止我们绘制太快,但是没有做任何事情来处理绘制太慢的情况。

2 个答案:

答案 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的值,好吗?