Files

214 lines
7.6 KiB
C#
Raw Permalink Normal View History

using OpenCvSharp;
namespace SHH.CameraSdk;
/// <summary>
/// [零延迟核心] 智能帧(内存复用+引用计数)
/// 功能:封装 OpenCV Mat 实现物理内存复用,通过引用计数管理生命周期,避免 GC 频繁回收导致的性能抖动
/// 特性:引用归零自动回池,全程无内存分配/释放开销,支撑高帧率实时流处理
/// </summary>
public class SmartFrame : IDisposable
{
2026-01-17 07:55:21 +08:00
public ConcurrentQueue<string> SubscriberIds { get; } = new ConcurrentQueue<string>();
2025-12-29 08:09:14 +08:00
#region --- (Private Resources & States) ---
/// <summary> 所属帧池:用于引用归零后自动回收复用 </summary>
private readonly FramePool _pool;
/// <summary> 引用计数器:线程安全,控制帧的生命周期 </summary>
/// <remarks> 初始值 0激活后设为 1引用归零则自动回池 </remarks>
private int _refCount = 0;
#endregion
#region --- (Public Properties) ---
/// <summary> 帧数据物理内存载体OpenCV Mat 对象) </summary>
/// <remarks> 内存由帧池预分配,全程复用,不触发 GC </remarks>
public Mat InternalMat { get; private set; }
/// <summary> [快捷属性] 原始图像宽度 (若 TargetMat 为空则返回 0) </summary>
public int InternalWidth => InternalMat?.Width ?? 0;
/// <summary> [快捷属性] 原始图像高度 (若 TargetMat 为空则返回 0) </summary>
public int InnernalHeight => InternalMat?.Height ?? 0;
/// <summary> 帧激活时间戳(记录帧被取出池的时刻) </summary>
public DateTime Timestamp { get; private set; }
#endregion
2025-12-27 07:05:07 +08:00
#region --- (Derivative Properties) ---
/// <summary>
/// [衍生] 目标图像副本(处理后的图像)
/// <para>用途:存储经过缩放、增强后的图像,供 UI 预览或 Web 推流直接使用</para>
/// <para>生命周期:完全跟随 SmartFrame主帧销毁时自动释放</para>
/// </summary>
public Mat? TargetMat { get; private set; }
/// <summary> 变换类型(标记该 TargetMat 是被缩小了还是放大了) </summary>
public FrameScaleType ScaleType { get; private set; } = FrameScaleType.None;
/// <summary> [快捷属性] 目标图像宽度 (若 TargetMat 为空则返回 0) </summary>
public int TargetWidth => TargetMat?.Width ?? 0;
/// <summary> [快捷属性] 目标图像高度 (若 TargetMat 为空则返回 0) </summary>
public int TargetHeight => TargetMat?.Height ?? 0;
#endregion
#region --- (Constructor & Activation) ---
/// <summary>
/// 初始化智能帧(由帧池调用,外部禁止直接实例化)
/// </summary>
/// <param name="pool">所属帧池实例</param>
/// <param name="width">帧宽度</param>
/// <param name="height">帧高度</param>
/// <param name="type">帧数据类型(如 MatType.CV_8UC3</param>
internal SmartFrame(FramePool pool, int width, int height, MatType type)
{
_pool = pool;
// 预分配物理内存:内存块在帧池生命周期内复用,避免频繁申请/释放
InternalMat = new Mat(height, width, type);
2025-12-27 07:05:07 +08:00
// 确保激活时清理旧的衍生数据(防止脏数据残留)
ResetDerivatives();
}
2026-01-17 00:03:16 +08:00
private int _isReturned = 0; // 0: 激活中, 1: 已归还池
/// <summary>
/// [生产者调用] 从帧池取出时激活帧
/// 功能:初始化引用计数,标记激活时间戳
/// </summary>
public void Activate()
{
2026-01-17 13:13:17 +08:00
// Optimized: [原因] 使用 Exchange 强制重置归还标记,确保该帧在逻辑上完全从池中脱离,防止归还竞态
Interlocked.Exchange(ref _isReturned, 0);
// 激活后引用计数设为 1代表生产者驱动/管道)持有该帧
_refCount = 1;
2026-01-17 13:13:17 +08:00
// 记录帧被取出池的时间,用于后续延迟计算
Timestamp = DateTime.Now;
}
#endregion
2025-12-27 07:05:07 +08:00
#region --- (Derivatives Management) ---
/// <summary>
/// 挂载处理后的目标图像
/// </summary>
/// <param name="processedMat">已处理的 Mat所有权移交给 SmartFrame</param>
/// <param name="scaleType">变换类型(缩小/放大/无)</param>
public void AttachTarget(Mat processedMat, FrameScaleType scaleType)
{
// 防御性编程:如果之前已有 TargetMat先释放防止内存泄漏
if (TargetMat != null)
{
TargetMat.Dispose();
}
TargetMat = processedMat;
ScaleType = scaleType;
}
2026-01-17 07:55:21 +08:00
private readonly object _subscriberLock = new(); // 建议在类头部定义此锁
2025-12-27 07:05:07 +08:00
/// <summary>
/// 内部清理:释放衍生数据
/// </summary>
private void ResetDerivatives()
{
if (TargetMat != null)
{
TargetMat.Dispose();
TargetMat = null;
}
ScaleType = FrameScaleType.None;
2025-12-29 08:09:14 +08:00
2026-01-17 07:55:21 +08:00
// 2. [核心逻辑] 线程安全地清空订阅者列表
// 使用 lock 确保在 Clear 的瞬间,没有其他分发线程正在执行 Add 操作
lock (_subscriberLock)
{
// 注意Clear() 只是把 Count 设为 0底层数组容量 (Capacity) 不变
// 这样下次复用时SubscriberIds 不需要重新分配内存,完美符合零拷贝初衷
SubscriberIds.Clear();
}
2025-12-27 07:05:07 +08:00
}
#endregion
#region --- (Reference Count Management) ---
/// <summary>
/// [消费者调用] 增加引用计数
/// 适用场景:帧需要被多模块同时持有(如同时分发到 UI 和 AI 分析)
/// </summary>
public void AddRef()
{
// 原子递增:线程安全,避免多线程竞争导致计数错误
Interlocked.Increment(ref _refCount);
}
#endregion
#region --- (Disposal & Pool Return) ---
/// <summary>
/// [消费者调用] 释放引用计数
/// 核心逻辑:引用归零后自动将帧归还至帧池,实现内存复用
/// </summary>
public void Dispose()
{
// 原子递减:线程安全,确保计数准确
if (Interlocked.Decrement(ref _refCount) <= 0)
{
2026-01-17 00:03:16 +08:00
// 2. 关键:原子抢占归还权。只有成功将 _isReturned 从 0 变为 1 的线程才能执行归还逻辑。
if (Interlocked.CompareExchange(ref _isReturned, 1, 0) == 0)
{
// 3. 彻底清理衍生数据TargetMat 必须释放)
ResetDerivatives();
// 4. 安全归还到池中
_pool.Return(this);
}
}
}
#endregion
2026-01-17 15:41:55 +08:00
// 在 SmartFrame 类中添加此方法
/// <summary>
/// [哨兵专用] 强制重置帧状态
/// 用于自愈机制:当引用计数由于逻辑 Bug 永久无法归零时,由 FramePool 强行回收
/// </summary>
internal void ForceReset()
{
// 1. 强行将计数器和归还标记归零
Interlocked.Exchange(ref _refCount, 0);
Interlocked.Exchange(ref _isReturned, 1);
// 2. 清理衍生数据,防止内存堆积
ResetDerivatives();
// 3. 标记已被强制回收,便于日志追踪
IsForceRecycled = true;
}
// 记录帧被从池中取出的精确时间
public long BorrowedTick { get; private set; }
// 标记是否已被哨兵强制回收
internal bool IsForceRecycled { get; set; }
public void MarkBorrowed()
{
BorrowedTick = Environment.TickCount64;
IsForceRecycled = false;
}
}