平滑可变帧率(视频)

时间:2015-07-19 01:20:44

标签: c# .net wpf video-streaming

我有一个用于查看ip-camera流的motion-jpeg解码器。它工作得很好,我可以使用多个设备轻松实现超过30 fps。我的问题是这些帧可以通过网络突发。

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

PlaybackFrame )持有BitmapImage的类 - > " 图像"
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;
        }

1 个答案:

答案 0 :(得分:0)

发现我的错误。我在 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;
    }