其他分享
首页 > 其他分享> > QT 视频播放界面

QT 视频播放界面

作者:互联网

1.播放视频 -----videoPthread

使用线程播放视频

void PlayThread::run()
{
while(cap.read(frame))         //----------循环 不断将cap读到的Mat数据存储到frame变量
{
QImage qImg=MatToQimage(frame);// -----------Mat数据到QImage图片的转换函数,opencv使用的图片类型是Mat,但是Mat不能直接显示在label上,所以需要一次转换
emit sendImg(qImg); //---------------一边播一边将图片传递给界面
//msleep(20);
}
}

此处插播 MatToQimage()函数的实现方式 参数:Mat类型数据,就是cap读到帧画面后存储的mat数据,返回值:转换后的QImage。

转换原理:我也不太了解,印象里是Mat图片数据是BGR通道分布,QImage是RGB通道分布,所以转换就是将B,G两个通道的数据交换(不知道是不是这样)

QImage playThread::changeToQImg(Mat img)
{
    QImage qImg;
    if (img.channels() == 3) {
        //cvtColor(img, img, CV_BGR2RGB);
        qImg = QImage((const unsigned char*)(img.data), img.cols, img.rows,
            img.cols * img.channels(), QImage::Format_RGB888);
    }
    else if (img.channels() == 1) {
        qImg = QImage((const unsigned char*)(img.data), img.cols, img.rows,
            img.cols * img.channels(), QImage::Format_Indexed8);
    }
    else {
        qImg = QImage((const unsigned char*)(img.data), img.cols, img.rows,
            img.cols * img.channels(), QImage::Format_RGB888);
    }
    return qImg;
}

 

2、线程发出的信号----------sendImg(QImage img)-------播放线程解出一帧就发送一帧

   界面上的槽-------------showImg(QImage showimg)-----------线程专递来图片,显示到界面的label上

   显示图片,窗口重绘函数,只更新label------paintEvent(QPaint xxxx)--------------使用update()调用重绘函数

  

void VideoPlayWin::paintEvent(QPaintEvent *e)    //----------------重绘事件
{
ui->label->setPixmap(QPixmap::fromImage(this->showimg));   //------------label上设置图片 setPixmap 但是设置上去的图片是Qimage,

                                QPixmap,Qimage这两个我也不知道有什么区别
ui->label->setScaledContents(true);      //设置图片自适应label大小
}

void VideoPlayWin::showImage(QImage showimg)//--------------界面上 接收图片的槽函数

{
this->showimg=showimg;   //---------------界面类的私有变量 this-<showimg, 等于传递来的showimg,然后调用update,使用重绘事件刷新界面,

                  等于每次传来一帧图片,就刷新一次label
this->update();
}

3、到此视频就可以在界面上播放,但是速度很快,所以需要在线程发送信号后msleep(25)

4、继续完善,界面需要能控制视频的暂停和播放

  bool  stopFalg;//------线程私有变量,bool类型判断是否被暂停

  void setStopFlag(bool value);//---------相应的,提供一个set 暂停位的函数,在线程之外的地方可以调用set函数修改flag的值,来控制run里解画面帧,

                   如果flag的值是true,就不要继续解码。

void PlayThread::run()
{
while(cap.read(frame))
{
while(stopFlag)//---------------stopFlag==true 标志位置1,说明点击了暂停。因为上面while(read)一直在不断解帧,所以这里要使用一个死循环,点击暂停就进到这个死循环,等于                   卡主上面read解码的流程,如果只是用if(stopFlag),当检测到暂停标志位之后,没有做死循环操作,确实是不会触发下面的emit信号,界面上的画面                         处于停止状态,但其实cap(read)还在偷偷的继续解码,等到我们点击继续播放,会发现视频已经播到后面去了。
{
qDebug()<<"stop"<<endl;
msleep(10);
}
QImage qImg=MatToQimage(frame);
emit sendImg(qImg);
//msleep(20);
}
}

界面点击按钮,修改stopFlag值,因为界面上按钮太多不够好,所以只设置一个按钮,那么如何确定哪一次点击是播放视频,哪一次点击是暂停线程。

所以,我们可以根据按钮上的文字进行判断,是要暂停还是播放,使用text()函数获取button上的文字。更改stopFlag后,记得也要把button上的文字修改掉,自然使用setText()

void VideoPlayWin::on_pushButton_clicked()  //----------点击暂停/播放按钮
{
if(ui->pushButton->text()=="播放")
{
this->videothread->start();
this->videothread->setIsSTop(false);
ui->pushButton->setText("暂停");
}
else if(ui->pushButton->text()=="暂停")
{
this->videothread->setIsSTop(true);
ui->pushButton->setText("播放");
}
//if number==biggest,btn can't clicked    这边测试过后发现,如果视频已经播完,这时候继续点击播放键已经无法打开视频了。

这种情况的话,就只好在播完视频之后,将视频直接关闭,按钮也回到播放状态。视频播完也就是线程的while(read)死循环结束,所以在线程里 发送一个结束信号,界面再响应该信号,这里先不处理,还要结合进度条的控制,后面再处理。
}

