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

使用Accord.Video.FFMPEG录屏时CPU与内存占用过高问题求助

解决Accord.Video.FFMPEG录屏时CPU和内存占用过高的问题

看了你的代码和问题描述,核心问题出在GDI+资源未正确释放,以及编码配置优化不足,这两个点是导致CPU飙升和内存暴增的主要原因。下面一步步给你拆解解决方案:

1. 修复GDI+资源泄漏(内存暴增的核心原因)

你代码里的Graphics、Pen这些GDI+对象都实现了IDisposable接口,但你没有手动释放它们——GC不会自动回收非托管的GDI资源,这会导致这些对象一直占用内存,最终累积到700MB以上。同时,未释放的资源也会让CPU因为资源竞争而升高。

修改你的帧处理代码,用using语句自动包裹需要释放的对象:

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs) 
{
    try 
    {
        if (this._isRecording) 
        {
            int screenRecordingLength = screenRecrodingRule != null ? screenRecrodingRule.length : 500;
            // 克隆原帧,避免影响框架内部的资源管理
            using (Bitmap frame = (Bitmap)eventArgs.Frame.Clone()) 
            {
                using (Graphics graphics = Graphics.FromImage(frame))
                {
                    CURSORINFO pci;
                    pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(CURSORINFO));
                    if (GetCursorInfo(out pci) && pci.flags == CURSOR_SHOWING) 
                    {
                        int x = pci.ptScreenPos.x - screenLeft;
                        int y = pci.ptScreenPos.y - screenTop;
                        Color c = Color.Yellow;
                        float width = 2;
                        int radius = 30;

                        if ((Control.MouseButtons & MouseButtons.Left) != 0 || (Control.MouseButtons & MouseButtons.Right) != 0) 
                        {
                            c = Color.OrangeRed;
                            width = 4;
                            radius = 35;
                        }

                        // 用using包裹Pen,自动释放资源
                        using (Pen p = new Pen(c, width))
                        {
                            graphics.DrawEllipse(p, x - radius / 2, y - radius / 2, radius, radius);
                        }

                        // 确保Hdc资源一定会被释放
                        IntPtr hdc = graphics.GetHdc();
                        try
                        {
                            DrawIcon(hdc, x, y, pci.hCursor);
                        }
                        finally
                        {
                            graphics.ReleaseHdc(hdc);
                        }
                    }

                    // 恢复帧写入,异常时记录日志而非跳过,避免帧堆积
                    try
                    {
                        this._writer.WriteVideoFrame(frame, (DateTime.Now - videoStartTime));
                    }
                    catch (Exception ex)
                    {
                        log.ErrorFormat("SR: Failed to write video frame: {0}", ex.Message);
                    }
                }
            }

            // 原有停止逻辑保持不变
            if (DateTime.Now.Subtract(videoStartTime).TotalSeconds > screenRecordingLength) 
            {
                log.DebugFormat("SR: Stopping the video capturing as the time limit is exceded config length:{0}, video length: {1}.", screenRecrodingRule.length, DateTime.Now.Subtract(videoStartTime).TotalSeconds);
                isVideoCaptureCompeted = true;
                previousScreenRecodringRule = null;
                _streamVideo.SignalToStop();
                Thread.Sleep(100);
                _writer.Flush();
                Thread.Sleep(100);
                _writer.Close();
                StopVideoCapture();
            }
        } 
        else 
        {
            _streamVideo.SignalToStop();
            Thread.Sleep(100);
            _writer.Flush();
            Thread.Sleep(100);
            _writer.Close();
        }
    } 
    catch (Exception ex) 
    {
        log.ErrorFormat("SR: Exception occured while capturing the video {0}, {1}", ex.Message, ex.StackTrace);
    }
}

关键修改点:

  • 用using包裹Bitmap、Graphics、Pen,确保每次处理完帧后自动释放所有GDI资源。
  • 对GetHdc()的调用添加try-finally,保证ReleaseHdc()一定会执行,避免Hdc资源泄漏。
  • 恢复WriteVideoFrame的调用,异常时记录日志而不是跳过,避免未释放的帧堆积。

2. 优化FFMPEG编码配置(降低CPU占用)

默认的编码配置可能没有做优化,导致CPU压力过大。初始化VideoFileWriter时,选择高效的编码格式并调整参数:

// 示例初始化代码
int fps = 15; // 降低帧率到15-20fps,足够流畅且减少CPU压力
int bitrate = 1000000; // 调整码率,平衡质量和文件大小/CPU占用
_writer = new VideoFileWriter();
_writer.Open(
    "output.mp4", 
    screenWidth, 
    screenHeight, 
    fps, 
    VideoCodec.H264, // 使用H.264编码,比默认编码更高效
    bitrate
);
// 添加CRF参数,恒定质量模式,减少编码波动,降低CPU占用
_writer.AddOption("crf", "23"); // 23是默认值,值越高压缩率越高(质量略降),CPU占用越低

3. 其他优化细节

  • 缓存状态减少重复计算:比如鼠标按钮状态的判断,可以缓存前一次的状态,只有当状态变化时才更新颜色/尺寸,减少重复运算。
  • 监控帧队列堆积:如果WriteVideoFrame的速度跟不上采集速度,会导致帧在内存中堆积。可以添加日志监控帧写入耗时,或设置不超过屏幕刷新频率的帧率(一般15-25fps足够)。
  • 彻底释放录屏资源:确保StopVideoCapture方法中正确释放_streamVideo资源,比如调用_streamVideo.WaitForStop()而非仅SignalToStop(),避免线程残留。

至于你尝试的GC.Collect()无效,是因为GC只能回收托管资源,而GDI+的Graphics、Pen等包含非托管资源,必须手动调用Dispose()才能释放,所以单纯调用GC无法解决内存问题。

内容的提问来源于stack exchange,提问作者Ravi Kanth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:02:36