opencv+qt实现简易视频播放器——支持进度条拖动(代码片段)

logani logani     2023-04-03     640

关键词:

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常用信号

1.移动滑动条时发出的信号

void sliderMove(int value);

2.点击滑动条时所发出的信号

void sliderPressed();

 3.释放时所发出的信号

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);
    



 由于只是读取帧,所以该播放器只能播放画面,声音暂时还没做;

感谢观看!!!!

以上就是全部内容,如果对您有帮助,欢迎点赞评论,或者发现有哪里写错的,欢迎指正!

 

视频控制的简易进度条

视频控制的简易进度条样式:作用:控制视频的播放点,实时显示视频播放位置 html:<divclass="coll"><spanname="progress"><b></b><b></b><b></b></span></div>css:.coll{position:abs 查看详情

qt视频播放器[qmediaplayer+qvideowidget](代码片段)

...装K-Lite解码器二、Qt代码结构VideoPlayer.promain.cppvideoplayer.h播放器videoplayer.cpp播放器videoplayer.ui播放器playerslider.h自定义进度条playerslider.cpp自定义进度条效果源码文件错误解决参考Qt实现视频播放器Qt播放视频报错DirectShowPlayerService::... 查看详情

基于libmpv内核设计开发的视频播放器-高级版(代码片段)

...已经对libmpv做了详细的介绍,第二篇文章里完成了完整的播放器开发,设计了UI,实现了播放器的基本功能。这一篇里对libmpv完成功能扩展,增加进度条预览图的支持(鼠标放在进度条上弹出预览窗口),支持视频章节解析渲染... 查看详情

基于libmpv内核设计开发的视频播放器-高级版(代码片段)

...已经对libmpv做了详细的介绍,第二篇文章里完成了完整的播放器开发,设计了UI,实现了播放器的基本功能。这一篇里对libmpv完成功能扩展,增加进度条预览图的支持(鼠标放在进度条上弹出预览窗口),支持视频章节解析渲染... 查看详情

qt学习笔记——①进度条可拖动点击②有暂停按钮的视频播放器(代码片段)

一、配置文件更改用到多媒体的话,需要修改配置文件的第一行,后面加一句sqlmultimediamultimediawidgets修改后的配置文件前3行如下QT+=coreguisqlmultimediamultimediawidgets//下面都不用改greaterThan(QT_MAJOR_VERSION,4):QT+=widgetsC 查看详情

js代码实现视频进度条点到哪个位置就播放那个位置的视频。进度条是用css样式另做的。

...追问谢谢本回答被提问者采纳 参考技术B用currentTime可以实现追问怎么写求代码 参考技术C我觉得你应该先把video标签的js内置方法和属性先看一下追问用currentTime是不是就可以实现大神求指导 查看详情

基于pyqt5的qmediaplayer实现视频播放器(拨动进度条,音量,更换播放模式,加入多个播放文件)(代码片段)

前言最近在学习pytq5的QMediaPlayer模块,其实刚开始是学习的pyside6的,因为pyside6的官方文档相对较详细一些,pyqt5的官方文档大片的文档解释都是TODO,我不太明白这种句式,但是做到添加播放列表QMediaPlaylist的... 查看详情

qt第三方圆形进度条-及其改进

Qt第三方圆形进度条的改进要实现一个圆形的进度条功能,在网上找到一个比较出名的第三方封装类:QRoundProgressBar,地址:sourceforge的QRoundProgressBar 功能封装的还是不错,提供了3种模式,线形、圆环、饼状。使用过程中发现... 查看详情

vs2008中slider控件制作播放器的进度条,如何在视频播放时改变滑块位置

现在改变滑块位置来选择视频播放位置的功能已经实现,但是不知道在视频播放时怎么来实现滑块随视频播放在进度条上前进刚看了,就是SetPor(intpos);该函数可以定位Slider的任意位置。首先要用SetRange()实现Slider的最大值与最小... 查看详情

在vue项目中使用video.js实现视频播放和视频进度条打点(代码片段)

...;</template>引入videojs插件importvideojsfrom"video.js";//播放器中文,不能使用.js文件importvideozhCNfrom"video.js/dist/lang/zh-CN.json";//样式文件注意要加上import"video.js/dist/video-js.css";//如果要播放RTMP要使用flash需要先npmivi... 查看详情

ffmpeg+qt视频进度条控制——点击跳转和拖动跳转(代码片段)

...条采用Qslider,设置进度条主要有两点,一是当前视频总时长,二是当前播放时长,需要通过FFmpeg转码成mp4文件才能获取相应的时长数据;往期回顾:【Qt+FFmpeg】视频转码详细流程_logani的博客-CSDN博客_qt&... 查看详情

HTML5 视频播放器进度条准确提示

】HTML5视频播放器进度条准确提示【英文标题】:HTML5VideoPlayerProgressBarAccurateTooltip【发布时间】:2021-05-2622:14:38【问题描述】:大多数视频播放器允许您单击进度条跳转到视频中的特定点,方法是使用鼠标在进度条上的位置来粗... 查看详情

利用mediaplayer实现在线音频的播放(支持进度条拖动快进快退)(代码片段)

效果图:项目GitHub地址https://github.com/chenjianqiang1016/MyNewAudioDemo下载项目后,运行起来,会发现音频无法播放,是因为,我项目中读音的链接是audioUrl="https://...test.mp3"之前调试的音频存放链接,指向... 查看详情

视频播放器——简介篇

从这篇开始,详细讲解一下视频播放器的内容。由于在过程中,只是使用了surfaceView+MediaPlayer来播放视频,并没有使用软解码什么的,所以可能就只支持Android本身支持的视频格式。当然,也遗留了很多问题&... 查看详情

unity视频播放器,基于videoplayer,实现滑动进度条,显示视频时长,全屏显示,音量调节

...增加一个大的容易点击的播放按钮,易于点击 如果播放器的适配模式为自适应, 查看详情

qt学习笔记——实现①从本地选择播放源②连接数据库③带有播放列表的视频播放器(代码片段)

...;六)——①进度条可拖动、点击②有暂停按钮的视频播放器的基础上构建。2.因为实现的功能有点多,所以讲解的思路和前几篇不太一样了,先不把UI的设计一股脑儿列出来,而是一个功能一个功能的介绍。3.实现... 查看详情

mediaplayer音乐播放器上一首下一首播放停止自动下一首进度条

本文介绍MediaPlayer的使用。MediaPlayer可以播放音频和视频,另外也可以通过VideoView来播放视频,虽然VideoView比MediaPlayer简单易用,但定制性不如用MediaPlayer,要视情况选择了。MediaPlayer播放音频比较简单,但是要播放视频就需要Surfa... 查看详情

b站进度条上面的波浪怎么关

...频播放页面,点击设置。在展开的选项中可以看到有FLASH播放器,点击选择它,选择FLASH播放器后,进度条就能够自动隐藏了。参考技术A视频进入全屏播放,点右下角的齿轮(设置),有一个高能进度条选项,取消勾选波浪就没... 查看详情