mvdocs

# 持续相机采集与缓冲回收

目标设计与 API 草案,尚未实施。总体边界和推进状态见设计概述。

本文负责外借引用、SDK 地址稳定、同步复制、入口接管、满载、归还泵及预览解耦。实际适配器提供供应商协议证据。

关联:存储 · ToolFlow · 停止 · 观察

# 两种采集接入

采集会话可以持续运行,但一个节点调用/输出暂存批次必须有结束边界。连续相机不能把整段无限采集作为一次直到停流才结束的节点调用;每个采集结果或有限批次单独发布,或由源适配器直接进入有界入口。流式推送若另行设计,需要自己的逐项接管协议,不能沿用“整次调用成功后统一路由”却提前消费部分暂存项。

SDK 能力 接入方式 回调结束后
可显式持有帧,地址稳定,SDK 等待归还后再复用 ImportNative(ReadOnly) 并由一次 releaseAsync 归还 在途图像保持那一份 SDK 引用
像素只在回调期间有效,或 SDK 无法保证后续稳定 回调内同步 Copy 到受控自有存储 原 SDK 缓冲立即按其协议归还

ReadOnly 不能约束外部 DMA 或 SDK。适配器必须确认采集写入及设备同步已经完成,并保证该 SDK 引用存活期间不覆盖数据;不能用 C# 引用计数掩盖 SDK 会提前复用的事实。无法提供这项保证时选择复制。停止采集或设备断开若可能使已持有地址失效,也必须按供应商协议提前脱离或采用复制模式,不能承诺软件锁能挽救失效地址。

SDK 的“持有一帧”只与一次底层协调实体对应,图像 Share 不重复 AddRef SDK。最后归还时 Release 一次。每次缓冲进入新的外借周期取得新 BufferId,即使物理地址与上次相同;在同一外借周期只建立一个协调实体,禁止重复 ImportNative 同一地址制造不相干的锁。

Copy 也不是无限制分配许可。采集适配器在复制前取得自有池和入口预算,不能先拷贝再发现队列已无容量。回调不能阻塞等待 UI、算法结束或同一 SDK 线程执行归还;使用 TryPostOwned 或有明确时限的 SDK 专用接入策略。

# 入口接管和拒绝的最短示例

以下 image 已由适配器创建为稳定 ReadOnly 图像,且正好拥有一个待交接 handle:

var accepted = false;
try
{
    accepted = input.TryPostOwned(image);
}
finally
{
    if (!accepted)
    {
        image.Dispose();
    }
}

TryPostOwned 必须保证抛错时未接管。相机导入前的失败仍由适配器归还 SDK;导入成功后只能通过图像关闭触发归还,不能再在外层 finally 无条件调用 SDK Release。CPU Copy 成功但入队失败时也关闭复制图像。

# SDK 池、归还泵与预览解耦

SDK 池的可用缓冲要覆盖采集占用、不同在途原图、消费者保留和正在归还的引用,并留采集余量。相同原图的多个 Share 不多占 SDK 池槽,但会延长该槽占用时间。小 ROI 同样可能占用完整相机大缓冲。用长期观察到的在途数量与持有时长确定容量,不能仅按显示帧率或节点数估算。

releaseAsync 在清理队列执行后可能还要投递 SDK 专属线程。适配器需要一个贯穿整个采集会话的归还泵;任务计数包含排队归还和实际回调执行,ReturnCompleted 只在 SDK 确认归还后完成。归还队列也必须有界或在采集入口预留归还容量,不能在所有引用都关闭后又积累无界清理工作。归还失败是会话故障,不能吞掉或重复调用同一个 Release。

预览使用一个有所有权的 Latest 槽,再由 UI 定时/合并取走最新图像,不能为每次采集无限 Post 一个带图像引用的 dispatcher 回调。被替换项立即关闭;槽位送入 ImageDisplay 后,其自有 handle 也按接管规则关闭。实时相机预览适配默认选 Snapshot,使 UI 暂停时最多保留一个受预算控制的显示副本;控件通用默认 RetainInput 仍适用于显式共享场景。

历史查看和长时间重试不能无限保留 SDK 槽位。宿主按保留时限把需要长期保存的内容复制到受配额约束的独立存储,再关闭 SDK 原图;历史副本同样有数量/字节上限。ROI 缓存也遵循此规则,不因逻辑图像小就忽略其牵连的 SDK 成本。

低拷贝指采集原图到算法可共享,并不承诺显示上传、颜色转换或历史保存都零拷贝。若可靠记录或慢算法超过采集速率,需调整采集速率、拒绝/丢弃策略或提供更多受控存储;不存在无限流量、零丢帧、有限内存和任意慢消费者同时成立的配置。