海康摄像头取流示例初始签入

This commit is contained in:
2025-12-26 03:18:21 +08:00
parent 86db2cf6b4
commit 6281f4248e
44 changed files with 5588 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace SHH.CameraSdk;
/// <summary>
/// 通道级能力描述(镜头身份证)
/// 核心职责:描述单个物理镜头或 NVR 通道的技术参数、功能支持特性与分辨率能力
/// 协作场景:作为 <see cref="DeviceMetadata"/> 的子级数据,支撑设备能力自发现、配置合法性校验
/// </summary>
public class ChannelMetadata
{
#region --- 1. (Basic Identification) ---
/// <summary>
/// 物理通道索引(从 1 开始计数,与 DVR/NVR 物理通道号一一对应)
/// 业务用途:作为通道唯一标识,用于码流订阅、参数配置
/// </summary>
public int ChannelIndex { get; init; }
/// <summary>
/// 通道名称(用于 OSD 叠加显示、UI 界面展示)
/// 示例:"北大门主入口" "地下车库A区"
/// </summary>
public string Name { get; init; } = string.Empty;
#endregion
#region --- 2. (Capability Support) ---
/// <summary>
/// 是否支持云台控制PTZPan/Tilt/Zoom 平移/俯仰/变焦)
/// 业务影响:决定 UI 是否显示云台控制按钮,是否允许下发 PTZ 指令
/// </summary>
public bool SupportPtz { get; init; }
/// <summary>
/// 是否支持音频输入(是否接入拾音器)
/// 业务影响:决定是否开启音频解码、音频流推送功能
/// </summary>
public bool SupportAudioIn { get; init; }
/// <summary>
/// 是否支持 AI 智能分析(人脸检测、车牌识别、行为分析等)
/// 业务影响:决定是否加载 AI 算法插件,是否接收智能事件上报
/// </summary>
public bool SupportAiAnalysis { get; init; }
#endregion
#region --- 3. (Resolution Capabilities) ---
/// <summary>
/// 通道支持的分辨率列表(只读集合,防止外部篡改)
/// 格式示例:["1920x1080", "1280x720", "3840x2160"]
/// 业务用途:前端清晰度选择下拉列表、动态配置分辨率合法性校验
/// </summary>
public ReadOnlyCollection<string> SupportedResolutions { get; init; } = new ReadOnlyCollection<string>(new List<string>());
#endregion
#region --- 4. (Constructors) ---
/// <summary>
/// 默认构造函数(用于序列化、初始状态初始化)
/// </summary>
public ChannelMetadata() { }
/// <summary>
/// 简化构造函数(用于快速创建通道标识、克隆或比对场景)
/// </summary>
/// <param name="index">物理通道索引</param>
/// <param name="name">通道名称</param>
public ChannelMetadata(int index, string name)
{
ChannelIndex = index;
Name = name;
}
/// <summary>
/// 完整构造函数(用于创建包含全量能力的通道元数据)
/// </summary>
/// <param name="index">物理通道索引</param>
/// <param name="name">通道名称</param>
/// <param name="supportPtz">是否支持云台</param>
/// <param name="supportAudio">是否支持音频输入</param>
/// <param name="supportAi">是否支持 AI 分析</param>
/// <param name="resolutions">支持的分辨率列表</param>
public ChannelMetadata(
int index,
string name,
bool supportPtz = false,
bool supportAudio = false,
bool supportAi = false,
IEnumerable<string>? resolutions = null)
{
ChannelIndex = index;
Name = name;
SupportPtz = supportPtz;
SupportAudioIn = supportAudio;
SupportAiAnalysis = supportAi;
SupportedResolutions = new ReadOnlyCollection<string>(resolutions?.ToList() ?? new List<string>());
}
#endregion
}

View File

