目录
视频展示
源码链接
clt123haha/Security (github.com)
如果觉得还不错给个star吧
功能介绍
1.登录注册
1.1 图片验证码
这里是使用了一个博客封装好的部分,暂时找不到原来的链接了,找到了会在这里进行补充
用户输入后与验证码的字符进行比对(大小写无关),大小写无关通过str.toLower () 实现
1.2 明文密文切换
在QPushButton中设置眼睛样式的图片后,链接一个槽函数,在进行点击后进行QlineEdit的模式切换以实现明文密文的切换
1.3密码存储
在信息无误后,将信息进行存储,其中密码以MD5进行加密后存储
2.监控界面
2.1监控画面
获取到设置信息中的摄像头信息,以;为分隔符获取到一个list,依次给与对应的摄像头线程,对捕捉到的信息进行纯净化处理后,将得到的QIamge通过信号发送出去,在绑定的槽函数中保存QIamge,然后触发uodate函数(以激活重绘事件)
2.2四通道切换
设定了一个int来判定状态(0为四通道正常播放,1-4为对应通道画面放大),在重绘事件中进行处理,isvisible可以进行隐藏,这里有一个问题是isvisible和label设置大小的顺序,要先设置label的大小在设置isvisible为true,否则窗口大小肯会受影响
2.3拍照功能
在单通道模式下允许拍照,否则弹窗提示。图片存储路径为:设定图片存储路径/摄像头名称/yymmddmmss.jpg,这里首先会判断有无摄像头文件夹,没有进行创建
2.4视频定时存储
在本次存储开始时,将图片存储下来作为封面。摄像头线程将RGB格式发给播放界面,将YUV格式发给存储线程,这里根据我设定的信息1500帧为一分钟,达到设置的时间后,就写尾帧将视频存储下来。
这里是我的设置
this->picture = av_frame_alloc();
this->picture->width = this->CodecContext->width;
this->picture->height = this->CodecContext->height;
this->picture->format = this->CodecContext->pix_fmt;
this->pictureRGB = av_frame_alloc();
this->pictureRGB->width = this->CodecContext->width;
this->pictureRGB->height = this->CodecContext->height;
this->pictureRGB->format = this->CodecContext->pix_fmt;
this->pictureYUV = av_frame_alloc();
this->pictureYUV->width = this->CodecContext->width;
this->pictureYUV->height = this->CodecContext->height;
this->pictureYUV->format = this->CodecContext->pix_fmt;
对于这里分为两个线程进行的原因:编码解码二者操作AVPacket时可能发生冲突,导致丢帧现象发生
3.视频回放界面
3.1翻页功能
将数据库的数据读取到QList中,点击对应页码后,加载数据
这里比较特殊的是第一页的情况,点击第二页后页码不会刷新,在往后页码才会进行改变
注:这里实测发现如果加载数据量比较大,会出现比较明显的卡顿现象,我的思路是使用limit + offset进行数据查询,我没有进行过二者的比对,只是一个思路
3.2视频播放
点击右边封面后,弹出视频播放界面,这里播放的原理和捕捉摄像头部分类似,只是准备工作需要做一点修改
这是摄像头的准备部分
fmt=av_find_input_format("dshow");
int res=avformat_open_input(&pFormatContext,cameraName.toUtf8(),fmt,nullptr);
这里是
int res=avformat_open_input(&formatContent,file.toStdString().c_str(), nullptr,nullptr);
4.照片回放界面
4.1分页功能
这里和视频回放界面的分页类似,只不过是把页码部分换成了滚动条,设置步长为1,setMinimum
为0,计算出页码总数,setMaximum为页码总数
4.2照片信息展示
右侧部分从上到下依次显示照片名称、宽、高、大小,这里照片名称可能会过长,导致窗口大小发生改变,这里做了一些处理
for(int i=10;i<size;i+=10)
{
result.insert(i, "\n");
}
4.3双击放大
双击和单机区分部分:
重写这两部分进去区分(效果不太精准)
void mousePressEvent(QMouseEvent *event); // 鼠标按压事件
void mouseDoubleClickEvent(QMouseEvent *event); // 鼠标双击事件
放大部分和四通道切换类似,只是放大后需要把滚动条隐藏
5.视频播放
5.1倍速播放
改变sleep的时间
msleep(waitTime);
这里下拉框绑定槽函数来改变线程的waitTime
connect(this->combobox1,SIGNAL(currentIndexChanged(int)),this,SLOT(changeSpead(int)));
void Player::changeSpead(int index)
{
int spead = 40 - 10*index;
th->setWaitTime(spead);
}
5.2视频暂停
点击按钮设定播放线程puase为true,循环直到状态改变
while(puase)
{
if(end)
break;
}
5.3视频转码
选择格式后,拼接出不同的文件名
void EncodeThreah::encodeFrame(AVFrame *pictureYUV)
{
//把AVFrame发送给pCodecContext
int res = avcodec_send_frame(this->pCodecContext, pictureYUV);
if(res<0)
{
qDebug()<<"avcodec_send_frame error";
return;
}
//一帧YUV420P压缩可能压缩成2个AVPacket
while (res >= 0) {
pictureYUV->pts = pkt_index++;
//把一帧AVFrame压缩成AVPacket
res = avcodec_receive_packet(this->pCodecContext, this->pkt);
if(res == AVERROR_EOF || res == AVERROR(EAGAIN))
{
break;
}
//AVPacket写入h264文件
av_interleaved_write_frame(this->opFormatContext, this->pkt);
num++;
//编码帧数+1
}
}
5.4进度条拖动
这里设置进度条长度为视频帧数,拖动后发送信号,捕捉后进行判断
1.当前帧数大于目标帧数
关闭当前的播放线程,重新开启播放线程,到达目标帧数后再发送QIMage
2.当前帧数小于等于目标帧数
到达目标帧数后再发送QIMage
5.5视频切换
这里设置了一个indexnow专门后来记录播放的视频在QList中的位置,在点击按钮后,关闭当前的播放线程,设置path后重新开启播放线程
这里要注意重新设置滚动条,否则会出现频闪等现象
下面是切换上一个视频的槽函数
void VideoPlaybackWin::videoUpOne()
{
indexnow -= 1;
if(indexnow >= 0)
{
player->setEnd(true);
player->setPath(videoList.at(indexnow).getPath());
QString filePath = videoList.at(indexnow).getPath();
QFileInfo fileInfo(filePath);
QString fileName = fileInfo.fileName();
QString extractedString = fileName.section('.', 0, 0);
player->setTitle(extractedString);
player->setUid(uid);
player->setTime(videoList.at(indexnow).getTime());
player->play();
}
else {
QMessageBox::information(this,"提示","已经是第一个视频");
return;
}
}
6.日志查看界面
主要是使用
QWidget *scrollWidget;
QScrollArea *scrollArea;
并且使用QHBoxLayout来达到一组信息一行的效果
QHBoxLayout *rowLayout = new QHBoxLayout();
QList<QString> loglist = logController::getlogController()->getLog();
for (int i = 0; i < loglist.size(); i++) {
QLabel* labelLog = new QLabel(loglist[i]);
// 每四个 QLabel 添加一个新的行布局
if (i % 4 == 0) {
rowLayout = new QHBoxLayout();
scrollLayout->addLayout(rowLayout);
}
rowLayout->addWidget(labelLog);
}
7.设置界面
7.1多选下拉框
这里是使用了一个博客封装好的部分,暂时找不到原来的链接了,找到了会在这里进行补充
7.2摄像头设备获取
获取设置好要使用的摄像头设备并判断是否可用,这里的getRead是摄像头线程进行相关的准备工作
namelist = SetController::getSetController()->cheakRow().at(3).split(";", QString::SkipEmptyParts);
int n = namelist.size();
if(n > 0)
{
th1->setName(namelist.at(0));
if(th1->getReady() != 0)
{
QMessageBox::information(this,"提示","摄像头无法打开");
logController::getlogController()->creatLog(-1,"设备故障");
return;
}
logController::getlogController()->creatLog(-1,"设备正常");
th1->start();
connect(th1,SIGNAL(change(QImage)),this,SLOT(getImg1(QImage)));
}
if(n > 1)
{
th2->setName(namelist.at(1));
if(th2->getReady() != 0)
{
QMessageBox::information(this,"提示","摄像头无法打开");
logController::getlogController()->creatLog(-1,"设备故障");
return;
}
logController::getlogController()->creatLog(-1,"设备正常");
th2->start();
connect(th2,SIGNAL(change(QImage)),this,SLOT(getImg2(QImage)));
}
if(n > 2)
{
th3->setName(namelist.at(2));
if(th3->getReady() != 0)
{
QMessageBox::information(this,"提示","摄像头无法打开");
logController::getlogController()->creatLog(-1,"设备故障");
return;
}
logController::getlogController()->creatLog(-1,"设备正常");
th3->start();
connect(th3,SIGNAL(change(QImage)),this,SLOT(getImg3(QImage)));
}
if(n > 3)
{
th4->setName(namelist.at(3));
if(th4->getReady() != 0)
{
QMessageBox::information(this,"提示","摄像头无法打开");
logController::getlogController()->creatLog(-1,"设备故障");
return;
}
logController::getlogController()->creatLog(-1,"设备正常");
th4->start();
connect(th4,SIGNAL(change(QImage)),this,SLOT(getImg4(QImage)));
}
联系我
如果发现相关bug或者想和我讨论这个项目,可以加我的微信hell0_0123456789