C++ OpenCV制作视频播放器

本文档详细介绍了使用Qt实现的视频播放器的功能,包括开始、暂停、结束、重新播放、录制视频等操作,并提供了相关源码。视频播放器支持进度条拖拽,点击暂停可停留在某一帧,录制视频并保存。源码中展示了窗口类和视频线程类的实现细节,通过QThread实现视频播放和录制的线程控制。
摘要由CSDN通过智能技术生成

目录

一:视频播放效果展示

二:完整源码分享


一:视频播放效果展示

1.1 点击开始播放按钮,能够播放视频

1.2 点击暂停播放 视频停在某一帧画面 同时按钮文本改变为继续播放

      以便用户继续播放视频 提高用户使用体验感

点击暂停 可以停在某一帧画面

点击继续 可以继续播放视频

1.3 点击结束播放 就不再播放视频 同时结束播放按钮不可以再次点击

1.4 点击重新播放 可以播放新的一个视频

1.5 点击开始录制,从当前帧画面开始录制,并将录制视频保存在自己配置的文件夹目录下

点击录制,录制300ms

去自己配置文件夹下,可以查看到自己刚才录制下来的视频 并且双击打开能够正常播放

1.6 点击中断录制,可以随时停止录制视频

点击中断录制 如下图中断后只录制69ms

同样能够在自己配置的文件夹目录下 查看到 自己中断录制前所录制下来的视频

1.7 进度条拖拽 视频画面也跟随进度条拖拽进度进行更新播放

经过以上的测试,大致完成了本次的视频播放器的基本业务流程

二:完整源码分享

Qt  UI界面设计

源码分享

窗口类

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QImage>
#include<QDebug>
#include<opencv2/opencv.hpp>
#include"videothread.h"

using namespace cv;
using namespace std;

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void paintEvent(QPaintEvent *arg);
    void videoinit(char *path);

private:
    Ui::Widget *ui;
    QImage img;
    VideoThread *thread;//线程对象
public slots:
    void ChangeData(int current,Mat frame);
private slots:
    void on_startButton_clicked();//开始播放
    void on_pauseButton_clicked();//结束
    void on_stopButton_clicked();//暂停
    void on_replayButton_clicked();//重新播放
    void on_startRecordButton_clicked();//开始录制
    void on_stopRecordButton_clicked();//中断录制
    void on_horizontalSlider_sliderPressed();//按下进度条
    void on_horizontalSlider_sliderReleased();//松开进度条
};

#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    videoinit("D:/000000000000000ffmpeg/那些年,我们一起追的女孩.mp4");
}

Widget::~Widget()
{
    delete ui;
}

void Widget::videoinit(char *path)
{
    //线程播放视频
    this->thread = new VideoThread(path);
    //进度条的初始化
    ui->horizontalSlider->setMinimum(0);
    ui->horizontalSlider->setMaximum(this->thread->getTotalpage());
    //进度条拖动范围
    ui->horizontalSlider->setRange(0,this->thread->getTotalpage());
    //进度条步长
    ui->horizontalSlider->setSingleStep(1);
    ui->horizontalSlider->setValue(0);

    //label初始化
    ui->totalPage->setText(QString::number(this->thread->getTotalpage()));
    ui->currentPage->setText(QString::number(0));

    connect(this->thread,SIGNAL(send2UI(int,Mat)),this,SLOT(ChangeData(int,Mat)),Qt::BlockingQueuedConnection);
}

void Widget::paintEvent(QPaintEvent *arg)
{
    //视频播放显示
    ui->label->setPixmap(QPixmap::fromImage(this->img));
}

void Widget::ChangeData(int current, Mat frame)
{
    this->img = QImage(frame.data,frame.cols,frame.rows,QImage::Format_RGB888);
    this->img = this->img.scaled(ui->label->width(),ui->label->height());

    ui->currentPage->setText(QString::number(current));
    ui->horizontalSlider->setValue(current);
    this->update();
}

