Canon EDSDK多次使用/快速切换LiveView后不再触发OnLiveViewUpdated问题
问题场景
基于.NET 8.0的WPF应用,通过Canon EDSDK实现相机LiveView预览,用于短视频录制全流程的状态查看。单次运行正常,但频繁切换LiveView开关或多次启停后,LiveView显示已启动,但相机停止推送图像(录制功能不受影响,视频可正常保存)。排查到两个核心异常:
EOSDigital.API.SDKExceptionSEVERE ERROR : NOT_SUPPORTEDSystem.PlatformNotSupportedException
临时方案是3秒后检测状态并重启相机会话,但属于治标不治本,需要彻底修复。
现有代码的潜在问题
1. 资源泄漏引发平台异常
System.Drawing.Image.FromStream(img)未用using包裹,长期频繁调用会导致GDI+资源泄漏,触发PlatformNotSupportedExceptionMemoryStream手动调用Dispose(),未利用using自动释放机制,存在内存泄漏风险- 未先验证触发事件的相机状态,仍可能处理无效流操作
2. 线程与事件管理不规范
- 临时方案中
Task.Factory.StartNew结合Thread.Sleep的检测逻辑不可靠,状态判断时机不准确 - 重置会话时未取消旧事件订阅,导致旧实例回调仍被触发,引发资源浪费与状态混乱
Dispatcher.BeginInvoke未指定合适优先级,高频率画面更新可能阻塞UI线程
3. 相机状态同步缺失
快速切换LiveView时,SDK内部状态未完成同步就执行启停操作,导致SDK返回NOT_SUPPORTED异常
彻底修复方案
1. 优化LiveView回调的资源管理
修正回调逻辑,确保所有资源自动释放,避免GDI+泄漏:
private void CamSession_OnLiveViewUpdated(Camera sender, Stream img) { try { // 优先验证当前相机的LiveView状态,避免无效处理 if (!sender.IsLiveViewOn) return; // 所有IDisposable资源用using自动释放 using var sourceImage = System.Drawing.Image.FromStream(img); using var bitmap = new System.Drawing.Bitmap(sourceImage); using var ms = new MemoryStream(); bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); ms.Seek(0, SeekOrigin.Begin); var bi = new BitmapImage(); bi.BeginInit(); bi.StreamSource = ms; bi.CacheOption = BitmapCacheOption.OnLoad; bi.EndInit(); bi.Freeze(); // 冻结后可安全跨线程访问 // 用Render优先级保证画面更新流畅,InvokeAsync支持异步等待 _ = LVCanvas.Dispatcher.InvokeAsync(() => { LVCanvas.Background = new ImageBrush(bi); }, DispatcherPriority.Render); } catch (EOSDigital.API.SDKException ex) { Debug.WriteLine($"SDK错误: {ex.Message}"); HandleLiveViewFailure(); } catch (PlatformNotSupportedException ex) { Debug.WriteLine($"平台不支持错误: {ex.Message}"); HandleLiveViewFailure(); } catch (Exception ex) { Debug.WriteLine($"意外错误: {ex.Message}"); } }
2. 规范相机会话生命周期管理
封装会话初始化、清理逻辑,避免重复代码与资源泄漏:
private CameraSession _camSession; private CancellationTokenSource _lvWatchDogCts; public PriseVideo(Utilisateur inv) { InitializeComponent(); InitializeCameraSession(); } private void InitializeCameraSession() { // 清理旧会话资源 CleanupCameraSession(); // 创建新会话并订阅事件 _camSession = new CameraSession(true); _camSession.OnLiveViewUpdated += CamSession_OnLiveViewUpdated; // 启动LiveView与看门狗检测 _camSession.startLV(); StartLiveViewWatchDog(); } private void CleanupCameraSession() { // 取消并释放看门狗任务 _lvWatchDogCts?.Cancel(); _lvWatchDogCts?.Dispose(); if (_camSession == null) return; // 取消事件订阅,避免内存泄漏 _camSession.OnLiveViewUpdated -= CamSession_OnLiveViewUpdated; // 安全停止LiveView并释放相机资源 try { if (_camSession.mainCamera?.IsLiveViewOn ?? false) { _camSession.mainCamera.StopLiveView(); } _camSession.mainCamera?.Dispose(); } catch { } _camSession = null; } private void StartLiveViewWatchDog() { _lvWatchDogCts = new CancellationTokenSource(); var token = _lvWatchDogCts.Token; _ = Task.Run(async () => { await Task.Delay(3000, token); if (token.IsCancellationRequested) return; // 检测LiveView状态,可扩展为检测最后画面更新时间 if (!(_camSession?.mainCamera?.IsLiveViewOn ?? false)) { await Dispatcher.InvokeAsync(() => InitializeCameraSession()); } }, token); } private void HandleLiveViewFailure() { // 在UI线程执行会话重置 _ = Dispatcher.InvokeAsync(() => InitializeCameraSession()); }
3. 优化LiveView切换逻辑
增加操作锁与状态同步延迟,避免快速连续操作引发SDK异常:
private bool _isLvOperationInProgress; public void ToggleLiveView() { if (_isLvOperationInProgress || _camSession?.mainCamera == null) return; _isLvOperationInProgress = true; try { if (_camSession.mainCamera.IsLiveViewOn) { _camSession.mainCamera.StopLiveView(); } else { _camSession.mainCamera.StartLiveView(); } // 等待SDK内部状态同步 Task.Delay(500).Wait(); } catch (EOSDigital.API.SDKException ex) { Debug.WriteLine($"切换LiveView错误: {ex.Message}"); HandleLiveViewFailure(); } finally { _isLvOperationInProgress = false; } }
关键修复点说明
- 资源自动释放:所有
IDisposable资源用using声明,彻底解决GDI+资源泄漏问题 - 事件订阅管理:重置会话前取消旧订阅,避免重复回调与内存泄漏
- 原子操作控制:切换LiveView时增加操作锁,防止快速连续操作引发SDK异常
- 可靠看门狗机制:用
CancellationTokenSource管理检测任务,支持主动取消,避免无效重置
内容的提问来源于stack exchange,提问作者thomas
相关产品推荐
相关产品推荐

