C#控制海康网络摄像头:从SDK集成到视频流保存的实战指南

1次阅读
没有评论

共计 2335 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景痛点

在工业级视频监控项目中,海康威视设备的稳定性与 C# 生态的便捷性结合是常见需求。但实际开发中会遇到几个典型问题:

C# 控制海康网络摄像头:从 SDK 集成到视频流保存的实战指南

  • SDK 文档晦涩:官方提供的 CHM 文档函数说明分散,回调机制描述不直观(比如预览回调的 YUV 数据格式需反复试验)
  • 初始化失败 :NET_DVR_Init() 返回 false 时缺乏详细错误信息,常见于:
  • DLL 依赖项缺失(如缺少 HCNetSDK 依赖的 libeay32.dll)
  • 未调用 NET_DVR_SetConnectTime 设置超时参数
  • 预览异常:黑屏问题往往源于:
  • 未正确处理 REALPLAYCALLBACK 的帧数据
  • 网络带宽不足导致丢帧(需调整 NET_DVR_PREVIEWINFO 的码流类型)

技术方案选型

对比常见技术路线:

  • HCNetSDK 原生方案:
  • 优势:直接访问设备所有功能(PTZ 控制、报警输入等)
  • 劣势:需处理非托管内存和复杂回调
  • OpenCV+RTSP:
  • 优势:跨平台且代码简洁
  • 劣势:无法使用设备特色功能(如智能分析结果获取)

核心流程采用海康 SDK 原生方案,关键步骤如下:

  1. SDK 初始化与环境检测
  2. 设备登录(支持 IPC/NVR 不同设备类型)
  3. 获取通道能力集(确定支持的视频格式)
  4. 启动预览并设置回调函数
  5. 视频流存储与转码

代码实现详解

1. SDK 安全封装

使用 SafeHandle 避免句柄泄漏:

public sealed class HikDeviceHandle : SafeHandleZeroOrMinusOneIsInvalid
{protected override bool ReleaseHandle()
    {return HCNetSDK.NET_DVR_Logout(handle) && 
               HCNetSDK.NET_DVR_Cleanup();}
}

2. 回调函数转委托

处理预览回调时需注意 IntPtr 到byte[]的转换:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void REALDATACALLBACK(
    int lRealHandle, 
    uint dwDataType, 
    IntPtr pBuffer, 
    uint dwBufSize, 
    IntPtr pUser);

// 实际处理函数
static void OnPreviewData(int handle, uint type, IntPtr pData, uint size, IntPtr user)
{byte[] buffer = new byte[size];
    Marshal.Copy(pData, buffer, 0, (int)size);
    // 处理 YUV420 数据...
}

3. 带重试的登录逻辑

public HikDeviceHandle Login(string ip, ushort port, string user, string pwd, int retry = 3)
{
    HCNetSDK.NET_DVR_DEVICEINFO_V30 devInfo;
    for (int i = 0; i < retry; i++)
    {
        int userId = HCNetSDK.NET_DVR_Login_V30(ip, port, user, pwd, out devInfo);
        if (userId >= 0)
            return new HikDeviceHandle(userId, devInfo);
        Thread.Sleep(1000);
    }
    throw new HikException($"登录失败,最后错误:{HCNetSDK.NET_DVR_GetLastError()}");
}

4. FFmpeg 转码关键帧处理

使用 FFmpeg.AutoGen 时需注意 GOP 对齐:

// 创建编码器上下文时指定 GOP 大小
codecContext->gop_size = 30; 

// 收到 I 帧时开始新文件
if ((pPacket->flags & AV_PKT_FLAG_KEY) != 0)
{StartNewRecording();
}

避坑指南

32/64 位 DLL 加载

通过 PE 头检测避免架构不匹配:

static void LoadCorrectDll()
{
    string arch = Environment.Is64BitProcess ? "x64" : "x86";
    string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, arch);
    SetDllDirectory(dllPath);
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string path);

网络断连处理

建议实现心跳检测机制:

// 定时检查设备在线状态
Timer checkTimer = new Timer(_ => 
{if (!HCNetSDK.NET_DVR_GetDeviceState(handle, out _))
    {OnDisconnected();
    }
}, null, 0, 5000);

性能优化

内存池管理

复用 YUV 缓冲区避免频繁 GC:

ConcurrentBag<byte[]> _yuvBuffers = new();

byte[] GetBuffer(int size)
{if (_yuvBuffers.TryTake(out var buf) && buf.Length >= size)
        return buf;
    return new byte[size];
}

多摄像头线程策略

建议采用生产者 - 消费者模式:

  1. 每个摄像头独立采集线程
  2. 共享一个写文件队列
  3. 使用 BlockingCollection 控制并发写入

思考题

如何实现断网续传功能?可考虑:

  • 本地缓存未上传的视频片段
  • 使用 NET_DVR_GetLastError 判断网络状态
  • 设计重传队列管理待同步数据
正文完
 0
评论(没有评论)