5、修改视频倍速

 此处变速框使用的是 下拉框 设计 先了解一下 QComBox的使用方式:

  5.1.currentIndex(); 获取当bai前ducomBox的索引,是int类型的值。//QComBox变速框第0个索引是0.5倍速,第1个索引值1倍速,第2个索引2倍速,第3个索引4倍速
  5.2.currentText(); 获取当前comBox的文本dao,是QString类型。
  5.3.currentData(int role = Qt::UserRole)获取当前comBox绑定的数据,是QVariant类型。

 所以,就不像暂停一样,在点击按钮的时候才改变线程里的值,我们在界面打开线程的时候,就可以将下拉框的索引值当做参数传递到线程中,然后在线程里写一个修改变速的函数

,switch(flag),直接打开线程,就在初始化的时候得到视频要显示的速度,然后在run函数里做msleep

  这里需要计算,视频的帧率,帧数,求得视频的时长

    rate = cap.get(5);      //获取帧率
    fraNum=cap.get(7);       //获取帧数
    duration=fraNum / rate;  //帧数/每x秒一帧=总共多少秒
    onePlayTime = 1000/rate; //msleep使用的是ms,所以再用1000/x秒,就能大概知道,视频帧和视频帧之间的延迟需要设置多长
  
那么默认播放速度就是按onePlayTime,如果设置了倍速=0。5倍,那么播放速度变慢,延迟时间就要长一点。
  speedTime=2*onePlayer;
代码:

PlayThread::PlayThread(QString pwd, int speedIndex)
{
cap.open(pwd.toLatin1().data());
rate = cap.get(5); //获取帧率
framNum=cap.get(7); //获取帧数
duration=framNum / rate; //帧数/每x秒一帧=总共多少秒
onePlayTime = 1000/rate; 
qDebug()<<"onePlayTime="<<onePlayTime<<endl;
changeSpeed(speedIndex);
//this->stopFlag=false;
}

计算切换倍速后播放速度的函数://switch语句每个分支一定要有break;否则就等于没有分支

void PlayThread::changeSpeed(int flag)
{
switch(flag)
{
case 0:
speedTime=2*onePlayTime;
break;
case 1:
speedTime=onePlayTime;
break;
case 2:
speedTime=onePlayTime/2;
break;
case 3:
speedTime=onePlayTime/4;
break;
default:
speedTime=onePlayTime;
}
}

界面上,点击下拉框的槽函数:右键点击comBox转到槽

void VideoPlayWin::on_comboBox_activated(int index)
{
this->videothread->changeSpeed(index);
}

 界面构造函数里调用线程的函数:ui->comboBox->currentIndex()就是下拉框的当前索引值

   videothread=new PlayThread(this->videopath,ui->comboBox->currentIndex()); 

  

6、实现进度条跟着视频移动

   6.1自定义控件进度条   自定义一个控制条可以实现点击进度条跳转视频吧

基本上就是根据点击的进度条坐标,求出占整条坐标的白百分比,百分比乘以进度条时长,就可以求得数字

 .h

#ifndef MYSLIDER_H
#define MYSLIDER_H

#include <QObject>
#include <QSlider>
#include <QWidget>

class MySlider : public QSlider
{
    Q_OBJECT
public:
    MySlider(QWidget *parent);
    MySlider(int min,int max,int single);

protected:
    void mousePressEvent(QMouseEvent *ev);

signals:
    void sendFrame(int,int);
    void sendOld(int);
    void sendSec(int);
public slots:
};

#endif // MYSLIDER_H

.cpp

#include "myslider.h"
#include <QDebug>
#include <QMouseEvent>


MySlider::MySlider(QWidget *parent): QSlider(parent)
{
    this->setMinimum(0);//最小值0
    this->setMaximum(0);//最大值100
    this->setSingleStep(1);//单步步长1
    this->setPageStep(0);//设置鼠标点击步长为0,触发点击事件
}

MySlider::MySlider(int min, int max, int single)
{
    this->setOrientation(Qt::Horizontal);  // 水平方向
    this->setMinimum(min);//最小值0
    this->setMaximum(max);//最大值100
    this->setSingleStep(single);//单步步长1
    this->setPageStep(0);//设置鼠标点击步长为0,触发点击事件
}

void MySlider::mousePressEvent(QMouseEvent *ev)
{
//        emit sendOld(this->value());
    //获取当前点击位置,得到的这个鼠标坐标是相对于当前QSlider的坐标
        int currentX = ev->pos().x();

        //获取当前点击的位置占整个Slider的百分比
        double per = currentX *1.0 /this->width();

        //利用算得的百分比得到具体数字
        int value = per*(this->maximum() - this->minimum()) + this->minimum();
//        emit sendFrame(this->value(),value);
        emit sendSec(value);
        qDebug()<<"当前帧是:"<<value;
        //设定滑动条位置
        this->setValue(value);

        //滑动条移动事件等事件也用到了mousePressEvent,加这句话是为了不对其产生影响,是的Slider能正常相应其他鼠标事件
        QSlider::mousePressEvent(ev);
}

6.2 播放线程添加一个私有变量 nowtime,表示了当前播放到的进度,就是在run函数的while(read)里进行++。每解出一帧照片,nowtime++
  emit sendNowTime(nowtime); 给界面传信号
  进度条根据输入的值移动 ui->horizontalSlider->setValue(nowtime);
  播放线程中有视频总帧数,将其设置为进度条的总长度,

6.3界面中编写拖动进度条的代码 但是带1个参,在服务里点击进度条,然后将数据传给视频播放线程
还有很多,下次再写

 

标签:视频,界面,QT,img,int,点击,线程,播放,QImage
来源: https://www.cnblogs.com/hhy-2216/p/15941423.html