OpenCV實(shí)現(xiàn)視頻播放器,其思路大致就是在線程中使用OpenCV中的VideoCapture循環(huán)讀取本地視頻的每一幀Mat,然后發(fā)送到界面轉(zhuǎn)換成QImage進(jìn)行顯示,而進(jìn)度條拖動則用到了VideoCapture中的set函數(shù),進(jìn)度條則是使用Qslider;并且通過自定義新的進(jìn)度條類實(shí)現(xiàn)點(diǎn)擊跳轉(zhuǎn)功能;
效果:
1.進(jìn)行播放,線程循環(huán)讀取視頻幀并計(jì)數(shù)當(dāng)前幀數(shù),把Mat幀和當(dāng)前幀數(shù)通過信號發(fā)送到窗口
窗口中創(chuàng)建線程,然后點(diǎn)擊播放時(shí)在調(diào)用start啟動線程;
void videothread::run()
{
while(stop==false)//線程運(yùn)行和停止 卡住線程 暫停時(shí)不退出線程
{
while(isrun==true)//視頻運(yùn)行和暫停
{
if(cap.read(frame))//捕獲視頻幀
{
this->currentFramecount++;
cvtColor(frame, frame, CV_BGR2RGB);//opencvBGR格式轉(zhuǎn)成Image用到的RGB
emit sendFrame(currentFramecount,frame);//發(fā)送幀數(shù)據(jù)
}
msleep(40);//延時(shí)
}
}
cap.release();//釋放打開的視頻
}
2.獲取當(dāng)前幀數(shù)設(shè)置進(jìn)度條當(dāng)前值
?3.通過VideoCapture中的get方法獲取視頻總幀數(shù)并保存
if(cap.open(filename));//創(chuàng)建視頻對象
{
this->allFramecount=cap.get(CAP_PROP_FRAME_COUNT);//獲取視頻文件中的總幀數(shù)
this->fps=int(round(cap.get(CAP_PROP_FPS)));//獲取視頻幀率
}
?4.設(shè)置進(jìn)度條取值范圍,即0-總幀數(shù)
ui->horizontalSlider->setRange(0,pthread->getVideoAllFramecount());//設(shè)置進(jìn)度條取值范圍
ui->horizontalSlider->setSingleStep(1);//設(shè)置單步長為1
ui->label_4->setText(QString::number(pthread->getVideoAllFramecount()));//設(shè)置總幀數(shù)
接下來就是進(jìn)度條拖動,在知道如何做之前,我們要先了解Qslider的幾個(gè)信號,我們拖動時(shí)就需要用到點(diǎn)擊、滑動和釋放信號
Qslider常用信號
1.移動滑動條時(shí)發(fā)出的信號
void sliderMove(int value);
2.點(diǎn)擊滑動條時(shí)所發(fā)出的信號
void sliderPressed();
?3.釋放時(shí)所發(fā)出的信號
void sliderReleased();
5.首先是拖動進(jìn)度條,拖動的時(shí)候先把播放的視頻暫停
就是停止循環(huán)讀幀
void Widget::on_horizontalSlider_sliderMoved(int position)//進(jìn)度條拖動
{
pthread->pauseThread();//播放暫停
}
void videothread::pauseThread()//這兩個(gè)函數(shù)用于確保是在運(yùn)行情況下才能切換狀態(tài)
{
if(this->isRunning()&&this->isrun==true)//當(dāng)前線程運(yùn)行且視頻運(yùn)行
{
this->isrun=false;
}
}
6.釋放進(jìn)度條
獲取拖動后的進(jìn)度條的值,然后通過set中的CV_CAP_PROP_POS_FRAMES進(jìn)行設(shè)置
void Widget::on_horizontalSlider_sliderReleased()//釋放進(jìn)度條
{
//ui->horizontalSlider->value();獲取當(dāng)前進(jìn)度條值
pthread->setCurrentFrame(ui->horizontalSlider->value());
pthread->resumeThread();//播放繼續(xù) 設(shè)置為true
}
void videothread::setCurrentFrame(int value)
{
this->currentFramecount=value;//當(dāng)前幀數(shù)
cap.set(CV_CAP_PROP_POS_FRAMES,currentFramecount);//進(jìn)度條跳轉(zhuǎn)對應(yīng)幀
}
7.點(diǎn)擊進(jìn)度條,重寫一個(gè)進(jìn)度條類
因?yàn)橄到y(tǒng)自帶的進(jìn)度條點(diǎn)擊時(shí)只能移動一小段,不能實(shí)現(xiàn)點(diǎn)哪就移動到哪,所以我們要自己重寫一個(gè)進(jìn)度條類,然后在ui中提升為重寫的進(jìn)度條
?文章來源地址http://www.zghlxwxcb.cn/news/detail-400448.html
完整源碼:
?自定義進(jìn)度條
#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();//自定義的鼠標(biāo)單擊信號,用于捕獲并處理
public slots:
};
#endif // NEWQSLIDER_H
#include "newqslider.h"
newqslider::newqslider(QWidget *parent) : QSlider(parent)
{
}
/*****************************************************************
* 函數(shù)名稱:mousePressEvent(QMouseEvent *ev)
* 功能描述:重寫鼠標(biāo)點(diǎn)擊事件,實(shí)現(xiàn)進(jìn)度條點(diǎn)擊哪跳到哪
* 參數(shù)說明: 無
* 返回值: 無
******************************************************************/
void newqslider::mousePressEvent(QMouseEvent *ev)
{
//先調(diào)用父類的鼠標(biāo)點(diǎn)擊處理事件,這樣可以不影響拖動的情況
QSlider::mousePressEvent(ev);
//獲取鼠標(biāo)的位置,這里并不能直接從ev中取值(因?yàn)槿绻峭蟿拥脑?,鼠?biāo)開始點(diǎn)擊的位置沒有意義了)
double pos = ev->pos().x() / (double)width();
setValue(pos * (maximum() - minimum()) + minimum());
//發(fā)送自定義的鼠標(biāo)單擊信號
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();//獲取視頻總幀數(shù)
void setCurrentFrame(int value);//設(shè)置當(dāng)前進(jìn)度條
bool getStop() const;
void setStop(bool value);//設(shè)置視頻結(jié)束標(biāo)識
bool getIsrun() const;
void setIsrun(bool value);
void pauseThread();//暫停
void resumeThread();//繼續(xù)
void stopThread();//停止
signals:
void sendFrame(int currentFrame,Mat frame);//當(dāng)前幀和 幀數(shù)
private:
VideoCapture cap;//視頻對象
Mat frame;
int currentFramecount;//視頻當(dāng)前幀數(shù)
int allFramecount;//總幀數(shù)
int fps;//視頻幀率
int videoWriterFrame;//錄制視頻幀
bool stop;//線程結(jié)束標(biāo)識位
bool isrun;//視頻暫停標(biāo)識位
};
#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));//創(chuàng)建視頻對象
{
this->allFramecount=cap.get(CAP_PROP_FRAME_COUNT);//獲取視頻文件中的總幀數(shù)
this->fps=int(round(cap.get(CAP_PROP_FPS)));//獲取視頻幀率
}
}
void videothread::run()
{
while(stop==false)//線程運(yùn)行和停止 卡住線程 暫停時(shí)不退出線程
{
while(isrun==true)//視頻運(yùn)行和暫停
{
if(cap.read(frame))//捕獲視頻幀
{
this->currentFramecount++;
cvtColor(frame, frame, CV_BGR2RGB);//opencvBGR格式轉(zhuǎn)成Image用到的RGB
emit sendFrame(currentFramecount,frame);//發(fā)送幀數(shù)據(jù)
}
msleep(40);//延時(shí)
}
}
cap.release();//釋放打開的視頻
}
int videothread::getVideoAllFramecount()
{
return allFramecount;
}
void videothread::setStop(bool value)
{
stop = value;
}
void videothread::setCurrentFrame(int value)
{
this->currentFramecount=value;//當(dāng)前幀數(shù)
cap.set(CV_CAP_PROP_POS_FRAMES,currentFramecount);//進(jìn)度條跳轉(zhuǎn)對應(yīng)幀
}
bool videothread::getIsrun() const
{
return isrun;
}
void videothread::setIsrun(bool value)
{
isrun = value;
}
void videothread::pauseThread()//這兩個(gè)函數(shù)用于確保是在運(yùn)行情況下才能切換狀態(tài)
{
if(this->isRunning()&&this->isrun==true)//當(dāng)前線程運(yùn)行且視頻運(yùn)行
{
this->isrun=false;
}
}
void videothread::resumeThread()
{
if(this->isRunning()&&this->isrun==false)//當(dāng)前線程運(yùn)行且視頻暫停
{
this->isrun=true;
}
}
void videothread::stopThread()
{
if(this->isRunning())//當(dāng)前線程運(yùn)行
{
this->stop=true;//結(jié)束線程
//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();//點(diǎn)擊進(jìn)度條信號槽
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");
//第五個(gè)參數(shù)采用Qt::BlockingQueuedConnection 信號發(fā)出后發(fā)送線程阻塞,直到槽函數(shù)執(zhí)行完畢,繼續(xù)發(fā)送下一條信號
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()));//點(diǎn)擊進(jìn)度條信號槽
ui->horizontalSlider->setRange(0,pthread->getVideoAllFramecount());//設(shè)置進(jìn)度條取值范圍
ui->horizontalSlider->setSingleStep(1);//設(shè)置單步長為1
ui->label_4->setText(QString::number(pthread->getVideoAllFramecount()));//設(shè)置總幀數(shù)
}
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);//設(shè)置當(dāng)前進(jìn)度條取值
ui->label_2->setText(QString::number(this->receiveCurrentFramecount));//設(shè)置當(dāng)前幀數(shù)
update();
}
void Widget::setPlayEndSlot()
{
pthread->setStop(true);//播放完畢 設(shè)置為true
}
void Widget::on_pushButton_clicked()
{
pthread->start();
pthread->setIsrun(true);//視頻開始
}
void Widget::on_pushButton_pause_clicked()//暫停
{
if(pthread->getIsrun()==false&&pthread->isRunning())//暫停狀態(tài)且線程運(yùn)行
{
pthread->resumeThread();//修改為播放狀態(tài)
ui->pushButton_pause->setText("暫停播放");
}
else if(pthread->getIsrun()==true&&pthread->isRunning())//播放狀態(tài)
{
pthread->pauseThread();;//視頻暫停
ui->pushButton_pause->setText("繼續(xù)播放");
}
}
void Widget::paintEvent(QPaintEvent *e)
{
ui->label->setPixmap(QPixmap::fromImage(this->img1));
}
void Widget::sliderClickedSlot()//點(diǎn)擊進(jìn)度條 信號槽
{
//自定義鼠標(biāo)點(diǎn)擊信號,可以實(shí)現(xiàn)點(diǎn)哪 跳轉(zhuǎn)到哪
qDebug()<<"點(diǎn)擊了進(jìn)度條";
pthread->pauseThread();//播放暫停
pthread->setCurrentFrame(ui->horizontalSlider->value());
pthread->resumeThread();//播放繼續(xù) 設(shè)置為true
}
void Widget::on_horizontalSlider_sliderReleased()//釋放進(jìn)度條
{
//ui->horizontalSlider->value();獲取當(dāng)前進(jìn)度條值
pthread->setCurrentFrame(ui->horizontalSlider->value());
pthread->resumeThread();//播放繼續(xù) 設(shè)置為true
//此處一定要先設(shè)置進(jìn)度條再開啟線程,否則線程開啟時(shí)再設(shè)置會沖突
}
void Widget::on_horizontalSlider_sliderMoved(int position)//進(jìn)度條拖動
{
pthread->pauseThread();//播放暫停
}
void Widget::on_pushButton_pause_2_clicked()//結(jié)束播放
{
pthread->stopThread();//結(jié)束線程
qDebug()<<"結(jié)束播放,線程關(guān)閉";
if(!pthread->isRunning())//線程不在運(yùn)行
{
if(this->isend!=true)//此時(shí)線程結(jié)束 已釋放,就不再釋放
{
//斷開連接
disconnect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)));//接收每一幀Mat
delete pthread;
qDebug()<<"釋放線程000000000000";
//this->pthread = NULL;
this->isend=true;//表明此時(shí)線程結(jié)束 已釋放
//ui->pushButton_pause_2->setEnabled(false);
}
}
}
void Widget::on_pushButton_pause_3_clicked()//重新播放
{
pthread->stopThread();//結(jié)束線程
qDebug()<<"重新播放";
if(!pthread->isRunning())//線程不在運(yùn)行
{
if(this->isend!=true)//此時(shí)線程結(jié)束 已釋放,就不再釋放
{
//斷開連接
disconnect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)));//接收每一幀Mat
delete pthread;
qDebug()<<"重播111111111111111";
}
//創(chuàng)建新線程
pthread=new videothread("carMove.mp4");
pthread->start();
pthread->setIsrun(true);//視頻開始
qDebug()<<"重播新創(chuàng)建線程333";
connect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)),Qt::BlockingQueuedConnection);//接收每一幀Mat
this->isend=false;//表明此時(shí)線程還未結(jié)束
//ui->pushButton_pause_2->setEnabled(true);
}
}
?由于只是讀取幀,所以該播放器只能播放畫面,聲音暫時(shí)還沒做;
感謝觀看?。。?!
以上就是全部內(nèi)容,如果對您有幫助,歡迎點(diǎn)贊評論,或者發(fā)現(xiàn)有哪里寫錯(cuò)的,歡迎指正!
文章來源:http://www.zghlxwxcb.cn/news/detail-400448.html
?
到了這里,關(guān)于【OpenCV+Qt】實(shí)現(xiàn)簡易視頻播放器——支持進(jìn)度條拖動的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!