You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在BackgroundWorker的DoWork事件中控制视频帧提取速度?

控制BackgroundWorker中视频帧提取与显示的速度

你的问题核心是后台线程提取帧的速度远快于正常播放节奏,直接用Thread.Sleep()容易出异常,下面给两种可靠的解决方案:

方案1:基于视频帧率的精确时间间隔控制

利用视频本身的帧率计算每帧的等待时间,避免固定Sleep带来的误差,同时加入任务取消检查,符合BackgroundWorker的使用规范:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    using (var vFReader = new VideoFileReader())
    {
        vFReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
        double frameRate = vFReader.FrameRate;
        // 计算每帧的理论间隔毫秒数(比如30帧就是约33ms)
        int frameInterval = (int)(1000 / frameRate);
        long lastFrameTick = DateTime.Now.Ticks;

        for (var i = 0; i < vFReader.FrameCount; i++)
        {
            // 读取当前帧并上报给UI
            var currentFrame = vFReader.ReadVideoFrame();
            backgroundWorker1.ReportProgress(i, currentFrame);

            // 计算已消耗时间,动态调整等待时长
            long currentTick = DateTime.Now.Ticks;
            long elapsedMs = (currentTick - lastFrameTick) / TimeSpan.TicksPerMillisecond;
            int waitMs = frameInterval - (int)elapsedMs;

            // 仅当需要等待且任务未取消时执行Sleep
            if (waitMs > 0 && !backgroundWorker1.CancellationPending)
            {
                Thread.Sleep(waitMs);
            }

            lastFrameTick = DateTime.Now.Ticks;

            // 检查是否需要终止任务
            if (backgroundWorker1.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
        }
    }
}

方案2:用线程定时器调度帧读取

如果不想在循环里阻塞线程,可以用System.Threading.Timer来定时触发帧读取操作,更灵活且易于调整速度:

首先在Form类里添加字段:

private VideoFileReader _videoReader;
private int _currentFrameIndex;
private System.Threading.Timer _frameTimer;
private int _frameInterval; // 自定义帧间隔毫秒数

修改启动按钮的点击事件:

private void button1_Click(object sender, EventArgs e)
{
    // 初始化视频阅读器
    _videoReader = new VideoFileReader();
    _videoReader.Open(@"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
    _currentFrameIndex = 0;
    // 按原帧率设置间隔,也可换成TrackBar的值自定义速度
    _frameInterval = (int)(1000 / _videoReader.FrameRate);

    // 启动定时器:立即执行第一次,之后每隔_frameInterval毫秒执行一次
    _frameTimer = new System.Threading.Timer(ReadFrameCallback, null, 0, _frameInterval);
}

添加定时器回调方法:

private void ReadFrameCallback(object state)
{
    // 终止条件:帧读完或任务被取消
    if (_currentFrameIndex >= _videoReader.FrameCount || backgroundWorker1.CancellationPending)
    {
        _frameTimer.Change(Timeout.Infinite, Timeout.Infinite);
        _videoReader.Close();
        _videoReader.Dispose();
        return;
    }

    // 读取帧并上报
    var frame = _videoReader.ReadVideoFrame();
    backgroundWorker1.ReportProgress(_currentFrameIndex, frame);
    _currentFrameIndex++;
}

最后添加窗体关闭时的资源清理:

protected override void OnFormClosing(FormClosingEventArgs e)
{
    _frameTimer?.Change(Timeout.Infinite, Timeout.Infinite);
    _frameTimer?.Dispose();
    _videoReader?.Close();
    _videoReader?.Dispose();
    base.OnFormClosing(e);
}

关于Thread.Sleep()触发异常的说明

你之前遇到的异常大概率不是Sleep本身导致的,而是缺少任务取消检查、资源提前释放,或者Sleep期间UI线程的操作和后台线程产生冲突。上面两种方案都加入了资源管理和取消逻辑,能避免这类问题。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 22:10:27