void Widget::on_startButton_clicked()
{
    this->thread->start();
    this->thread->setIsRun(true);
    qDebug()<<"thread start";
}

void Widget::on_pauseButton_clicked()
{
    //如果当前的线程没有在执行
    if(this->thread->getIsRun() == false)
    {
        //继续执行线程
        this->thread->resumeThread();
        ui->pauseButton->setText("暂停播放");
    }
    else
    {
        //点击暂停播放视频的按钮
        this->thread->pauseThread();
        //要设置可继续播放视频的按钮
        ui->pauseButton->setText("继续播放");
    }
}

//点击结束播放
void Widget::on_stopButton_clicked()
{
    this->thread->stopThread();

    if(this->thread->isRunning() == false && this->thread != NULL)
    {
        //解除信号槽的绑定
        disconnect(this->thread,SIGNAL(send2UI(int,Mat)),
                   this,SLOT(ChangeData(int,Mat)));
        delete this->thread;
        this->thread = NULL;
        //设置不可以点击
        ui->stopButton->setEnabled(false);
    }
}

//重新播放
void Widget::on_replayButton_clicked()
{
    this->thread->stopThread();
//    QThread::sleep(1);
    if(this->thread->isRunning() == false)
    {
        //解除信号槽的绑定
        disconnect(this->thread,SIGNAL(send2UI(int,Mat)),
                   this,SLOT(ChangeData(int,Mat)));
        delete this->thread;

        //播放新的一个视频
        videoinit("D:/000000000000000ffmpeg/中国合伙人.avi");
        this->thread->start();
        this->thread->setIsRun(true);
    }
}

//开始录制
void Widget::on_startRecordButton_clicked()
{
    this->thread->setIsRecord(true);
}

//中断录制
void Widget::on_stopRecordButton_clicked()
{
    this->thread->setIsRecord(false);
}

//进度条按下
void Widget::on_horizontalSlider_sliderPressed()
{
    //暂停当前线程
    this->thread->pauseThread();

}

//进度条释放
void Widget::on_horizontalSlider_sliderReleased()
{
    //改变当前帧进度
    this->thread->setCurrentpage(ui->horizontalSlider->value());
    //继续线程
    this->thread->resumeThread();
}

视频线程类

#ifndef VIDEOTHREAD_H
#define VIDEOTHREAD_H
#include<QThread>
#include<QDebug>
#include<QString>
#include<QDateTime>
#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

class VideoThread : public QThread
{
    Q_OBJECT
public:
    VideoThread(char *path);
    //总帧数
    int getTotalpage() const;
    void setTotalpage(int value);
    //当前帧进度
    int getCurrentpage() const;
    void setCurrentpage(int value);
    //录制
    bool getIsRecord() const;
    void setIsRecord(bool value);
    void run();//运行
    void pauseThread();//暂停
    void resumeThread();//继续
    void stopThread();//停止
    bool getIsRun() const;
    void setIsRun(bool value);

private:
    VideoCapture cap;//视频对象
    VideoWriter writer;//写视频对象
    Mat frame;//当前每一帧的画面  一帧画面
    int totalpage;//视频总帧数
    int currentpage;//视频正在播放的当前帧数
    int pagenum;//录制的帧数
    int frame_width;//帧宽
    int frame_height;//帧高
    //暂停方法:1.锁对象QMutex 2.条件变量 3.标志位bool 是线程暂停
    bool isRun;//控线线程暂停
    bool isStop;//控制结束
    bool isRecord;//控制线程什么时候开始/结束录制
signals:
    void send2UI(int current,Mat frame);
};

#endif // VIDEOTHREAD_H
#include "videothread.h"

