一、前言
在視頻監控行業領域,海康當之無愧是老大,穩坐第一的寶座很多年了,近期需要將視頻監控系統改成采用海康sdk的內核,於是特意去查閱了sdk的使用手冊,sdk相關的文檔和文件可以直接官網下載到,而且是經常更新的,目前提供了windows和linux上的,win上的sdk還是更新比較快而且完整的,但是在linux上的就不得不吐槽下了,很舊很舊的版本,不要看打包文件很新,其實里面的內容是很老的,不知道為啥官方不也同步更新linux上的sdk,也許現在linux上的需求還不是很多吧,但是隨着國產化的大力推進,估計不久的將來linux上的也會慢慢的完善起來的,說不定官方早就有了此計划,還有一種個人的猜測是linux上的開發包估計官方自己內部用的新的,可能打算自己做linux上的各種客戶端吧。
官方的sdk手冊做的還是非常詳細的,而且還給到了VC/JAVA/C#等語言的demo代碼,這點肯定值的表揚的,一個產品持續賣得很好,肯定有他深層次的原因,除了產品本身質量過硬以外,周邊的服務也是必須要跟上的。由於本人習慣用Qt來做開發,所以直接編譯了VC的demo,一步到位跑起來了,界面相對來說簡單丑陋了點,但是要的大部分的功能還是有的,可能很多按鈕也沒擺擺整齊呢,管他呢,我們要的就是功能,演示如何使用對應的方法函數等,至於界面美化這塊,盡量交給專業的UI妹子吧,沒有UI妹子那就自己搞點qss樣式表弄上去湊合着用吧,界面的美化核心主要就兩點:布局+配色。
官方把他們常用的大類設備的sdk都封裝到一起了,一個頭文件老大了,光HCNetSDK.h這個頭文件都47451行,我的乖乖,估計是為了方便用戶使用,都放到一個頭文件里面了,其實這些sdk不光只是支持攝像機,NVR/CVR等設備也都是支持的,兼容性比價好,用同一個sdk都可以,官方只是對一個大類的都統一起來了,不同大類的硬件產品之間的sdk還是有點區別出入,比如現在流行的人臉識別、紅外測溫等,那都是新增加的硬件和功能,但是總體接口和使用流程上都大體一致。
解碼處理流程:
- 調用NET_DVR_Init初始化sdk,一個項目中只需要初始化一次。
- 調用NET_DVR_SetConnectTime、NET_DVR_SetReconnect設置連接時間與重連時間。
- 填充NET_DVR_PREVIEWINFO結構體數據用來登錄。
- 調用NET_DVR_Login_V30登錄設備。
- 調用NET_DVR_RealPlay_V40播放視頻。
- 調用NET_DVR_StopRealPlay停止播放。
- 調用NET_DVR_Logout登出設備。
二、功能特點
- 支持播放視頻流和本地MP4文件。
- 支持句柄和回調兩種模式。
- 多線程顯示圖像,不卡主界面。
- 自動重連網絡攝像頭。
- 可設置邊框大小即偏移量和邊框顏色。
- 可設置是否繪制OSD標簽即標簽文本或圖片和標簽位置。
- 可設置兩種OSD位置和風格。
- 可設置是否保存到文件以及文件名。
- 可直接拖曳文件到haikangwidget控件播放。
- 支持h264/h265視頻流。
- 可暫停播放和繼續播放。
- 支持存儲單個視頻文件和定時存儲視頻文件。
- 自定義頂部懸浮條,發送單擊信號通知,可設置是否啟用。
- 可設置畫面拉伸填充或者等比例填充。
- 可設置解碼是速度優先、質量優先、均衡處理。
- 可對視頻進行截圖(原始圖片)和截屏(視頻窗體)。
- 錄像文件存儲為MP4文件。
- 支持焦距控制、雲台控制。
- 可定制功能。
三、效果圖
四、相關站點
- 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
- 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
- 個人主頁:https://blog.csdn.net/feiyangqingyun
- 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
- 體驗地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652
五、核心代碼
bool HaiKangThread::playRtsp()
{
bool ok = false;
QString ip, userName, userPwd;
int port, streamType;
getInfo(ip, port, streamType, userName, userPwd);
//登錄設備
NET_DVR_DEVICEINFO_V30 deviceInfo;
long userid = NET_DVR_Login_V30(ip.toUtf8().data(), port, userName.toUtf8().data(), userPwd.toUtf8().data(), &deviceInfo);
if (userid >= 0) {
qDebug() << TIMEMS << "登錄海康設備成功" << userid << deviceInfo.sSerialNumber;
//這里還需要拿到視頻流的寬高
//qDebug() << TIMEMS << url << "videoWidth:" << videoWidth << "videoHeight:" << videoHeight;
//以下參數具體見對應頭文件說明
NET_DVR_PREVIEWINFO previewInfo;
previewInfo.lChannel = 1;
previewInfo.dwStreamType = streamType;
previewInfo.dwLinkMode = (transport == "tcp" ? 0 : 1);
previewInfo.bBlocked = 0;
previewInfo.byProtoType = 1;
previewInfo.byPreviewMode = 0;
previewInfo.dwDisplayBufNum = 15;
//回調則可以拿到音視頻數據,否則就直接句柄播放
if (callback) {
hand = NET_DVR_RealPlay_V40(userid, &previewInfo, RealDataCallBack, this);
} else {
previewInfo.hPlayWnd = (HWND)playWidget->winId();
hand = NET_DVR_RealPlay_V40(userid, &previewInfo, NULL, NULL);
}
if (hand >= 0) {
ok = true;
qDebug() << TIMEMS << "打開視頻數據成功" << url << "碼流" << streamType;
} else {
qDebug() << TIMEMS << "打開視頻數據失敗" << url << NET_DVR_GetLastError();
}
} else {
qDebug() << TIMEMS << "登錄海康設備失敗" << NET_DVR_GetLastError();
}
return ok;
}
bool HaiKangThread::playLocal()
{
//轉碼以便支持中文路徑
QTextCodec *codec = QTextCodec::codecForName("gb2312");
QByteArray data = codec->fromUnicode(url);
PlayM4_GetPort(&port);
bool ok = PlayM4_OpenFile(port, data.data());
if (ok) {
//設置文件播放完畢回調函數
PlayM4_SetFileEndCallback(port, FileEndCallback, this);
//回調則可以拿到音視頻數據,否則就直接句柄播放
if (callback) {
PlayM4_SetDecCallBackMend(port, DecCallBack, (quser)this);
PlayM4_Play(port, NULL);
} else {
PlayM4_Play(port, (HWND)playWidget->winId());
}
//同時播放聲音
PlayM4_PlaySound(port);
//倒放
//PlayM4_ReversePlay(port);
//快進播放,多次調用速度更快
//PlayM4_Fast(port);
//PlayM4_Fast(port);
ok = true;
qDebug() << TIMEMS << "打開視頻文件成功" << url;
} else {
qDebug() << TIMEMS << "打開視頻文件失敗" << url << PlayM4_GetLastError(port);
}
return ok;
}
bool HaiKangThread::init()
{
//判斷該攝像機是否能聯通
if (checkConn && isRtsp) {
if (!checkUrl(url, checkTime)) {
return false;
}
}
if (playWidget == NULL) {
return false;
}
//視頻流和本地文件分別處理
bool ok = false;
if (isRtsp) {
ok = playRtsp();
} else {
ok = playLocal();
}
if (!ok) {
return false;
}
//設置保存文件
this->initSave();
return true;
}
void HaiKangThread::free()
{
if (isRtsp) {
//停止播放+登出設備
NET_DVR_StopRealPlay(hand);
NET_DVR_Logout(hand);
hand = -1;
} else {
//停止播放+關閉文件+釋放端口
PlayM4_Stop(port);
PlayM4_StopSound();
PlayM4_CloseFile(port);
PlayM4_FreePort(port);
port = -1;
}
}