@@ -0,0 +1,176 @@
namespace SHH.CameraSdk;
/// <summary>
/// 增强型设备元数据中心 (V3.3.1 修复版)
/// 核心职责:
/// <para>1. 封装设备的硬件参数、通道能力、功能集,提供能力自发现</para>
/// <para>2. 支持元数据同步与差异对比,指导上层模块执行差异化处理</para>
/// <para>3. 存储运维指标与 SDK 原生句柄,支撑故障诊断与性能调优</para>
/// 设计特性:只读优先,通过版本号标记同步状态,避免并发修改冲突
/// </summary>
public class DeviceMetadata
{
#region --- 1. (Identity) ---
/// <summary> 设备型号名称(如 DS-2CD3T47G2-LIU </summary>
public string ModelName { get; init; } = "Unknown";
/// <summary> 设备唯一序列号(全局唯一,用于设备溯源) </summary>
public string SerialNumber { get; init; } = string.Empty;
/// <summary> 固件/系统版本号(用于判断 SDK 兼容性) </summary>
public string FirmwareVersion { get; init; } = string.Empty;
/// <summary> 所属厂商/品牌(决定驱动适配逻辑) </summary>
public DeviceBrand Brand { get; init; } = DeviceBrand.Unknown;
/// <summary> 元数据版本号(本地刷新计数,每次同步自增) </summary>
public long Version { get; private set; }
/// <summary> 最后同步时间(标记元数据的最新有效时刻) </summary>
public DateTime LastSyncedAt { get; private set; }
#endregion
#region --- 2. (Cascaded Capabilities) ---
private readonly ReadOnlyCollection<ChannelMetadata> _channels;
/// <summary> 通道元数据集合(只读,防止外部篡改) </summary>
public ReadOnlyCollection<ChannelMetadata> Channels
{
get => _channels;
init => _channels = value ?? new ReadOnlyCollection<ChannelMetadata>(new List<ChannelMetadata>());
}
/// <summary> 设备总通道数量IPC 通常为 1NVR 为接入路数) </summary>
public int ChannelCount => Channels.Count;
/// <summary> 索引器:通过通道号快速获取对应通道的能力描述 </summary>
/// <param name="channelIndex">物理通道号</param>
/// <returns>通道元数据 / 不存在则返回 null</returns>
public ChannelMetadata? this[int channelIndex] =>
Channels.FirstOrDefault(c => c.ChannelIndex == channelIndex);
#endregion
#region --- 3. ---
/// <summary> 设备实时健康度字典(如 CPU 使用率、温度、内存占用等) </summary>
public Dictionary<string, object> HealthMetrics { get; init; } = new();
/// <summary>
/// 厂商 SDK 原始句柄/结构体快照
/// <para>注意事项:</para>
/// <para>1. 标记 [JsonIgnore] 防止序列化非托管指针导致程序崩溃</para>
/// <para>2. 仅用于驱动层与 SDK 交互,上层业务禁止直接操作</para>
/// </summary>
[JsonIgnore]
public object? NativeHandle { get; init; }
/// <summary> 厂商扩展标签(如设备位置、安装时间、责任人等自定义信息) </summary>
public Dictionary<string, string> Tags { get; init; } = new();
#endregion
#region --- 4. (Constructor & Sync) ---
/// <summary>
/// 默认构造函数(用于 BaseVideoSource 初始状态,无通道数据)
/// </summary>
public DeviceMetadata() : this(Enumerable.Empty<ChannelMetadata>()) { }
/// <summary>
/// 完整构造函数(初始化通道元数据集合)
/// </summary>
/// <param name="channels">通道元数据列表</param>
public DeviceMetadata(IEnumerable<ChannelMetadata> channels)
{
// 转换为只读集合,确保通道数据不可变
_channels = new ReadOnlyCollection<ChannelMetadata>(channels?.ToList() ?? new List<ChannelMetadata>());
// 标记初始同步状态
MarkSynced();
}
/// <summary>
/// 标记元数据同步完成
/// <para>作用:更新版本号与同步时间,用于差异对比的基准判断</para>
/// </summary>
public void MarkSynced()
{
Version++;
LastSyncedAt = DateTime.Now;
}
#endregion
#region --- 5. (Business Helpers) ---
/// <summary>
/// 校验动态流配置的合法性(基于设备能力)
/// </summary>
/// <param name="options">待校验的动态配置项</param>
/// <param name="errorMessage">校验失败时的详细原因</param>
/// <returns>合法返回 true非法返回 false</returns>
public bool ValidateOptions(DynamicStreamOptions options, out string errorMessage)
{
errorMessage = string.Empty;
if (options == null) return true;
// 示例校验规则:云台控制权限校验
if (options.VendorExtensions?.ContainsKey("PtzAction") == true
&& !Channels.Any(c => c.SupportPtz))
{
errorMessage = "该设备物理硬件不支持云台控制功能";
return false;
}
// 可扩展其他校验规则:如分辨率合法性、码流类型支持性等
return true;
}
/// <summary>
/// 元数据差异比对逻辑(用于 BaseVideoSource.RefreshMetadataAsync 方法)
/// </summary>
/// <param name="other">最新拉取的设备元数据</param>
/// <returns>元数据差异描述符</returns>
public MetadataDiff CompareWith(DeviceMetadata other)
{
// 入参防护:对比对象为空则返回无差异
if (other == null) return MetadataDiff.None;
return new MetadataDiff
{
// 1. 基础信息变更:任意通道名称变化则标记
NameChanged = this.Channels.Any(c =>
{
var targetChannel = other[c.ChannelIndex];
return targetChannel != null && targetChannel.Name != c.Name;
}),
// 2. 能力集变更PTZ/音频/AI 功能支持状态变化则标记
CapabilityChanged = this.Channels.Any(c =>
{
var targetChannel = other[c.ChannelIndex];
if (targetChannel == null) return false;
return targetChannel.SupportPtz != c.SupportPtz
|| targetChannel.SupportAudioIn != c.SupportAudioIn
|| targetChannel.SupportAiAnalysis != c.SupportAiAnalysis;
}),
// 3. 致命变更:品牌不一致或通道数量变化(设备更换/替换场景)
IsMajorChange = this.Brand != other.Brand || this.ChannelCount != other.ChannelCount,
// 4. 分辨率配置变更:任意通道的分辨率档位数量变化则标记
ResolutionProfilesChanged = this.Channels.Any(c =>
{
var targetChannel = other[c.ChannelIndex];
return targetChannel != null
&& targetChannel.SupportedResolutions.Count != c.SupportedResolutions.Count;
})
};
}
#endregion
}

