From a27045e0a02aaf8b509f04b6647040a9ba7a9909 Mon Sep 17 00:00:00 2001 From: wilson Date: Sat, 17 Jan 2026 07:55:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SHH.CameraSdk/Core/Memory/SmartFrame.cs | 15 +++++++++++---- .../Drivers/HikVision/HikVideoSource.cs | 16 +++++++--------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/SHH.CameraSdk/Core/Memory/SmartFrame.cs b/SHH.CameraSdk/Core/Memory/SmartFrame.cs index 81c50ee..06730cd 100644 --- a/SHH.CameraSdk/Core/Memory/SmartFrame.cs +++ b/SHH.CameraSdk/Core/Memory/SmartFrame.cs @@ -9,7 +9,7 @@ namespace SHH.CameraSdk; /// public class SmartFrame : IDisposable { - public List SubscriberIds { get; } = new List(16); + public ConcurrentQueue SubscriberIds { get; } = new ConcurrentQueue(); #region --- 私有资源与状态 (Private Resources & States) --- @@ -114,6 +114,8 @@ public class SmartFrame : IDisposable ScaleType = scaleType; } + private readonly object _subscriberLock = new(); // 建议在类头部定义此锁 + /// /// 内部清理:释放衍生数据 /// @@ -126,9 +128,14 @@ public class SmartFrame : IDisposable } ScaleType = FrameScaleType.None; - // 2. [核心逻辑] 清空订阅者列表 - // 注意:Clear() 只是把 Count 设为 0,底层数组容量不变,不会触发 GC - SubscriberIds.Clear(); + // 2. [核心逻辑] 线程安全地清空订阅者列表 + // 使用 lock 确保在 Clear 的瞬间,没有其他分发线程正在执行 Add 操作 + lock (_subscriberLock) + { + // 注意:Clear() 只是把 Count 设为 0,底层数组容量 (Capacity) 不变 + // 这样下次复用时,SubscriberIds 不需要重新分配内存,完美符合零拷贝初衷 + SubscriberIds.Clear(); + } } #endregion diff --git a/SHH.CameraSdk/Drivers/HikVision/HikVideoSource.cs b/SHH.CameraSdk/Drivers/HikVision/HikVideoSource.cs index 73bb336..986bbf9 100644 --- a/SHH.CameraSdk/Drivers/HikVision/HikVideoSource.cs +++ b/SHH.CameraSdk/Drivers/HikVision/HikVideoSource.cs @@ -158,7 +158,7 @@ public class HikVideoSource : BaseVideoSource, throw new CameraException(HikErrorMapper.Map(err), $"预览失败: {err}", DeviceBrand.HikVision, (int)err); } - _sdkLog.Debug($"[SDK] Hik 网络取流成功 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}"); + _sdkLog.Information($"[SDK] Hik 网络取流成功 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}"); AddAuditLog($"[SDK] Hik 网络取流成功 => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}"); } catch (Exception ex) @@ -506,7 +506,8 @@ public class HikVideoSource : BaseVideoSource, Cv2.CvtColor(rawYuvWrapper, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_YV12); } - smartFrame.SubscriberIds.AddRange(decision.TargetAppIds); + foreach(var targetAppId in decision.TargetAppIds) + smartFrame.SubscriberIds.Enqueue(targetAppId); // ========================================================================= // 【修正】删除这里的 GlobalStreamDispatcher.Dispatch! @@ -530,13 +531,10 @@ public class HikVideoSource : BaseVideoSource, } finally { - // 【核心修复】 - // 只有当分发失败(异常)时,驱动层才负责回收。 - // 一旦分发成功,所有权属于 GlobalProcessingCenter,驱动层严禁 Dispose。 - if (!handoverSuccess && smartFrame != null) - { - smartFrame.Dispose(); - } + // Optimized: [原因] 驱动层必须释放它持有的初始引用。 + // 如果 Dispatch 内部已经 AddRef,此处 Dispose 只会让计数从 2 降到 1,帧不会回池。 + // 如果没有其他人持有,此处 Dispose 会让计数从 1 降到 0,帧安全回池。 + smartFrame?.Dispose(); } }