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

Canon EDSDK多次使用/快速切换LiveView后不再触发OnLiveViewUpdated问题

问题场景

基于.NET 8.0的WPF应用,通过Canon EDSDK实现相机LiveView预览,用于短视频录制全流程的状态查看。单次运行正常,但频繁切换LiveView开关或多次启停后,LiveView显示已启动,但相机停止推送图像(录制功能不受影响,视频可正常保存)。排查到两个核心异常:

  • EOSDigital.API.SDKException SEVERE ERROR : NOT_SUPPORTED
  • System.PlatformNotSupportedException

临时方案是3秒后检测状态并重启相机会话,但属于治标不治本,需要彻底修复。

现有代码的潜在问题

1. 资源泄漏引发平台异常

  • System.Drawing.Image.FromStream(img)未用using包裹,长期频繁调用会导致GDI+资源泄漏,触发PlatformNotSupportedException
  • MemoryStream手动调用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;
    }
}
关键修复点说明
  1. 资源自动释放:所有IDisposable资源用using声明,彻底解决GDI+资源泄漏问题
  2. 事件订阅管理:重置会话前取消旧订阅,避免重复回调与内存泄漏
  3. 原子操作控制:切换LiveView时增加操作锁,防止快速连续操作引发SDK异常
  4. 可靠看门狗机制:用CancellationTokenSource管理检测任务,支持主动取消,避免无效重置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 18:08:13