View File

@@ -0,0 +1,120 @@
namespace SHH.CameraSdk;
/// <summary>
/// 视频流动态配置项(运行时可调整参数容器)
/// 核心职责:承载无需重启流即可动态调整的视频参数,支持局部更新
/// 核心特性:
/// <para>1. Nullable 模式:仅非空字段会触发参数更新,避免全量重置导致的性能抖动</para>
/// <para>2. 分类管理:按画面策略、帧率控制、传输输出、厂商扩展划分参数,逻辑清晰</para>
/// <para>3. 空值检查:通过 IsEmpty 判断是否存在有效配置,避免无效底层 SDK 调用</para>
/// </summary>
public class DynamicStreamOptions
{
#region --- 1. (Resolution & Scaling) ---
/// <summary>
/// 目标输出宽度(像素)
/// <para>Nullable 规则null = 保持当前配置;非 null = 触发图像缩放逻辑</para>
/// <para>注意事项:建议与 TargetHeight 成对设置,避免画面比例失衡</para>
/// </summary>
public int? TargetWidth { get; set; }
/// <summary>
/// 目标输出高度(像素)
/// <para>Nullable 规则null = 保持当前配置;非 null = 触发图像缩放逻辑</para>
/// <para>协作关系:与 TargetWidth 配合使用,若仅设置其一,会按原始宽高比自动计算另一值</para>
/// </summary>
public int? TargetHeight { get; set; }
/// <summary>
/// 图像放大开关
/// <para>Nullable 规则null = 保持当前策略true = 允许放大false = 禁止放大</para>
/// <para>性能影响:禁止放大可节省插值计算资源,适合低性能设备</para>
/// </summary>
public bool? AllowEnlarge { get; set; }
/// <summary>
/// 图像缩小开关
/// <para>Nullable 规则null = 保持当前策略true = 允许缩小false = 禁止缩小</para>
/// <para>适用场景:禁止缩小可保留原始画质,适合需要高清分析的场景</para>
/// </summary>
public bool? AllowShrink { get; set; }
#endregion
#region --- 2. (Frame Rate Control) ---
/// <summary>
/// 目标渲染/显示帧率fps
/// <para>Nullable 规则null = 不修改0 = 跟随原始流速度;非 0 = 强制限定显示帧率</para>
/// <para>作用域:仅影响 UI 预览层,不会改变底层码流的采集帧率</para>
/// </summary>
public int? TargetDisplayFps { get; set; }
/// <summary>
/// 目标 AI 分析帧率fps
/// <para>Nullable 规则null = 不修改;非 null = 限定算法处理的输入帧率</para>
/// <para>性能优化:降低此值可显著减少高分辨率下的 GPU/CPU 负荷(如 4K 从 30fps 降到 5fps</para>
/// </summary>
public int? TargetAnalyzeFps { get; set; }
#endregion
#region --- 3. (Transmission & Output) ---
/// <summary>
/// Web 推流开关
/// <para>Nullable 规则null = 保持当前状态true = 启动推流false = 停止推流</para>
/// <para>协作组件:开启后会将处理后的视频帧推送到流媒体服务器(如 FFmpeg/RTSP 服务器)</para>
/// </summary>
public bool? EnableStreamOutput { get; set; }
/// <summary>
/// 渲染窗体句柄
/// <para>Nullable 规则null = 保持当前窗口;非 null = 切换到新窗口渲染</para>
/// <para>适用场景:支持视频窗口拖拽、多显示器切换等交互操作</para>
/// </summary>
public IntPtr? RenderHandle { get; set; }
/// <summary>
/// 码流类型切换
/// <para>取值规则0 = 主码流(高清/大带宽)1 = 子码流(标清/低延迟)2 = 第三码流</para>
/// <para>Nullable 规则null = 不切换;非 null = 执行码流切换</para>
/// <para>注意事项:切换会销毁并重建预览句柄,可能导致短暂的画面中断</para>
/// </summary>
public int? StreamType { get; set; }
#endregion
#region --- 4. (Vendor Specific) ---
/// <summary>
/// 厂商特有参数扩展字典
/// <para>用途:存放无法标准化的品牌专属功能参数</para>
/// <para>示例:海康 "FocusMode"=Auto/Manual大华 "SmartH264"=true/false</para>
/// <para>注意事项:键值对需与对应厂商 SDK 的参数名一致,否则无效</para>
/// </summary>
public Dictionary<string, object> VendorExtensions { get; set; } = new();
#endregion
#region --- 5. ---
/// <summary>
/// 逻辑空检查:判断当前配置包是否包含任何有效修改项
/// <para>使用场景:调用 SDK 前判断,避免无意义的底层调用,提升性能</para>
/// </summary>
public bool IsEmpty =>
TargetWidth is null &&
TargetHeight is null &&
AllowEnlarge is null &&
AllowShrink is null &&
TargetDisplayFps is null &&
TargetAnalyzeFps is null &&
EnableStreamOutput is null &&
RenderHandle is null &&
StreamType is null &&
VendorExtensions.Count == 0;
#endregion
}

