简介这份资源是面向Linux平台监控视频开发者的Qt示例工程源码聚焦海康监控摄像头的视频流接入与播放场景适合具备C基础、希望将Qt多媒体能力落地到安防监控项目的开发者参考。压缩包共220个文件约1.2MB以62个h头文件与58个cpp源文件为主体配合51个ui界面文件、10个pri工程配置及png、bmp等界面素材另有pro、qrc、ts等工程与资源文件结构完整便于直接编译调试。源码覆盖网络通信、视频解码、多线程处理、设备发现与配置、播放控制及错误日志等关键环节可帮助读者理解RTSP流获取、QMediaPlayer与QVideoWidget配合显示、界面交互与线程分离等实现思路。目前已有1152人学习下载适合作为监控客户端二次开发与系统集成的起步参考。1. Linux 下海康监控视频 QT demo 源码从拉流到上屏的完整链路很多做安防上位机的开发者都遇到过这个场景设备是海康的 IPC 或 NVR要求在 Linux 桌面环境里做一个能实时预览、能切换通道、能截图录像的客户端。用厂商自带的客户端当然省事但客户要的是集成进自己的系统界面要定制功能要裁剪这时候就得自己写。这份 Linux 下海康监控视频 QT demo 源码解决的就是从零搭建这个客户端的第一步——把海康设备的 RTSP 流拉下来解码再用 QT 渲染到窗口上。它适合两类人一类是刚接触安防视频开发、想找个能跑通的起点的新手另一类是有 QT 基础、需要快速验证海康 SDK 在 Linux 下集成方式的熟手。源码本身不复杂但把 Linux 编译环境、海康 SDK 初始化、QT 与解码线程的配合这几件事串起来了这个串起来的过程才是真正值钱的地方。2. 环境搭建与 SDK 集成让海康库在 Linux 上跑起来2.1 依赖清单与目录规划海康的 Linux SDK 拿到手是一个压缩包解压后通常包含inc/头文件目录和lib/库文件目录库文件又分HCNetSDKCom组件库和主库libhcnetsdk.so。这份 demo 的工程目录一般会这样组织project/ ├── hikvision/ # SDK 头文件与库 │ ├── inc/ │ └── lib/ ├── src/ │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── videothread.cpp ├── build/ └── demo.pro把 SDK 单独放一个目录好处是换版本时只动这一块业务代码不用碰。demo.pro里通过INCLUDEPATH和LIBS把路径引进来这是 QT 工程的标准做法。2.2 .pro 文件的关键配置QT core gui widgets TARGET HikDemo TEMPLATE app SOURCES src/main.cpp \ src/mainwindow.cpp \ src/videothread.cpp HEADERS src/mainwindow.h \ src/videothread.h # 海康 SDK 头文件路径 INCLUDEPATH $$PWD/hikvision/inc # 链接海康主库和组件库 LIBS -L$$PWD/hikvision/lib \ -lhcnetsdk \ -lHCCore \ -lPlayCtrl \ -lAudioRender \ -lSuperRender # 运行时库搜索路径避免找不到 .so QMAKE_RPATHDIR $$PWD/hikvision/lib这里有几个参数值得说清楚。-lhcnetsdk是海康网络 SDK 主库负责登录、预览、云台控制-lPlayCtrl是播放库负责解码和回放-lHCCore是核心组件库很多接口依赖它。QMAKE_RPATHDIR这行容易被忽略不写的话编译能过运行时报error while loading shared libraries因为系统找不到libhcnetsdk.so。我一般还会在.pro里加一句QMAKE_LFLAGS -Wl,-rpath,$$ORIGIN/hikvision/lib让程序在任意路径下都能找到库。2.3 编译与运行前的检查编译命令就是标准的 qmake 流程cd project qmake demo.pro make -j4 ./HikDemo如果编译报undefined reference to NET_DVR_Init说明库没链上检查LIBS路径是否写对。如果运行时报libhcnetsdk.so: cannot open shared object file用ldd HikDemo | grep hcn看依赖解析情况缺哪个补哪个。还有一个常见问题是海康 SDK 对 glibc 版本有要求太老的发行版可能需要升级系统库这个没有捷径只能对齐环境。注意海康 SDK 的库文件分 32 位和 64 位下载时看清楚混用会直接报格式错误。3. 核心模块拆解登录、拉流、解码、上屏四步走3.1 设备登录与句柄管理海康 SDK 的所有操作都围绕一个LONG类型的用户句柄展开。登录代码通常长这样// 初始化 SDK整个进程只需调用一次 NET_DVR_Init(); NET_DVR_SetConnectTime(2000, 1); NET_DVR_SetReconnect(10000, true); // 设备登录参数 NET_DVR_USER_LOGIN_INFO loginInfo {0}; strcpy(loginInfo.sDeviceAddress, 192.168.1.64); loginInfo.wPort 8000; strcpy(loginInfo.sUserName, admin); strcpy(loginInfo.sPassword, password123); loginInfo.bUseAsynLogin false; // 设备信息登录成功后回填 NET_DVR_DEVICEINFO_V40 deviceInfo {0}; LONG lUserID NET_DVR_Login_V40(loginInfo, deviceInfo); if (lUserID 0) { printf(Login failed, error code: %d\n, NET_DVR_GetLastError()); return -1; }NET_DVR_Login_V40是较新的登录接口兼容老设备。sDeviceAddress填设备 IPwPort默认 8000注意不是 RTSP 的 554。bUseAsynLogin设 false 表示同步登录代码简单但会阻塞如果设备多建议改异步。登录失败时NET_DVR_GetLastError()返回错误码常见的有 1用户名密码错、7连接失败、29设备拒绝。句柄lUserID后续所有操作都要用程序退出前必须NET_DVR_Logout(lUserID)否则设备端连接数会累积达到上限后新连接进不来。3.2 预览请求与回调数据登录成功后发起预览NET_DVR_PREVIEWINFO previewInfo {0}; previewInfo.lChannel 1; // 通道号从 1 开始 previewInfo.dwStreamType 0; // 0 主码流1 子码流 previewInfo.dwLinkMode 0; // 0 TCP1 UDP previewInfo.bBlocked 1; // 阻塞取流 previewInfo.hPlayWnd NULL; // 不用 SDK 自带窗口 LONG lRealPlayHandle NET_DVR_RealPlay_V40( lUserID, previewInfo, RealDataCallBack, NULL); if (lRealPlayHandle 0) { printf(RealPlay failed: %d\n, NET_DVR_GetLastError()); }dwStreamType选主码流还是子码流很关键。主码流分辨率高但码率高本地预览如果网络带宽不够会卡子码流分辨率低但流畅适合多画面预览。hPlayWnd设为 NULL 表示不用 SDK 自带的渲染窗口而是通过回调拿原始码流自己解码这样才能把画面嵌进 QT 的控件里。回调函数RealDataCallBack会在每次收到数据时被调用参数里带码流类型和原始数据指针。3.3 回调中解码与 QT 信号传递回调函数运行在海康 SDK 的内部线程里不能直接操作 QT 界面必须通过信号槽把数据转到主线程void CALLBACK RealDataCallBack( LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser) { switch (dwDataType) { case NET_DVR_SYSHEAD: // 系统头送入播放库 PlayM4_SetStreamOpenMode(0, STREAME_REALTIME); PlayM4_OpenStream(0, pBuffer, dwBufSize, 1024*1024); PlayM4_Play(0, NULL); break; case NET_DVR_STREAMDATA: // 码流数据 if (dwBufSize 0) { PlayM4_InputData(0, pBuffer, dwBufSize); } break; } }NET_DVR_SYSHEAD是流的头部信息必须先送给播放库做初始化PlayM4_OpenStream的最后一个参数是缓冲区大小一般给 1MB 够用。NET_DVR_STREAMDATA是实际的音视频数据持续喂给PlayM4_InputData。播放库解码后的 YUV 数据通过PlayM4_SetDecCallBack注册的回调出来在那个回调里把 YUV 转成 QImage再用emit发信号给主窗口的QLabel或自定义QWidget去update()。这条链路是SDK 回调线程 → 播放库解码 → 解码回调 → 信号 → 主线程渲染。每一步的线程归属要清楚跨线程传数据用QMetaObject::invokeMethod或信号槽的Qt::QueuedConnection直接调 UI 函数必崩。3.4 资源释放与异常退出程序退出时的清理顺序不能乱if (lRealPlayHandle 0) { NET_DVR_StopRealPlay(lRealPlayHandle); lRealPlayHandle -1; } if (lUserID 0) { NET_DVR_Logout(lUserID); lUserID -1; } NET_DVR_Cleanup();先停预览再登出最后清理 SDK。顺序反了会出现句柄泄漏下次启动登录失败。如果程序异常崩溃没走到清理设备端的连接会保持一段时间才超时释放调试阶段频繁重启容易撞上连接数上限这时候要么等要么去设备后台踢掉旧连接。4. 避坑与排查海康 SDK 在 Linux 下的五个血泪经验4.1 预览黑屏但无报错现象登录成功NET_DVR_RealPlay_V40返回句柄有效但窗口一直黑。原因通常是码流类型选错或解码回调没注册。先确认dwStreamType和实际码流匹配再检查PlayM4_SetDecCallBack是否在PlayM4_Play之前调用。如果用的是子码流但设备没开子码流也会黑屏。解决用NET_DVR_GetDVRConfig查一下码流配置或者换主码流试。4.2 程序运行一段时间后卡死现象预览正常几分钟后界面无响应。原因多半是解码回调里做了耗时操作或者 YUV 转 RGB 用了 CPU 软转且分辨率高。解决把转换放到独立线程或者用PlayM4_SetDisplayCallBack让播放库直接渲染到指定窗口句柄绕过手动转换。如果必须自己转用libyuv比手写循环快一个数量级。4.3 多通道预览时只有一路有画面现象同时开四个通道只有一个窗口出图。原因是播放库的通道号复用了。PlayM4_OpenStream的第一个参数是播放通道号每个预览实例必须用不同的通道号。解决维护一个通道号分配表0 到 15 轮流用停预览时回收。4.4 编译报错找不到 pthread现象链接阶段报undefined reference to pthread_create。原因QT 工程默认没链 pthread但海康库内部用了。解决在.pro里加LIBS -lpthread。这个问题在较新的 GCC 上更常见因为 glibc 2.34 之后 pthread 合并进了 libc但海康的库还是按老方式链接的。4.5 设备时间不同步导致录像检索异常现象预览正常但按时间检索录像返回空。原因Linux 主机和设备的时间差超过阈值海康 SDK 的时间检索对时差敏感。解决登录后用NET_DVR_GetDVRConfig读设备时间和本机对比差超过 60 秒就NET_DVR_SetDVRConfig同步过去。这个坑在离线部署环境里特别常见因为设备可能没接 NTP。注意海康 SDK 的错误码文档在头文件注释里NET_DVR_GetErrorMsg能把错误码转成可读字符串调试时先打这个。5. 二次开发改造从 demo 到可交付客户端的三个关键改动5.1 把硬编码配置抽成配置文件demo 里设备 IP、端口、用户名密码都是写死的实际项目必须抽出来。我一般用QSettings读写 ini 文件结构大概这样[Device] ip192.168.1.64 port8000 useradmin passwordpassword123 channel1 streamType0读取代码QSettings settings(config.ini, QSettings::IniFormat); QString ip settings.value(Device/ip).toString(); int port settings.value(Device/port, 8000).toInt();这样换设备只改配置文件不用重新编译。密码如果敏感可以加一层简单的异或或 Base64但别自己造加密算法用QCryptographicHash做校验就够了。5.2 用 QThread 封装预览实例demo 通常把预览逻辑放在主窗口里通道一多就乱。改造方向是封装一个PreviewChannel类继承QThread每个通道一个实例内部管理自己的登录句柄、预览句柄、播放通道号。主窗口只负责创建和销毁实例通过信号槽接收图像帧。这样加通道就是new PreviewChannel的事删通道就是delete生命周期清晰。class PreviewChannel : public QThread { Q_OBJECT public: explicit PreviewChannel(const DeviceConfig cfg, int playChannel, QObject *parent nullptr); void stop(); signals: void frameReady(const QImage image); void errorOccurred(const QString msg); protected: void run() override; private: DeviceConfig m_cfg; int m_playChannel; LONG m_userID -1; LONG m_realHandle -1; std::atomicbool m_running{false}; };run()里做登录和预览stop()里做清理。注意stop()要能被主线程调用所以用原子标志位而不是直接terminate()后者会跳过清理导致句柄泄漏。5.3 截图与录像的落盘策略截图用PlayM4_GetJPEG直接从播放库拿 JPEG 数据比从 QImage 再编码快。录像用NET_DVR_SaveRealData存原始码流文件是 PS 格式能用海康的播放器打开但通用播放器不认。如果要存 MP4得自己解码再编码成本高。我一般给客户两个选项原始码流存档省 CPU专用播放器看或者截图序列通用格式但占空间。落盘路径按日期分目录文件名带时间戳避免单目录文件过多。5.4 验证改造是否成功的检查清单改完之后别急着交付按这个顺序过一遍单通道预览 30 分钟不卡顿、不泄漏句柄四通道同时预览 CPU 占用低于 70%拔网线 10 秒后重连能自动恢复程序 kill -9 后重启能正常登录说明设备端连接释放了配置文件改 IP 后无需重编译生效。这几条过了基本能扛住现场环境。从那以后我每次接海康 SDK 的项目都强制先跑一遍这个 demo 的登录和预览流程确认环境没问题再动业务代码。希望帮到你。本文还有配套的精品资源点击获取