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

使用Emgu.CV开发RTSP播放器遇AccessViolationException及帧率问题求助

问题解决方案

一、解决释放时的AccessViolationException

核心原因

UpdateFrame方法由定时器/后台线程周期性触发,释放capture时线程仍在尝试访问已释放的资源,引发内存非法访问。同时代码存在**重复调用QueryFrame()**的问题,既丢帧又加剧线程冲突风险。

修复步骤

  1. 新增线程同步标识,控制捕获逻辑的执行
  2. 调整锁范围,避免无效同步
  3. 释放capture前先终止捕获线程/定时器,再同步清理资源

修改后的UpdateFrame代码:

private bool _isCaptureActive = true; // 新增捕获状态标识
private void UpdateFrame(object sender, EventArgs e)
{
    try
    {
        // 先判断捕获是否活跃,避免释放后继续执行
        if (!_isCaptureActive || capture == null || capture.Ptr == IntPtr.Zero)
            return;

        // 仅调用一次QueryFrame,避免丢帧和重复资源访问
        using (var mat = capture.QueryFrame())
        {
            if (mat == null)
            {
                Debug.WriteLine("QueryFrame = null");
                return;
            }

            using (Image<Bgr, byte> image = mat.ToImage<Bgr, byte>())
            {
                lock (captureLock)
                {
                    isPlaying = true;
                    if (!isRecording && videoWriter != null)
                    {
                        videoWriter.Dispose();
                        videoWriter = null; // 释放后置空,避免重复操作
                    }
                    else if (isRecording && videoWriter != null)
                    {
                        videoWriter.Write(image);
                    }

                    // 用BeginInvoke替代Invoke,避免阻塞捕获线程
                    if (pictureBoxMain.InvokeRequired)
                    {
                        pictureBoxMain.BeginInvoke((MethodInvoker)(() => 
                        {
                            // 释放旧Bitmap,避免内存泄漏
                            if (pictureBoxMain.Image != null)
                                pictureBoxMain.Image.Dispose();
                            pictureBoxMain.Image = image.ToBitmap();
                        }));
                    }
                    else
                    {
                        if (pictureBoxMain.Image != null)
                            pictureBoxMain.Image.Dispose();
                        pictureBoxMain.Image = image.ToBitmap();
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine("UC_MPP.MPP.UpdateFrame : " + ex.Message);
    }
}

释放capture的正确逻辑

private void StopCapture()
{
    // 先终止捕获状态,阻止UpdateFrame继续执行
    _isCaptureActive = false;
    // 等待当前捕获逻辑执行完成(根据线程模型调整)
    Thread.Sleep(100);
    
    lock (captureLock)
    {
        if (videoWriter != null)
        {
            videoWriter.Dispose();
            videoWriter = null;
        }
        
        if (capture != null)
        {
            capture.Dispose();
            capture = null;
        }
        
        isPlaying = false;
    }
    
    // 清理UI资源
    if (pictureBoxMain.InvokeRequired)
    {
        pictureBoxMain.Invoke((MethodInvoker)(() => 
        {
            if (pictureBoxMain.Image != null)
                pictureBoxMain.Image.Dispose();
            pictureBoxMain.Image = null;
        }));
    }
}

二、解决帧率低(20fps仅捕获8fps)的问题

核心原因

  1. 重复调用QueryFrame()导致丢帧
  2. UI更新用Invoke阻塞捕获线程
  3. 未配置RTSP缓冲和帧率参数,导致数据堆积

修复步骤

  1. 初始化capture时设置RTSP优化参数:
capture = new VideoCapture("rtsp://your-camera-url");
// 减少RTSP缓冲,降低延迟
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.BufferSize, 1);
// 强制捕获帧率与摄像头一致
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps, 20);
// 启用硬件加速(如果设备支持)
capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.HwAcceleration, (double)Emgu.CV.CvEnum.HwAccelerationType.Any);
  1. 用BeginInvoke替代Invoke更新UI(已在上述代码中实现)
  2. 改用线程循环捕获替代定时器,更稳定:
private Thread _captureThread;
private void StartCapture()
{
    _isCaptureActive = true;
    _captureThread = new Thread(() =>
    {
        while (_isCaptureActive && capture != null && capture.Ptr != IntPtr.Zero)
        {
            // 用Grab+Retrieve替代QueryFrame,更灵活可控
            if (capture.Grab())
            {
                using (var mat = new Mat())
                {
                    capture.Retrieve(mat);
                    using (Image<Bgr, byte> image = mat.ToImage<Bgr, byte>())
                    {
                        lock (captureLock)
                        {
                            // 录制和UI更新逻辑同之前
                        }
                    }
                }
            }
            // 避免CPU占用过高
            Thread.Sleep(1);
        }
    });
    _captureThread.IsBackground = true;
    _captureThread.Start();
}

内容的提问来源于stack exchange,提问作者DK kim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:09:53