View File

@@ -0,0 +1,53 @@
namespace SHH.CameraSdk;
/// <summary>
/// 元数据变更差异描述符(只读结构体)
/// 核心职责:对比设备当前运行元数据与最新拉取元数据的差异,明确变更类型及业务影响
/// 协作场景指导上层模块执行差异化处理如仅刷新UI、重启流、调整功能按钮
/// </summary>
public readonly struct MetadataDiff
{
#region --- (Change Type Properties) ---
/// <summary>
/// 设备基础描述信息变更(如名称、位置)
/// 业务影响:仅需刷新 UI 显示文字,无需中断当前流
/// </summary>
public bool NameChanged { get; init; }
/// <summary>
/// 设备能力集变更(如新增/移除对讲、截图功能)
/// 业务影响:需调整 UI 功能按钮的可用性,无需中断流
/// </summary>
public bool CapabilityChanged { get; init; }
/// <summary>
/// 致命/破坏性变更(如设备型号、编码格式变更)
/// 业务影响:必须销毁当前流实例并重启,否则会导致流异常或崩溃
/// </summary>
public bool IsMajorChange { get; init; }
/// <summary>
/// 分辨率/帧率档位列表变更
/// 业务影响:需重新校验当前流参数是否合法,非法则自动降级到可用档位
/// </summary>
public bool ResolutionProfilesChanged { get; init; }
/// <summary>
/// 全局变更标识:是否存在任何类型的元数据变更
/// 业务用途:快速判断是否需要执行后续差异化处理逻辑
/// </summary>
public bool HasChanges => NameChanged || CapabilityChanged || IsMajorChange || ResolutionProfilesChanged;
#endregion
#region --- (Quick Instances) ---
/// <summary>
/// 无变更状态的快捷实例
/// 业务用途:元数据刷新后无变化时直接返回,避免重复创建空对象
/// </summary>
public static MetadataDiff None => new();
#endregion
}

