使用Emgu.CV开发RTSP播放器遇AccessViolationException及帧率问题求助
问题解决方案
一、解决释放时的AccessViolationException
核心原因
UpdateFrame方法由定时器/后台线程周期性触发,释放capture时线程仍在尝试访问已释放的资源,引发内存非法访问。同时代码存在**重复调用QueryFrame()**的问题,既丢帧又加剧线程冲突风险。
修复步骤
- 新增线程同步标识,控制捕获逻辑的执行
- 调整锁范围,避免无效同步
- 释放
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)的问题
核心原因
- 重复调用
QueryFrame()导致丢帧 - UI更新用
Invoke阻塞捕获线程 - 未配置RTSP缓冲和帧率参数,导致数据堆积
修复步骤
- 初始化
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);
- 用
BeginInvoke替代Invoke更新UI(已在上述代码中实现) - 改用线程循环捕获替代定时器,更稳定:
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
相关产品推荐
相关产品推荐

