基于Qt+海康sdk+MySql的远程录像下载程序
一.前言
距離上次完成的Linux下QT實時音頻采集傳輸項目已經有一個月的時間。之后又在Linux上完成了許多大大小小的項目,多是與UI有關。
這次給實現的是Windows下的QT+Mysql+海康sdk的遠程錄像下載程序。
需要QT連上Mysql,數據庫版本必須和QT對應,32位QT是連不上64位的Mysql滴。連接方法可以參考這個文章:QT5.5連接mysql5.6?或 本文setsql.cpp中 sql_init() 函數的內容。
另外還測試了用SqlServer連接,原理差不多,只修改了連接部分,程序運行良好。
二.程序流程
程序的流程思路很簡單:先通過QT進入本地Mysql數據庫,遍歷一張表,找出未下載過的錄像記錄并讀取對應的IP和時間Time,逐個添加到海康sdk中,下載該IP號錄像機在這個時間Time的錄像。下載完畢后更新數據庫。
?
1.Mysql表參考 (表名:ear)
??
EAR_Ip:錄像機IP號? ? ?EAR_Time:下載該時間的錄像?? ??
EAR_Confirm:確認是否下載(已下載為1,未下載為0)
?
2.海康sdk錄像下載流程(截自官方的設備網絡SDK使用手冊)
?
?寫的非常清楚,對應的sdk函數都在里面寫好了,跟著調用就好。本篇是按時間查找下載。
?
?
3.程序流程
二.項目代碼
?
1.? ?pro文件
手頭的海康Lib、.h文件、.dll文件需要鏈接到QT上,不知道如何添加的朋友可以參考這篇文章:QT 添加 lib庫
QT+= sql 要加上
#------------------------------------------------- # # Project created by QtCreator 2018-07-30T16:12:52 # #-------------------------------------------------QT += core gui sqlgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = DownloadVideo TEMPLATE = app# The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += main.cpp\dialog.cpp \setsql.cpp \HK_sdk.cppHEADERS += dialog.h \setsql.h\HK_sdk.hFORMS += dialog.uiINCLUDEPATH += $$PWD/includeLIBS += -L$$PWD/ -lGdiPlus LIBS += -L$$PWD/ -lHCAlarm LIBS += -L$$PWD/ -lHCCore LIBS += -L$$PWD/ -lHCGeneralCfgMgr LIBS += -L$$PWD/ -lHCPreview LIBS += -L$$PWD/ -lPlayCtrl LIBS += -L$$PWD/ -lHCNetSDK2.? ?.h文件
?
HK_SDK.h
/********************************* * 本文件主要包含海康SDK中 * 下載模塊的各種接口函數 * 以及對QSTring格式的時間處理函數 * Jingenfan@126.com 姓值錢的金三歲 * --------2018年7月30日----------- *********************************/#ifndef HK_SDK_H #define HK_SDK_H #include <QApplication> #include <QApplication> #include <QProcess> #include <QMessageBox> #include <QSqlDatabase> #include <QSqlQuery> #include <QTextCodec> #include <QDebug> #include <QString> #include <iostream> #include <stdio.h> #include <QTimer> #include <QWidget> #include <QObject> #include <QDateTime>#include <windows.h> #include "windows.h" #include "HCNetSDK.h"using namespace std;//因為要用到QTimer,因此繼承QObject class HK_SDK:public QObject {Q_OBJECT public:explicit HK_SDK(QObject *parent=0);~HK_SDK();void HK_Login(QString ip); //登錄設備bool HK_DownLoadVideo(QString Ptime); //下載錄像void get_DownloadPos(); //獲取下載進度void Time_Adjust(QString time); //時間處理int ChannelNumber = -1; //存儲通道號QString SavePath = "D:/Qt Save/SDK_Save/VideoSave/"; //文件存儲路徑char * IPAdress = "192.168.0.10"; //錄像總線IP號WORD PORT = 8000; //錄像總線端口號char * Username = "admin"; //錄像總線登錄用戶名char * Password = "admin123"; //錄像總線登錄密碼 long user_id = -1; //登錄號返回的用戶id號(類似socket里面的套接字fd)uint last_error = 0; //用于存儲最近一次的錯誤int findHandle = -1; //錄像查找句柄int downHandle = -1; //錄像下載句柄int ipos = 0; //錄像下載句柄DWORD dwReturn; //作參數傳入NET_DVR_GetDVRConfig,作用未知NET_DVR_DEVICEINFO_V30 lpDeviceInfo; //設備參數結構體NET_DVR_LOCAL_GENERAL_CFG _cfg; //通用參數配置結構體NET_DVR_IPPARACFG_V40 m_struIpParaCfgV40; //IP設備資源及IP通道資源配置結構體LPNET_DVR_FILECOND_V40 pFindCond; //欲查找的文件信息結構LPNET_DVR_PLAYCOND pDownloadCond; //回放或者下載信息結構體QTimer *tim; //作定時器,隔一段時間獲取一次下載進度DWORD HouRange = 0; //設定下載的時間范圍 小時DWORD MinRange = 1; //設定下載的時間范圍 分鐘DWORD SecRange = 30; //設定下載的時間范圍 秒public slots:void get_Pos(); //槽函數,響應定時器,獲取當前下載進度private:/*錄像起始時間結構體*/typedef struct STime_Config{DWORD Year;DWORD Month;DWORD Day;DWORD Hour;DWORD Minute;DWORD Second;}stime_conf;stime_conf *st;/*錄像結束時間結構體*/typedef struct ETime_Config{DWORD Year;DWORD Month;DWORD Day;DWORD Hour;DWORD Minute;DWORD Second;}etime_conf;etime_conf *et;/*用于存儲sdk返回的設備通道號*/int iChannelNum[96];};#endif // HK_SDK_Hsetsql.h
/********************************* * 本文件功能主要用于連接、讀取 * 和更新本地Mysql數據庫 * 同時定義HK類用于控制錄像下載 * Jingenfan@126.com 姓值錢的金三歲 *********************************/ #ifndef SETSQL_H #define SETSQL_H #include <QtSql/qsql.h> #include <QMessageBox> #include <QSqlQuery> #include <QSqlDatabase> #include <list>#include <QDebug> #include <iostream>#include "HK_sdk.h" using namespace std;class SetSql { public:SetSql();~SetSql();QString TableName="ear"; //庫名 可自行修改bool sql_init(); //連接本地sql庫void Show_Table(); //打印表中所有數據void Change_TableConfirm(QString EAR_IP); //下載完畢后根據EAR_IP改變表中對應數據的EAR_Confirmvoid GetTime(); //獲得表中EAR_Confirm=0(未下載)的數據ip和timevoid DownLoadVideo(); //下載錄像private:HK_SDK *hk; //該類包含海康sdk下載所需的接口函數QSqlDatabase db; //數據庫句柄/*結構體存儲未下載過的錄像的IP和時間*/typedef struct UPDATE{QString EAR_IP;QString EAR_Time;bool EAR_Confirm;}update;update up;/*List容器存儲update結構體*/typedef list<update> upSave;upSave ups;}; #endif // SETSQL_H3.? ?.cpp文件
HK_SDK.cpp
#include "HK_sdk.h"HK_SDK::HK_SDK(QObject *parent):QObject(parent) {st = new stime_conf;et = new etime_conf;tim = new QTimer(this);connect(tim,SIGNAL(timeout()),this,SLOT(get_Pos())); //定時器連接槽函數,槽函數功能為獲取下載進度/*初始化SDK*/if(NET_DVR_Init()){qDebug()<<"Init success!";NET_DVR_SetLogToFile(3,"D:/Qt Save/log/",true);}elseqDebug()<<"Init Failed !"; }HK_SDK::~HK_SDK() {if(!NET_DVR_Logout(user_id)){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_Logout Error:"<<last_error;return;}user_id = -1;delete pFindCond;delete pDownloadCond;delete tim; }void HK_SDK::HK_Login(QString ip) {if(user_id<0){//按賬號密碼等信息登錄對應的IP,返回一個user_iduser_id = NET_DVR_Login_V30(IPAdress,PORT,Username,Password,&lpDeviceInfo);if(user_id < 0){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_Login_V30 failed :"<<last_error;}else{qDebug()<<"Login success !";}//設置錄像文件分片大小_cfg.byExceptionCbDirectly = 0;_cfg.byNotSplitRecordFile = 0;_cfg.i64FileSize = 1024 * 1024 * 1024 ; //1Gif(!NET_DVR_SetSDKLocalCfg(NET_DVR_LOCAL_CFG_TYPE_GENERAL,&_cfg)){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_SetSDKLocalCfg Error:"<<last_error;}}//打印在線的IP和通道對應的字典memset(&m_struIpParaCfgV40,0,sizeof(m_struIpParaCfgV40));int dwDChanTotalNum = lpDeviceInfo.byIPChanNum + 256 * lpDeviceInfo.byHighDChanNum;if(dwDChanTotalNum > 0){if(!NET_DVR_GetDVRConfig(user_id,NET_DVR_GET_IPPARACFG_V40,0,&m_struIpParaCfgV40,sizeof(m_struIpParaCfgV40),&dwReturn)){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_GET_IPPARACFG_V40 Failed:"<<last_error;}else{for(int i = 0;i < 10/*MAX_IP_DEVICE_V40*/;i++){iChannelNum[i] = i+ m_struIpParaCfgV40.dwStartDChan;}qDebug()<<"===========================";for(int i = 0;i < 10/*MAX_IP_DEVICE_V40*/;i++){int j = m_struIpParaCfgV40.struIPDevInfo[i].byEnable;qDebug()<<"IP:" << m_struIpParaCfgV40.struIPDevInfo[i].struIP.sIpV4<< "通道號:" <<iChannelNum[i]<<" 是否有效:"<< j;if(ip == m_struIpParaCfgV40.struIPDevInfo[i].struIP.sIpV4){ChannelNumber = i;qDebug()<<"Channel Match :"<<i<<" IP:"<<ip<<" | "<<m_struIpParaCfgV40.struIPDevInfo[i].struIP.sIpV4;}}qDebug()<<"============================";}} }bool HK_SDK::HK_DownLoadVideo(QString Ptime) {pFindCond = new NET_DVR_FILECOND_V40();pFindCond->lChannel = iChannelNum[ChannelNumber];pFindCond->dwFileType = 0xff; //0xff-全部,0-定時錄像,1-移動偵測,2-報警觸發,...pFindCond->dwIsLocked = 0xff; //0-未鎖定文件,1-鎖定文件,0xff表示所有文件(包括鎖定和未鎖定)//設置錄像查找的開始時間pFindCond->struStartTime.dwYear = st->Year;pFindCond->struStartTime.dwMonth = st->Month;pFindCond->struStartTime.dwDay = st->Day;pFindCond->struStartTime.dwHour = st->Hour;pFindCond->struStartTime.dwMinute = st->Minute;pFindCond->struStartTime.dwSecond = st->Second;//設置錄像查找的結束時間pFindCond->struStopTime.dwYear = et->Year;pFindCond->struStopTime.dwMonth = et->Month;pFindCond->struStopTime.dwDay = et->Day;pFindCond->struStopTime.dwHour = et->Hour;pFindCond->struStopTime.dwMinute = et->Minute;pFindCond->struStopTime.dwSecond = et->Second;findHandle = NET_DVR_FindFile_V40(user_id,pFindCond);if(findHandle < 0){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_FindFile_V40 Failed:"<<last_error;return false;}else{pDownloadCond = new NET_DVR_PLAYCOND();pDownloadCond->dwChannel = iChannelNum[ChannelNumber];//設置錄像下載的開始時間pDownloadCond->struStartTime.dwYear = st->Year;pDownloadCond->struStartTime.dwMonth = st->Month;pDownloadCond->struStartTime.dwDay = st->Day;pDownloadCond->struStartTime.dwHour = st->Hour;pDownloadCond->struStartTime.dwMinute = st->Minute;pDownloadCond->struStartTime.dwSecond = st->Second;//設置錄像下載的結束時間pDownloadCond->struStopTime.dwYear = et->Year;pDownloadCond->struStopTime.dwMonth = et->Month;pDownloadCond->struStopTime.dwDay = et->Day;pDownloadCond->struStopTime.dwHour = et->Hour;pDownloadCond->struStopTime.dwMinute = et->Minute;pDownloadCond->struStopTime.dwSecond = et->Second;//設置錄像的存儲路徑,命名格式為表EAR_Time里的時間+.mp4,年月日時分秒用'-'隔開Ptime.replace("-","-");Ptime.replace("T","-");Ptime.replace(":","-");QString str = SavePath + Ptime +".mp4";char* SaveFile;QByteArray ba = str.toLatin1(); // mustSaveFile=ba.data();qDebug()<<"**********************************"<<SaveFile;//創建下載(這時候并不是已經開始,需要NET_DVR_PlayBackControl_V40觸發)downHandle = NET_DVR_GetFileByTime_V40(user_id,SaveFile,pDownloadCond);if(downHandle < 0){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_GetFileByTime_V40: "<<last_error;return false;}else{//真正開始下載錄像if(!NET_DVR_PlayBackControl_V40(downHandle,NET_DVR_PLAYSTART)){last_error = NET_DVR_GetLastError();qDebug()<<"NET_DVR_PlayBackControl_V40: "<<last_error;return false;}else{qDebug()<<"DwonLoad Video......";tim->start(5000); //開啟定時器 每隔五秒獲取一次下載進度return true;}}} }/*獲取下載進度 0~100*/ void HK_SDK::get_DownloadPos() {ipos = NET_DVR_GetDownloadPos(downHandle);qDebug()<<"Download===========> "<<ipos<<"%";if(ipos == 100){qDebug()<<"DwonLoad Video finished......";tim->stop();} }/*時間處理函數 sdk需要年、月、日、時、分、秒,這里* 將EAR_Time字符串分割開,并添加范圍,傳入結構體*/ void HK_SDK::Time_Adjust(QString time) {qDebug()<<"TIME ADJUST!!!!!!!!!!!!";//2018-07-24T09:06:11DWORD Year = time.section('-',0,0).toInt();DWORD Month= time.section('-',1,1).toInt();DWORD Day = time.section('-',2,2).section('T',0,0).toInt();DWORD Hour = time.section('-',2,2).section('T',1,1).section(':',0,0).toInt();DWORD Min = time.section('-',2,2).section('T',1,1).section(':',1,1).toInt();DWORD Sec = time.section('-',2,2).section('T',1,1).section(':',2,2).toInt();qDebug()<<Year<<Month<<Day<<Hour<<Min<<Sec<<endl;DWORD S_sum = Hour * 3600 + Min * 60 +Sec;DWORD R_sum = HouRange * 3600 + MinRange * 60 + SecRange;DWORD D_sum = 24*3600+60*60+60;DWORD SUM=0;/*時間加減范圍處理比較麻煩 也可以用QDatetime進行加減*/if(S_sum > R_sum){st->Year = Year;st->Month = Month;st->Day = Day;SUM = S_sum - R_sum;st->Hour = SUM/3600;st->Minute = (SUM%3600)/60;st->Second = (SUM%3600)%60;}else{st->Year = Year;st->Month = Month;if(Day = 1){st->Day = Day;st->Hour = 0;st->Minute = 0;st->Second = 0;qDebug()<<"=============僅限查詢本月視頻 上月視頻請重新設置============";}else{st->Day = Day - 1;SUM = S_sum - R_sum + 24 * 3600;st->Hour = SUM/3600;st->Minute = (SUM%3600)/60;st->Second = (SUM%3600)%60;}}qDebug()<<st->Year<<st->Month<<st->Day<<st->Hour<<st->Minute<<st->Second;//------------------------------------------------------------------------------if(S_sum + R_sum > D_sum){et->Year = Year;et->Month = Month;et->Day = Day;et->Hour = 23;et->Minute = 59;et->Second = 59;qDebug()<<"=============僅限查詢本日視頻 次日視頻請重新設置============";}else{et->Year = Year;et->Month = Month;et->Day = Day;SUM = S_sum + R_sum;et->Hour = SUM/3600;et->Minute = (SUM%3600)/60;et->Second = (SUM%3600)%60;}qDebug()<<et->Year<<et->Month<<et->Day<<et->Hour<<et->Minute<<et->Second; }/*槽函數 觸發下載進度查詢*/ void HK_SDK::get_Pos() {get_DownloadPos(); }?
setsql.cpp
#include "setsql.h"SetSql::SetSql() {hk = new HK_SDK();sql_init();DownLoadVideo(); }SetSql::~SetSql() {delete hk; }/*登錄數據庫*/ bool SetSql::sql_init() {db = QSqlDatabase::addDatabase("QMYSQL");db.setConnectOptions("UTF8");db.setHostName("127.0.0.1"); //mysql的地址db.setPort(3306); //數據庫端口號db.setDatabaseName("ear"); //連接的數據庫名稱db.setUserName("root"); //mysql登錄名db.setPassword("root"); //mysql密碼if(db.open()){qDebug()<<"SQL init success!";qDebug()<<db.driverName();Show_Table();} }/*打印表中所有數據*/ void SetSql::Show_Table() {QSqlQuery query(db);QString Tname = TableName;query.exec("select * from "+Tname);qDebug()<<"==================================";while (query.next()) {QString IP = query.value(0).toString();QString time= query.value(1).toString();bool confirm = query.value(2).toBool();qDebug()<<IP<<time<<confirm;}qDebug()<<"=================================="; }/*下載完錄像后,修改對應的EAR_Confirm為1 */ void SetSql::Change_TableConfirm(QString EAR_IP) {QSqlQuery query(db);QString ip = "'"+EAR_IP+"'";query.exec("UPDATE ear SET EAR_Confirm=1 WHERE EAR_Ip="+ip);qDebug()<<EAR_IP<<" Confirm Is Updated !";Show_Table(); }/*得到EAR_Confirm為0時的ip和time*/ void SetSql::GetTime() {QSqlQuery query(db);query.exec("select EAR_Ip,EAR_Time,EAR_Confirm from "+TableName+" where EAR_Confirm=0");while (query.next()) {up.EAR_IP = query.value(0).toString();up.EAR_Time = query.value(1).toString();up.EAR_Confirm = query.value(2).toBool();ups.push_back(up);}list<update>::iterator iter;qDebug()<<"<=======Get Time========>";for(iter=ups.begin();iter!=ups.end();iter++){qDebug()<<iter->EAR_IP<<iter->EAR_Time<<iter->EAR_Confirm;}qDebug()<<"<=======Get Time========>"; }/*循環遍歷List,按ip和time下載對應的錄像*/ void SetSql::DownLoadVideo() {GetTime();list<update>::iterator iter;QString time;for(iter=ups.begin();iter!=ups.end();iter++){time = iter->EAR_Time;hk->Time_Adjust(time);hk->HK_Login(iter->EAR_IP);if(hk->HK_DownLoadVideo(iter->EAR_Time))Change_TableConfirm(iter->EAR_IP);} }Main.cpp
#include "dialog.h" #include <QApplication> #include "setsql.h" #include "HK_sdk.h" #include <QApplication> #include <QProcess> #include <QMessageBox> #include <QSqlDatabase> #include <QSqlQuery> #include <QTextCodec>/*海康SDK頭文件 必須添加!*/ #include "HCNetSDK.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);/*這句非常重要 很多人讀寫數據庫亂碼問題就是沒加這句話*/QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF8"));SetSql sql;return a.exec(); }四.運行結果
若表如下,IP號0.68和0.2沒下載,需要下載該EAR_Time下的錄像
運行QT程序,GetTime()函數找到他們倆
然后開始下載,得到下載進度
下載完后兩個表已更新
對應路徑下已經得到了這兩個錄像文件,命名格式按EAR_Time命名
五.遇到的問題與注意事項
(1)?海康的sdk分 64位和32位,請與QT版本對應好,否則無法鏈接。
(2) 海康的幾個.h文件需要轉編碼格式(若數據庫和QT是其他編碼格式就轉成和他們一樣),我是用Notepad++轉成UTF-8。
(3)出現數據庫在QT輸出框上顯示亂碼的問題,程序Main開頭加上 這句話:
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF8"));(4)注意sdk中類似于下面這種
LPNET_DVR_FILECOND_V40 / LPNET_DVR_PLAYCOND
和
NET_DVR_FILECOND_V40 / NET_DVR_PLAYCOND
的區別。前面有沒有LP,決定了這些函數內傳入的參數結構體是否該加取地址符(&),需要注意一下。
總結
以上是生活随笔為你收集整理的基于Qt+海康sdk+MySql的远程录像下载程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Microsoft Graph - 社区
- 下一篇: Android开发:ListView+S