ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

QT 视频播放界面

2022-02-27 06:31:09  阅读:188  来源: 互联网

标签:视频 界面 QT img int 点击 线程 播放 QImage


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有