VideoThread::VideoThread(char *path):QThread()
{
    if(this->cap.open(path))
    {
        qDebug()<<"Video is open";
        //cap对象创建成功 路径成功 就可获取到视频总帧数
        this->totalpage = cap.get(CV_CAP_PROP_FRAME_COUNT);
        qDebug()<<"Video totalpage ="<<this->totalpage;
        //帧宽 帧高 的获取
        this->frame_width = cap.get(CV_CAP_PROP_FRAME_WIDTH);
        this->frame_height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
    }
    //当前帧
    this->currentpage = 0;
    //录制视频帧
    this->pagenum = 0;
    //运行
    this->isRun = false;
    //结束
    this->isStop = false;
    //录制
    this->isRecord = false;
}

int VideoThread::getTotalpage() const
{
    return totalpage;
}

void VideoThread::setTotalpage(int value)
{
    totalpage = value;
}

int VideoThread::getCurrentpage() const
{
    return currentpage;
}

void VideoThread::setCurrentpage(int value)
{
    currentpage = value;
    //拖动进度条 改变画面
    this->cap.set(CV_CAP_PROP_POS_FRAMES,this->currentpage);
}

bool VideoThread::getIsRecord() const
{
    return isRecord;
}

void VideoThread::setIsRecord(bool value)
{
    isRecord = value;
    //开始录制
    if(this->isRecord == true)
    {
        QDateTime current_date_time = QDateTime::currentDateTime();

        QString filename = "./video/";
        filename += current_date_time.toString("yyyy_MM_dd_hh_mm_ss");
        filename += ".avi";
        qDebug()<<"filename = "<<filename;

        this->writer.open(filename.toStdString(),//视频文件名
                          VideoWriter::fourcc('M','J','P','G'),//编码类型
                          25,//帧率
                          Size(this->frame_width,this->frame_height),//尺寸大小
                          true);//彩色格式为true 灰度为false
    }
}

void VideoThread::run()
{
    //控制线程停止
    while (this->isStop == false)
    {
        //控线线程的暂停和继续
        if(this->isRun == true)
        {
            //如果读到一帧
            if(cap.read(frame))
            {
                //当前帧数递增
                this->currentpage++;
                cvtColor(frame,frame,CV_BGR2RGB);
                emit send2UI(this->currentpage,frame);

                //开始录制
                if(this->isRecord == true)
                {
                    cvtColor(frame,frame,CV_BGR2RGB);
                    //录制300ms
                    if(this->pagenum<300)
                    {
                        this->writer.write(frame);
                        this->pagenum++;
                        qDebug()<<"Record pagenum = "<<pagenum;
                    }
                    else
                    {
                        //录制保存
                        this->writer.release();
                        //释放置零
                        this->pagenum = 0;
                        this->isRecord = false;
                    }
                }
                else{
                    //录制保存
                    this->writer.release();
                    this->pagenum = 0;
                }
            }
            //播放设置1ms 25帧
            msleep(40);
        }
    }
    cap.release();
    qDebug()<<"thread move away";
}

//暂停视频播放
void VideoThread::pauseThread()
{
     qDebug()<<"pauseThread ";
     //如果当前线程正在执行
     if(QThread::isRunning()&&this->isRun == true)
     {
          this->isRun = false;
     }
}

//继续播放
void VideoThread::resumeThread()
{
    qDebug()<<"resumeThread ";
    //如果当前线程正在执行
    if(QThread::isRunning()&&this->isRun == false)
    {
         this->isRun = true;
    }
}

//结束播放
void VideoThread::stopThread()
{
    qDebug()<<"stopThread";
    //如果当前线程正在执行
    if(QThread::isRunning()&&this->isStop == false)
    {
          this->isStop = true;
          QThread::quit();
          QThread::wait();
          QThread::exit();
//        QThread::terminate();
    }
}

bool VideoThread::getIsRun() const
{
    return isRun;
}

void VideoThread::setIsRun(bool value)
{
    isRun = value;
}

主入口测试

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

以上就是本次视频播放器的全部制作流程啦!欢迎一起学习!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenruhan_QAQ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值