View File

@@ -0,0 +1,57 @@
namespace SHH.CameraSdk;
/// <summary>
/// 视频分辨率档位描述符Record 类型,不可变对象)
/// 核心职责:定义相机通道在特定编码格式下支持的分辨率、帧率上限及友好描述
/// 协作场景:
/// <para>1. 前端界面:展示清晰度选择下拉列表</para>
/// <para>2. 配置校验:下发分辨率前判断是否符合硬件能力,防止超限黑屏</para>
/// <para>3. 性能评估:通过总像素量计算编码/解码的计算负载与带宽压力</para>
/// </summary>
/// <param name="Width">画面像素宽度(如 1920、3840</param>
/// <param name="Height">画面像素高度(如 1080、2160</param>
/// <param name="MaxFps">该分辨率下硬件支持的最大输出帧率(防止超限配置)</param>
/// <param name="Description">档位友好描述(如 "高清(1080P/H.265)"</param>
public record ResolutionProfile(
int Width,
int Height,
int MaxFps,
string Description
)
{
#region --- (Derived Properties) ---
/// <summary>
/// 当前档位的总像素量
/// 业务用途:衡量编码/解码的计算负载、网络传输的带宽压力
/// 计算公式Width * Height
/// </summary>
public long TotalPixels => (long)Width * Height;
/// <summary>
/// 当前档位的屏幕宽高比
/// 业务用途前端渲染容器WinForm/WPF/Web自动调整比例避免画面拉伸变形
/// 保护逻辑:高度为 0 时返回 0防止除零异常
/// </summary>
public double AspectRatio => Height == 0 ? 0 : (double)Width / Height;
/// <summary>
/// 判断当前档位是否属于高清范畴行业标准720P 及以上,即 1280x720 分辨率)
/// 业务用途:前端分类展示、带宽策略选择
/// </summary>
public bool IsHighDefinition => Width >= 1280 && Height >= 720;
#endregion
#region --- (Overridden Methods) ---
/// <summary>
/// 格式化分辨率档位的显示文本
/// 输出格式:友好描述 (宽x高@最大帧率fps)
/// 示例:高清(1080P/H.265) (1920x1080@30fps)
/// </summary>
/// <returns>格式化的显示字符串</returns>
public override string ToString() => $"{Description} ({Width}x{Height}@{MaxFps}fps)";
#endregion
}

View File

