☰
Linux下海康监控视频QT demo源码:从拉流到上屏完整链路
2026/10/16 3:32:02 网站建设 项目流程

简介:这份资源是面向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填设备 IP,wPort默认 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 TCP,1 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] ip=192.168.1.64 port=8000 user=admin password=password123 channel=1 streamType=0

读取代码:

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::atomic<bool> 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 的登录和预览流程,确认环境没问题再动业务代码。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询