平滑可变帧率(视频)
本文关键字:视频 帧率 平滑 | 更新日期: 2023-09-27 18:03:20
我编写了一个用于查看ip摄像机流的运动jpeg解码器。它工作得很好,我可以轻松地在多个设备上实现超过30 fps。我的问题是这些帧可以在网络上以突发的形式出现。
我想要实现的是一个公式,用于平均帧之间的时间,以获得更稳定的播放。目前,我的帧从网络线程被放入ConcurrentQueue中,最近的帧显示在UI线程上。下面是我当前平滑视频流的代码,但它不像我计划的那样工作…
PlaybackFrame) Class holding BitmapImage -> "Image"
base.getFrame())从ConcurrentQueue
private const int MAX_FRAME_DELAY = 500;
private const float RATE_FACTOR = 0.1f;
private long last_frame_time;
private long last_update_time;
private float rate;
//=============================
public override bool Get(out BitmapImage image) {
PlaybackFrame f = null;
if (base.getFrame(out f)) {
long now = Environment.TickCount;
if (last_frame_time > 0) {
// Get # of frames in buffer
int count = getCount();
//
// Get duration since last update & last frame displayed
int update_duration = (int)(now - last_update_time);
int frame_duration = (int)(now - last_frame_time);
//
// estimated delay based on current frame-rate
float target_rate = 0;
if (count > 0) target_rate = update_duration / (float)count;
//
// offset actual delay/rate by current value
last_update_time = now;
rate = lerp(rate, target_rate, RATE_FACTOR);
//
// [backup] if duration exceeds 0.5 seconds, display next frame
if (frame_duration >= MAX_FRAME_DELAY) {
image = f.Image;
last_frame_time = now;
return true;
}
//
// if duration exceeds delay, display image
if (frame_duration > rate) {
image = f.Image;
last_frame_time = now;
return true;
} else {
// too soon, wait...
image = null;
return false;
}
} else {
// first image, display
last_frame_time = now;
image = f.Image;
return true;
}
} else {
// no image available
image = null;
return false;
}
}
private float lerp(float a, float b, float f) {
return a*(1f - f) + b*f;
}
发现我的错误了。我在base.getFrame()方法中计算时间测量值,该方法仅在帧可用时执行。通过将测量值移出这个块,它们在每个渲染事件上都被更新,基于缓冲区内可用的帧数创建一个平滑的时间步。
-
仍然需要清理,但工作得很好…
private const int MAX_FRAME_DELAY = 500; private const float RATE_FACTOR = 0.01f; private long last_frame_time; private long last_update_time; private float rate; //============================= public override bool Get(out BitmapImage image) { PlaybackFrame f = null; long now = Environment.TickCount; if (last_frame_time > 0) { int count = getCount(); // int update_duration = (int)(now - last_update_time); int frame_duration = (int)(now - last_frame_time); // float target_rate = 0; if (count > 0) target_rate = update_duration / (float)count; // last_update_time = now; rate = lerp(rate, target_rate, RATE_FACTOR); // if (frame_duration >= MAX_FRAME_DELAY) { if (getFrame(out f)) { rate = MAX_FRAME_DELAY; last_frame_time = now; image = f.Image; return true; } else { image = null; return false; } } // if (frame_duration > rate) { if (getFrame(out f)) { last_frame_time = now; image = f.Image; return true; } else { image = null; return false; } } else { image = null; return false; } } else { if (getFrame(out f)) { last_frame_time = now; image = f.Image; return true; } else { image = null; return false; } } } private float lerp(float a, float b, float f) { return a*(1f - f) + b*f; }