OpenCV实现视频播放器,其思路大致就是在线程中使用OpenCV中的VideoCapture循环读取本地视频的每一帧Mat,然后发送到界面转换成QImage进行显示,而进度条拖动则用到了VideoCapture中的set函数,进度条则是使用Qslider;并且通过自定义新的进度条类实现点击跳转功能;
效果:
1.进行播放,线程循环读取视频帧并计数当前帧数,把Mat帧和当前帧数通过信号发送到窗口
窗口中创建线程,然后点击播放时在调用start启动线程;
void videothread::run()
{
while(stop==false)//线程运行和停止 卡住线程 暂停时不退出线程
{
while(isrun==true)//视频运行和暂停
{
if(cap.read(frame))//捕获视频帧
{
this->currentFramecount++;
cvtColor(frame, frame, CV_BGR2RGB);//opencvBGR格式转成Image用到的RGB
emit sendFrame(currentFramecount,frame);//发送帧数据
}
msleep(40);//延时
}
}
cap.release();//释放打开的视频
}
2.获取当前帧数设置进度条当前值
3.通过VideoCapture中的get方法获取视频总帧数并保存
if(cap.open(filename));//创建视频对象
{
this->allFramecount=cap.get(CAP_PROP_FRAME_COUNT);//获取视频文件中的总帧数
this->fps=int(round(cap.get(CAP_PROP_FPS)));//获取视频帧率
}
4.设置进度条取值范围,即0-总帧数
ui->horizontalSlider->setRange(0,pthread->getVideoAllFramecount());//设置进度条取值范围
ui->horizontalSlider->setSingleStep(1);//设置单步长为1
ui->label_4->setText(QString::number(pthread->getVideoAllFramecount()));//设置总帧数
接下来就是进度条拖动,在知道如何做之前,我们要先了解Qslider的几个信号,我们拖动时就需要用到点击、滑动和释放信号
Qslider常用信号
void sliderMove(int value);
void sliderPressed();
void sliderReleased();
5.首先是拖动进度条,拖动的时候先把播放的视频暂停
就是停止循环读帧
void Widget::on_horizontalSlider_sliderMoved(int position)//进度条拖动
{
pthread->pauseThread();//播放暂停
}
void videothread::pauseThread()//这两个函数用于确保是在运行情况下才能切换状态
{
if(this->isRunning()&&this->isrun==true)//当前线程运行且视频运行
{
this->isrun=false;
}
}
6.释放进度条
获取拖动后的进度条的值,然后通过set中的CV_CAP_PROP_POS_FRAMES进行设置
void Widget::on_horizontalSlider_sliderReleased()//释放进度条
{
//ui->horizontalSlider->value();获取当前进度条值
pthread->setCurrentFrame(ui->horizontalSlider->value());
pthread->resumeThread();//播放继续 设置为true
}
void videothread::setCurrentFrame(int value)
{
this->currentFramecount=value;//当前帧数
cap.set(CV_CAP_PROP_POS_FRAMES,currentFramecount);//进度条跳转对应帧
}
7.点击进度条,重写一个进度条类
因为系统自带的进度条点击时只能移动一小段,不能实现点哪就移动到哪,所以我们要自己重写一个进度条类,然后在ui中提升为重写的进度条
完整源码:
自定义进度条
#ifndef NEWQSLIDER_H
#define NEWQSLIDER_H
#include <QWidget>
#include <QSlider>
#include <QMouseEvent>
class newqslider : public QSlider
{
Q_OBJECT
public:
explicit newqslider(QWidget *parent = 0);
void mousePressEvent(QMouseEvent *ev);
signals:
void costomSliderClicked();//自定义的鼠标单击信号,用于捕获并处理
public slots:
};
#endif // NEWQSLIDER_H
#include "newqslider.h"
newqslider::newqslider(QWidget *parent) : QSlider(parent)
{
}
/*****************************************************************
* 函数名称:mousePressEvent(QMouseEvent *ev)
* 功能描述:重写鼠标点击事件,实现进度条点击哪跳到哪
* 参数说明: 无
* 返回值: 无
******************************************************************/
void newqslider::mousePressEvent(QMouseEvent *ev)
{
//先调用父类的鼠标点击处理事件,这样可以不影响拖动的情况
QSlider::mousePressEvent(ev);
//获取鼠标的位置,这里并不能直接从ev中取值(因为如果是拖动的话,鼠标开始点击的位置没有意义了)
double pos = ev->pos().x() / (double)width();
setValue(pos * (maximum() - minimum()) + minimum());
//发送自定义的鼠标单击信号
emit costomSliderClicked();
}
线程类
#ifndef VIDEOTHREAD_H
#define VIDEOTHREAD_H
#include <QObject>
#include <QThread>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <QDebug>
#include <QDateTime>
using namespace std;
using namespace cv;
class videothread : public QThread
{
Q_OBJECT
public:
videothread(char*filename);
void run();
int getVideoAllFramecount();//获取视频总帧数
void setCurrentFrame(int value);//设置当前进度条
bool getStop() const;
void setStop(bool value);//设置视频结束标识
bool getIsrun() const;
void setIsrun(bool value);
void pauseThread();//暂停
void resumeThread();//继续
void stopThread();//停止
signals:
void sendFrame(int currentFrame,Mat frame);//当前帧和 帧数
private:
VideoCapture cap;//视频对象
Mat frame;
int currentFramecount;//视频当前帧数
int allFramecount;//总帧数
int fps;//视频帧率
int videoWriterFrame;//录制视频帧
bool stop;//线程结束标识位
bool isrun;//视频暂停标识位
};
#endif // VIDEOTHREAD_H
#include "videothread.h"
videothread::videothread(char*filename)
{
this->stop = false;
this->isrun =false;
this->currentFramecount=0;
this->videoWriterFrame=0;
if(cap.open(filename));//创建视频对象
{
this->allFramecount=cap.get(CAP_PROP_FRAME_COUNT);//获取视频文件中的总帧数
this->fps=int(round(cap.get(CAP_PROP_FPS)));//获取视频帧率
}
}
void videothread::run()
{
while(stop==false)//线程运行和停止 卡住线程 暂停时不退出线程
{
while(isrun==true)//视频运行和暂停
{
if(cap.read(frame))//捕获视频帧
{
this->currentFramecount++;
cvtColor(frame, frame, CV_BGR2RGB);//opencvBGR格式转成Image用到的RGB
emit sendFrame(currentFramecount,frame);//发送帧数据
}
msleep(40);//延时
}
}
cap.release();//释放打开的视频
}
int videothread::getVideoAllFramecount()
{
return allFramecount;
}
void videothread::setStop(bool value)
{
stop = value;
}
void videothread::setCurrentFrame(int value)
{
this->currentFramecount=value;//当前帧数
cap.set(CV_CAP_PROP_POS_FRAMES,currentFramecount);//进度条跳转对应帧
}
bool videothread::getIsrun() const
{
return isrun;
}
void videothread::setIsrun(bool value)
{
isrun = value;
}
void videothread::pauseThread()//这两个函数用于确保是在运行情况下才能切换状态
{
if(this->isRunning()&&this->isrun==true)//当前线程运行且视频运行
{
this->isrun=false;
}
}
void videothread::resumeThread()
{
if(this->isRunning()&&this->isrun==false)//当前线程运行且视频暂停
{
this->isrun=true;
}
}
void videothread::stopThread()
{
if(this->isRunning())//当前线程运行
{
this->stop=true;//结束线程
//msleep(10);
this->terminate();
}
}
bool videothread::getStop() const
{
return stop;
}
窗口类
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QSlider>
#include "videothread.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void paintEvent(QPaintEvent *e);
public slots:
void receiveFrame(int currentFrame,Mat frame);
private slots:
void on_pushButton_clicked();
void sliderClickedSlot();//点击进度条信号槽
void on_horizontalSlider_sliderReleased();
void on_horizontalSlider_sliderMoved(int position);
void on_pushButton_pause_clicked();
void on_pushButton_pause_2_clicked();
void on_pushButton_pause_3_clicked();
private:
Ui::Widget *ui;
videothread *pthread;
int receiveCurrentFramecount;
QImage img1;
bool isend;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
this->receiveCurrentFramecount=0;
this->isend=false;
ui->setupUi(this);
pthread=new videothread("carMove.mp4");
//第五个参数采用Qt::BlockingQueuedConnection 信号发出后发送线程阻塞,直到槽函数执行完毕,继续发送下一条信号
connect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)),Qt::BlockingQueuedConnection);//接收每一帧Mat
//connect(pthread,SIGNAL(endPlayVideo()),this,SLOT(setPlayEndSlot()),Qt::BlockingQueuedConnection);
connect(ui->horizontalSlider,SIGNAL(costomSliderClicked()),this,SLOT(sliderClickedSlot()));//点击进度条信号槽
ui->horizontalSlider->setRange(0,pthread->getVideoAllFramecount());//设置进度条取值范围
ui->horizontalSlider->setSingleStep(1);//设置单步长为1
ui->label_4->setText(QString::number(pthread->getVideoAllFramecount()));//设置总帧数
}
Widget::~Widget()
{
delete ui;
}
void Widget::receiveFrame(int currentFrame, Mat frame)
{
this->receiveCurrentFramecount=currentFrame;
this->img1 = QImage(frame.data,frame.cols,frame.rows,QImage::Format_RGB888);
this->img1 = this->img1.scaled(ui->label->width(),ui->label->height());
ui->horizontalSlider->setValue(this->receiveCurrentFramecount);//设置当前进度条取值
ui->label_2->setText(QString::number(this->receiveCurrentFramecount));//设置当前帧数
update();
}
void Widget::setPlayEndSlot()
{
pthread->setStop(true);//播放完毕 设置为true
}
void Widget::on_pushButton_clicked()
{
pthread->start();
pthread->setIsrun(true);//视频开始
}
void Widget::on_pushButton_pause_clicked()//暂停
{
if(pthread->getIsrun()==false&&pthread->isRunning())//暂停状态且线程运行
{
pthread->resumeThread();//修改为播放状态
ui->pushButton_pause->setText("暂停播放");
}
else if(pthread->getIsrun()==true&&pthread->isRunning())//播放状态
{
pthread->pauseThread();;//视频暂停
ui->pushButton_pause->setText("继续播放");
}
}
void Widget::paintEvent(QPaintEvent *e)
{
ui->label->setPixmap(QPixmap::fromImage(this->img1));
}
void Widget::sliderClickedSlot()//点击进度条 信号槽
{
//自定义鼠标点击信号,可以实现点哪 跳转到哪
qDebug()<<"点击了进度条";
pthread->pauseThread();//播放暂停
pthread->setCurrentFrame(ui->horizontalSlider->value());
pthread->resumeThread();//播放继续 设置为true
}
void Widget::on_horizontalSlider_sliderReleased()//释放进度条
{
//ui->horizontalSlider->value();获取当前进度条值
pthread->setCurrentFrame(ui->horizontalSlider->value());
pthread->resumeThread();//播放继续 设置为true
//此处一定要先设置进度条再开启线程,否则线程开启时再设置会冲突
}
void Widget::on_horizontalSlider_sliderMoved(int position)//进度条拖动
{
pthread->pauseThread();//播放暂停
}
void Widget::on_pushButton_pause_2_clicked()//结束播放
{
pthread->stopThread();//结束线程
qDebug()<<"结束播放,线程关闭";
if(!pthread->isRunning())//线程不在运行
{
if(this->isend!=true)//此时线程结束 已释放,就不再释放
{
//断开连接
disconnect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)));//接收每一帧Mat
delete pthread;
qDebug()<<"释放线程000000000000";
//this->pthread = NULL;
this->isend=true;//表明此时线程结束 已释放
//ui->pushButton_pause_2->setEnabled(false);
}
}
}
void Widget::on_pushButton_pause_3_clicked()//重新播放
{
pthread->stopThread();//结束线程
qDebug()<<"重新播放";
if(!pthread->isRunning())//线程不在运行
{
if(this->isend!=true)//此时线程结束 已释放,就不再释放
{
//断开连接
disconnect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)));//接收每一帧Mat
delete pthread;
qDebug()<<"重播111111111111111";
}
//创建新线程
pthread=new videothread("carMove.mp4");
pthread->start();
pthread->setIsrun(true);//视频开始
qDebug()<<"重播新创建线程333";
connect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)),Qt::BlockingQueuedConnection);//接收每一帧Mat
this->isend=false;//表明此时线程还未结束
//ui->pushButton_pause_2->setEnabled(true);
}
}
由于只是读取帧,所以该播放器只能播放画面,声音暂时还没做;
感谢观看!!!!
以上就是全部内容,如果对您有帮助,欢迎点赞评论,或者发现有哪里写错的,欢迎指正!