@@ -0,0 +1,80 @@
namespace SHH.CameraSdk;
/// <summary>
/// 视频源状态变更事件参数
/// 核心职责:封装状态迁移的完整上下文信息,支撑三大业务场景
/// <para>1. UI 层:实时反馈设备状态、显示错误提示</para>
/// <para>2. 诊断层记录异常堆栈、SDK 错误码,辅助问题定位</para>
/// <para>3. 运维层:触发自动重连、告警推送等自动化决策</para>
/// </summary>
public class StatusChangedEventArgs : EventArgs
{
#region --- (Event Core Properties) ---
/// <summary>
/// 变更后的目标状态
/// 业务用途:
/// 1. UI 层:控制状态图标颜色(如 Playing→绿色、Faulted→红色
/// 2. 运维层:状态为 Reconnecting 时触发重连策略调整
/// </summary>
public VideoSourceStatus NewStatus { get; }
/// <summary>
/// 状态描述文本(可读)
/// 业务用途:
/// 1. UI 层:直接显示在状态栏或操作日志面板
/// 2. 日志层:写入业务日志,便于人工排查
/// </summary>
public string Message { get; }
/// <summary>
/// 关联的异常对象(可选)
/// 业务用途:仅当 NewStatus = Faulted 时有效,提供异常堆栈、类型等代码级诊断信息
/// </summary>
public Exception? Exception { get; }
/// <summary>
/// SDK 底层原始错误码(可选)
/// 业务用途:
/// 1. 厂商适配:匹配海康 NET_DVR_GetLastError、大华 SDK 错误码等
/// 2. 精准诊断:区分“用户锁定(153)”“密码错误(41)”“网络超时”等根因
/// </summary>
public int? LastErrorCode { get; }
/// <summary>
/// 变更后的新句柄(可选)
/// 业务用途:渲染器解绑/重绑场景,监听此值更新窗口句柄绑定关系
/// </summary>
public IntPtr? NewHandle { get; init; }
/// <summary>
/// 状态变更发生的时间戳
/// 业务用途:日志时序排序、状态迁移耗时统计
/// </summary>
public DateTime Timestamp { get; } = DateTime.Now;
#endregion
#region --- (Constructor) ---
/// <summary>
/// 初始化状态变更事件参数
/// </summary>
/// <param name="status">变更后的目标状态</param>
/// <param name="msg">可读的状态描述文本</param>
/// <param name="ex">可选:关联的异常对象</param>
/// <param name="errorCode">可选SDK 底层错误码</param>
public StatusChangedEventArgs(
VideoSourceStatus status,
string msg,
Exception? ex = null,
int? errorCode = null)
{
NewStatus = status;
Message = msg;
Exception = ex;
LastErrorCode = errorCode;
}
#endregion
}

View File

