距离上次完成的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.程序流程
手头的海康Lib、.h文件、.dll文件需要链接到QT上,不知道如何添加的朋友可以参考这篇文章:QT 添加 lib库
QT+= sql 要加上
#-------------------------------------------------
#
# Project created by QtCreator 2018-07-30T16:12:52
#
#-------------------------------------------------
QT += core gui sql
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = 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.0
SOURCES += main.cpp\
dialog.cpp \
setsql.cpp \
HK_sdk.cpp
HEADERS += dialog.h \
setsql.h\
HK_sdk.h
FORMS += dialog.ui
INCLUDEPATH += $$PWD/include
LIBS += -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/ -lHCNetSDK
HK_SDK.h
/*********************************
* 本文件主要包含海康SDK中
* 下载模块的各种接口函数
* 以及对QSTring格式的时间处理函数
* [email protected] 姓值钱的金三岁
* --------2018年7月30日-----------
*********************************/
#ifndef HK_SDK_H
#define HK_SDK_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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_H
setsql.h
/*********************************
* 本文件功能主要用于连接、读取
* 和更新本地Mysql数据库
* 同时定义HK类用于控制录像下载
* [email protected] 姓值钱的金三岁
*********************************/
#ifndef SETSQL_H
#define SETSQL_H
#include
#include
#include
#include
#include
#include
#include
#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_Confirm
void GetTime(); //获得表中EAR_Confirm=0(未下载)的数据ip和time
void 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 upSave;
upSave ups;
};
#endif // SETSQL_H
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);
}
else
qDebug()<<"Init Failed !";
}
HK_SDK::~HK_SDK()
{
if(!NET_DVR_Logout(user_id))
{
last_error = NET_DVR_GetLastError();
qDebug()<<"NET_DVR_Logout Error:"< 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:"<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:"<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(); // must
SaveFile=ba.data();
qDebug()<<"**********************************"<start(5000); //开启定时器 每隔五秒获取一次下载进度
return true;
}
}
}
}
/*获取下载进度 0~100*/
void HK_SDK::get_DownloadPos()
{
ipos = NET_DVR_GetDownloadPos(downHandle);
qDebug()<<"Download===========> "<stop();
}
}
/*时间处理函数 sdk需要年、月、日、时、分、秒,这里
* 将EAR_Time字符串分割开,并添加范围,传入结构体*/
void HK_SDK::Time_Adjust(QString time)
{
qDebug()<<"TIME ADJUST!!!!!!!!!!!!";
//2018-07-24T09:06:11
DWORD 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()< 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()<Year<Month<Day<Hour<Minute<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()<Year<Month<Day<Hour<Minute<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()<::iterator iter;
qDebug()<<"<=======Get Time========>";
for(iter=ups.begin();iter!=ups.end();iter++)
{
qDebug()<EAR_IP<EAR_Time<EAR_Confirm;
}
qDebug()<<"<=======Get Time========>";
}
/*循环遍历List,按ip和time下载对应的录像*/
void SetSql::DownLoadVideo()
{
GetTime();
list::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);
}
}
#include "dialog.h"
#include
#include "setsql.h"
#include "HK_sdk.h"
#include
#include
#include
#include
#include
#include
/*海康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,决定了这些函数内传入的参数结构体是否该加取地址符(&),需要注意一下。