平滑可变帧率(视频)

本文关键字:视频 帧率 平滑 | 更新日期: 2023-09-27 18:03:20

我编写了一个用于查看ip摄像机流的运动jpeg解码器。它工作得很好,我可以轻松地在多个设备上实现超过30 fps。我的问题是这些帧可以在网络上以突发的形式出现。

我想要实现的是一个公式,用于平均帧之间的时间,以获得更稳定的播放。目前,我的帧从网络线程被放入ConcurrentQueue中,最近的帧显示在UI线程上。下面是我当前平滑视频流的代码,但它不像我计划的那样工作…

PlaybackFrame) Class holding BitmapImage -> "Image"
base.getFrame())从ConcurrentQueue

中检索PlaybackFrame
    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;
    }