@@ -0,0 +1,150 @@
namespace SHH.CameraSdk;
/// <summary>
/// 视频源基础配置对象 (V3.3.1 修复版)
/// 核心职责:定义建立相机物理连接所需的所有标准化参数与厂商扩展参数
/// 关键修复:
/// <para>1. [Fix Bug U: 配置漂移] 驱动层接收配置时需创建副本,防止外部修改导致的连接异常</para>
/// <para>2. 强化配置有效性校验,提前拦截非法参数</para>
/// 注意事项:此类为引用类型,传递时建议使用深拷贝
/// </summary>
public class VideoSourceConfig
{
#region --- 1. (Core Connection Configurations) ---
/// <summary> 业务系统唯一标识对应数据库自增ID全局唯一 </summary>
public long Id { get; set; }
/// <summary> 设备显示名称(如:北大门-枪机01用于前端展示 </summary>
public string Name { get; set; } = string.Empty;
/// <summary> 设备品牌(决定加载对应的驱动实现类) </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public DeviceBrand Brand { get; set; } = DeviceBrand.Unknown;
/// <summary> 设备 IP 地址或域名(如 192.168.1.100 或 camera.example.com </summary>
public string IpAddress { get; set; } = string.Empty;
/// <summary> 设备端口号厂商默认值海康8000、大华37777、RTSP 554 </summary>
public ushort Port { get; set; }
/// <summary> 设备登录用户名默认值admin </summary>
public string Username { get; set; } = "admin";
/// <summary> 设备登录密码(默认空字符串,需根据实际设备配置) </summary>
public string Password { get; set; } = string.Empty;
/// <summary> 渲染句柄(可选):用于硬解码时直接绑定显示窗口,提升渲染性能 </summary>
public IntPtr RenderHandle { get; set; } = IntPtr.Zero;
/// <summary> 物理通道号IPC 通常为 1NVR 对应接入的摄像头通道索引) </summary>
public int ChannelIndex { get; set; } = 1;
/// <summary> 默认码流类型0 = 主码流(高清)1 = 子码流(低带宽) </summary>
public int StreamType { get; set; } = 0;
/// <summary> 传输协议TCP/UDP/Multicast默认 TCP 保证可靠性) </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public TransportProtocol Transport { get; set; } = TransportProtocol.Tcp;
/// <summary> 连接超时时间(毫秒,默认 5000ms = 5秒 </summary>
public int ConnectionTimeoutMs { get; set; } = 5000;
#endregion
#region --- 2. (Vendor-Specific Extensions) ---
/// <summary>
/// 厂商扩展参数字典
/// 用途:存储无法标准化的品牌专属参数
/// 示例:
/// <code>
/// {
/// "RtspPath": "/h264/ch1/main/av_stream",
/// "HikLoginMode": "ISAPI",
/// "DaHuaStreamProtocol": "HTTP"
/// }
/// </code>
/// </summary>
public Dictionary<string, string> VendorArguments { get; set; } = new();
#endregion
#region --- 3. (Configuration Utility Methods) ---
/// <summary>
/// 配置有效性校验:检查核心参数是否合法,非法则抛出异常
/// 作用:提前拦截无效配置,避免驱动层连接时出现未知错误
/// </summary>
/// <exception cref="ArgumentException">核心参数非法时抛出</exception>
public void Validate()
{
if (Id <= 0)
throw new ArgumentException("配置ID必须为正整数", nameof(Id));
if (string.IsNullOrWhiteSpace(IpAddress))
throw new ArgumentException("IP地址不能为空", nameof(IpAddress));
if (Port == 0)
throw new ArgumentException("端口号必须为有效数值", nameof(Port));
if (Brand == DeviceBrand.Unknown)
throw new ArgumentException("必须指定设备品牌", nameof(Brand));
if (ChannelIndex <= 0)
throw new ArgumentException("通道号必须为正整数", nameof(ChannelIndex));
if (ConnectionTimeoutMs <= 0)
throw new ArgumentException("连接超时时间必须大于0", nameof(ConnectionTimeoutMs));
}
/// <summary>
/// 生成设备唯一连接指纹
/// 用途:用于连接池去重、缓存键、日志标识等场景
/// 格式:{Brand}://{Username}@{IpAddress}:{Port}/{ChannelIndex}
/// </summary>
/// <returns>唯一连接指纹字符串</returns>
public string GetConnectionKey()
{
// 使用 StringBuilder 提升拼接性能,避免频繁创建字符串
return new StringBuilder()
.Append(Brand)
.Append("://")
.Append(Username)
.Append('@')
.Append(IpAddress)
.Append(':')
.Append(Port)
.Append('/')
.Append(ChannelIndex)
.ToString();
}
/// <summary>
/// 创建配置对象的深拷贝(防止外部修改导致配置漂移)
/// </summary>
/// <returns>新的配置副本</returns>
public VideoSourceConfig DeepCopy()
{
var copy = new VideoSourceConfig
{
Id = this.Id,
Name = this.Name,
Brand = this.Brand,
IpAddress = this.IpAddress,
Port = this.Port,
Username = this.Username,
Password = this.Password,
RenderHandle = this.RenderHandle,
ChannelIndex = this.ChannelIndex,
StreamType = this.StreamType,
Transport = this.Transport,
ConnectionTimeoutMs = this.ConnectionTimeoutMs
};
// 深拷贝扩展参数字典
foreach (var kvp in this.VendorArguments)
{
copy.VendorArguments.Add(kvp.Key, kvp.Value);
}
return copy;
}
#endregion
}