基于QMediaPlayerQt+数据库实现的视频播放器

基于QMediaPlayerQt+数据库实现的视频播放器+实现了抓取帧的操作

预览

在这里插入图片描述
在这里插入图片描述

功能

基本功能

  1. 实现本地视频的播放
  2. 实现文件列表,显示添加到播放器的视频文件
  3. 实现收藏列表,可以点击收藏按钮可以收藏指定视频
  4. 实现收藏列表,可以显示收藏的视频
  5. 播放,暂停,继续播放,倍速,重新加载,删除该视频
  6. 设计抓取按钮,播放过程中可以抓取当前视频的帧图像,并自动保存该图像相关信息
  7. 设计进度条,实时显示以播放时长与视频总时长
  8. 数据库管理数据

具体设计

1. 存储视频信息的数据库设计

  • SQLite 是一个轻量级的本地数据库,它非常适合小型应用的开发

视频名称

视频地址

视频是否被收藏

2. 选择合适的视频解码器

  • Qt 中的多媒体播放,底层是使用DirectShowPlayerService,所以安装一个DirectShow解码器,例如LAV Filters,或者k-lite解码器,就可以解决运行出错问题
  1. 第一次选择LAV (卡顿, 花屏)

  2. 第二次选择K-lite(较好)

  3. 两款软件只能二选一,推荐选择:K-Lite。

    原因:两款软件均采用Lav 算法,但LAV Filters 软件效果较差,总容易出现花屏现象。卸载LAV Filters,再安装K-Lite后,问题解决,视频流利播放。就实际使用来说k-lite更流畅,不容易出现花屏与卡死

3. ui设计

  • 播放视频列表窗口

    • 所有视频列表
    • 收藏视频列表
  • 播放窗口

  • 按钮组窗口

  • 进度条窗口

4. 添加文件

5. 添加文件到数据库

  • 数据库表格式:文件名称 + 文件地址 + 是否收藏

5. 视频列表窗口

  • 将数据库中的文件显示到视频列表中

6. 从文件列表中播放视频

  • 双击视频列表中的视频文件,将会自动播放视频

7. 播放与暂停

  • 相互切换

8. 删除视频

  1. 删除所有视频列表中的视频,同时在数据库中删除
  2. 取消收藏收藏列表中的某些视频

9. 倍速

  • 调整视频播放速度(点击按钮视频加速)

10. 抓取当前播放视频的帧图像

  • 点击该功能按钮的时候,获取当前播放视频的当前帧的图片

  • 实现方法:

  1. 通过继承虚基类QAbstractVideoSurface ,子类中实现presentsupportedPixelFormats方法
  2. 当点击抓帧按钮的时候,切换当前视频输出流到VedioSurface 对象中,获取present 发出信号表示获取到当前帧
  3. 主函数中保存当前帧的图像及相关信息

11. 进度条

  • 声音

  • 进度

    同时还要显示视频总时间与剩余时间:

    • 进度条右边设置两个文本框,一个用来显示当前时间,一个用来显示总视频时长

12. 收藏按钮

  • 点击按钮将当前视频对应数据项修改为收藏,表示收藏该视频,在收藏列表中显示

gitee地址

gitee地址

部分代码

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QFileDialog>
#include <QTime>
#include <QVideoFrame>
#include <QAbstractVideoSurface>
#include <qabstractvideobuffer.h>
#include <QPainter>



Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 实例化
    my_video_player = new QMediaPlayer;
    my_video_list = new QMediaPlaylist;
    my_video_widget = new QVideoWidget(ui->play_label);
    my_videosurface = new VideoSurface;

    // 设置播放者要播放的播放列表以及播放到的窗口
    my_video_player->setPlaylist(my_video_list);
    my_video_player->setVideoOutput(my_video_widget);

    // 设置播放窗口的大小
    my_video_widget->resize(ui->play_label->size());

    // 设置初始音量
    my_video_player->setVolume(50);
    ui->music_horizontalSlider_2->setValue(50);

    // 设置视频列表与收藏列表
    ui->viedo_tabWidget->setTabText(0, "所有视频");
    ui->viedo_tabWidget->setTabText(1, "我的收藏");
    ui->viedo_tabWidget->setCurrentIndex(0);

    setWindowTitle("视频播放器");
    resize(1024, 768);

    // 美化视频进度条
    ui->progress_horizontalSlider->setStyleSheet("QSlider::groove:horizontal {                            \
                           height: 8px;                                             \
                           background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,  \
                                                       stop: 0 rgb(124, 124, 124),  \
                                                       stop: 1.0 rgb(72, 71, 71));} \
                           QSlider::handle:horizontal {                             \
                           border-image: url(:/image/飞船.png);                      \
                           width: 40px;                                             \
                           height:40px;                                             \
                           margin: -8px px -8px -3px;}                              \
                           QSlider::sub-page:horizontal{                            \
                           background:rgba(255, 85, 127,1);}                        \
                           ");

    // 添加数据库
    //  SQLite 是一个轻量级的本地数据库,它非常适合小型应用的开发
    db = QSqlDatabase::addDatabase("QSQLITE");     //添加sqlite驱动
    db.setDatabaseName("myVediodatabase.db");  // 设置表名

    // 打开数据库连接,执行数据库操作,添加属性是否收藏了该视频
    db.open();
    QString mlist = "create table mlist(filename text,path text,loved bool);";
    db.exec(mlist);  // 创建数据库表

    QString sql = "select * from mlist;";
    QSqlQuery query;
    if(query.exec(sql)){
        qDebug()<<"显示列表";
    }
    else{
        qDebug()<<"显示失败";
    }

    // 默认添加数据库中的数据信息到视频列表中,同时将数据库数据添加到播放列表中
    while(query.next()){
        QString file_name = QString(query.value("filename").toString() );       //转换类型
        QString path = QString( query.value("path").toString() );       //转换类型
        bool loved = bool(query.value("loved").toBool());


        // 如果该视频被收藏同时 添加到收藏列表
        if (loved){
            ui->love_listWidget_2->addItem(file_name);
        }

        ui->videoslistWidget->addItem(file_name);


        my_video_list->addMedia(QUrl(path));
    }

    // 播放收藏列表中的视频
    connect(ui->love_listWidget_2, &QListWidget::doubleClicked, this, [&](){
        is_start = true;
        QString filename;
        //选择文件播放
        QListWidgetItem* item = ui->love_listWidget_2->currentItem();  //获取当前item
        filename = item->text();
        now_play_vedio = filename;

        // 查询该文件是否在数据库中
        QString sql = QString("select * from mlist where filename='%1';").arg(filename);
        QSqlQuery query;
        // 查询该视频数据是否在数据库中
        if(query.exec(sql)){
            qDebug()<<"查询成功";
        }
        else {
            qDebug()<<"查询失败";
        }
        if(query.next()){
            qDebug()<<"登录成功";
            filename = query.value("path").toString();  // 找到的文件的路径
        }
        else{
            qDebug()<<"登录失败";
        }
        // 播放者播放的文件设置为当前查询到的文件
        my_video_player->setMedia(QUrl(filename));

        qDebug()<<"name:"<<filename;

        // 播放视频
        my_video_player->play();
    });
    // 如果双击视频列表中的某一个视频,直接播放该视频
    connect(ui->videoslistWidget, &QListWidget::doubleClicked, this, [&](){
        QString filename;
        //选择文件播放
        QListWidgetItem* item = ui->videoslistWidget->currentItem();  //获取当前item
        filename = item->text();
        now_play_vedio = filename;

        // 查询该文件是否在数据库中
        QString sql = QString("select * from mlist where filename='%1';").arg(filename);
        QSqlQuery query;
        // 查询该视频数据是否在数据库中
        if(query.exec(sql)){
            qDebug()<<"查询成功";
        }
        else {
            qDebug()<<"查询失败";
        }
        if(query.next()){
            qDebug()<<"登录成功";
            filename = query.value("path").toString();  // 找到的文件的路径
        }
        else{
            qDebug()<<"登录失败";
        }
        // 播放者播放的文件设置为当前查询到的文件
        my_video_player->setMedia(QUrl(filename));

        qDebug()<<"name:"<<filename;

        // 播放视频
        is_start = true;
        ui->play_pause_pushButton->setStyleSheet("QPushButton#play_pause_pushButton{border-image:url"
                                                  "(:/image/暂停.png)}");
        my_video_player->play();
    });

    // 视频进度改变,进度条改变
    connect(my_video_player, &QMediaPlayer::positionChanged, this, &Widget::updatePosition);
    // 切换到其他视频时,进度条长度改变,同时视频总时长也改变
    connect(my_video_player, &QMediaPlayer::durationChanged, this, &Widget::updateDuration);

}

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

void Widget::paintEvent(QPaintEvent *event){
    my_video_widget->resize(ui->play_label->size());

    // 加载背景
    QPainter painter(this);

    QPixmap pix;
    pix.load(":/image/background1.jpeg");

    painter.setOpacity(0.7);//透明度设置
    painter.drawPixmap(0,0,this->width(),this->height(),pix);


}


void Widget::on_openFile_pushButton_clicked()
{
    /* 打开文件功能,获取要打开的视频列表 */
    QString curPash = "D:/桌面";
    QString dlgTitle="选择视频文件";
    QString filter="mp4文件(*.mp4);;mp3文件(*.mp3);;flv文件(*.flv);;avi文件(*.avi)";
    QStringList fileList = QFileDialog::getOpenFileNames(this,dlgTitle,curPash,filter);

    qDebug() << fileList;

    // 把导入的文件加入到显示视频列表的框中
    db.open();
    for(int i=0; i<fileList.count(); i++)
    {
        //将选择的文件显示在文本框上
        QString path = fileList.at(i);
        QString name = path.mid(path.lastIndexOf("/")+1,path.lastIndexOf(".")-1-path.lastIndexOf("/"));

        // 防止同一个视频在数据库中添加多次
        QSqlQuery query;
        QString sql_find_vedio = QString("select * from mlist where filename='%1';").arg(name);
        query.exec(sql_find_vedio);
        if(query.next()){  // 判断视频数据库中是否存在该视频
            qDebug()<<"数据库已经存在该数据";
            continue;
        }

        // 向数据库插入数据
        QString sql = QString("insert into mlist values('%1','%2','%3');").arg(name).arg(path).arg(false);
        qDebug()<<sql;
        if (query.exec(sql)){
            qDebug() << "插入数据库成功";
        }
        else{
            qDebug()<<"插入数据库失败";
            continue;
        }


        ui->videoslistWidget->addItem(name);
    }
}

void Widget::on_play_pause_pushButton_clicked()
{
    /* 视频播放与暂停 */
    // 如果当前视频正在播放,点击后会暂停
    if (is_start){
        my_video_player->pause();
        is_start = false;
        // 变换图标
        ui->play_pause_pushButton->setStyleSheet("QPushButton#play_pause_pushButton{border-image:url"
                                                  "(:/image/播放.png)}");
    }
    else{
        my_video_player->play();
        is_start = true;
        ui->play_pause_pushButton->setStyleSheet("QPushButton#play_pause_pushButton{border-image:url"
                                                  "(:/image/暂停.png)}");
    }

}


void Widget::on_stop_pushButton_clicked()
{
    /* 重写加载视频 */
    my_video_player->stop();
    my_video_player->play();

}

void Widget::onShowImage(QVideoFrame frame)
{
    /* 获取当前帧的槽函数 */
    // 保存图片
    my_video_player->pause();
    // 将视频帧数据映射到内存空间
    frame.map(QAbstractVideoBuffer::ReadOnly);

    QImage image(
        frame.bits(),
        frame.width(),
        frame.height(),
        frame.bytesPerLine(),
        QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat())
    );
    QTime nowtime;
    nowtime = QTime::currentTime();
    QString savepixname = "D:\\桌面\\get_images\\" + nowtime.toString("hh-mm-ss-zzz") + ".jpg";
    image.save(savepixname);

    frame.unmap();

    // 获取当前播放位置
    my_video_player->setPosition(my_video_player->position());
    my_video_player->setVideoOutput(my_video_widget);
    // 断开本次链接
    disconnect(my_videosurface, &VideoSurface::showImage, this, &Widget::onShowImage);

    my_video_player->play();
}


void Widget::on_getimage_pushButton_clicked()
{
    /* 抓取当前视频的帧图像 */
    // 点击截屏按钮的时候,将my_Mediaplayer流输出到vediosurface中,用于截屏保存
    //qint64 m_nPausePos = my_video_player->position();
    connect(my_videosurface, &VideoSurface::showImage, this, &Widget::onShowImage);

    // 暂停当前播放视频保存视频位置
    my_video_player->pause();

    qint64 now_position = my_video_player->position();

    // 修改输出设备
    my_video_player->setVideoOutput(my_videosurface);  // 将输出设备设置为vediosurface
    my_video_player->setPosition(now_position);
    my_video_player->play();

}

void Widget::on_del_pushButton_3_clicked()
{
    my_video_player->stop();
    ui->play_pause_pushButton->setStyleSheet("QPushButton#play_pause_pushButton{border-image:url"
                                              "(:/image/播放.png)}");
    QString filename;

    QString sql;
    QSqlQuery query;

    /* 判断当前在视频列表页还是收藏列表列,删除对应的视频 */
    if (ui->viedo_tabWidget->currentIndex() == 0){
        QListWidgetItem* item = ui->videoslistWidget->currentItem();  //获取当前item
        filename = item->text();

        sql = QString("delete from mlist where filename='%1';").arg(filename);
        if(query.exec(sql)){
            qDebug()<<"删除成功";
        }
        else {
            qDebug()<<"删除失败";
        }
        delete item;
    }
    // 删除收藏的视频,但是该视频还在数据库中(取消收藏)
    else if (ui->viedo_tabWidget->currentIndex() == 1){
        QListWidgetItem* item = ui->love_listWidget_2->currentItem();  //获取当前item
        QSqlQuery query;
        QString sql_find_vedio = QString("UPDATE mlist SET loved='%1' WHERE filename='%2';").arg(false).arg(item->text());
        query.exec(sql_find_vedio);
        delete item;
    }

}

void Widget::on_music_horizontalSlider_2_valueChanged(int value)
{
    /* 改变声音大小 */
    my_video_player->setVolume(value);
}

void Widget::on_progress_horizontalSlider_valueChanged(int value)
{
    /* 拖动进度条,视频进度改变 */
    my_video_player->setPosition(qint64(value));
}

void Widget::updateDuration(qint64 duration)
{
    /* 更新当前视频进度 */
    ui->totaltime_label->setText(QString("%1:%2").arg(duration/1000/60,2,10,QChar('0')).arg(duration/1000%60));
    ui->progress_horizontalSlider->setRange(0, duration);
    ui->progress_horizontalSlider->setEnabled(duration > 0);
    ui->progress_horizontalSlider->setPageStep(duration / 10);
}


void Widget::updatePosition(qint64 position)
{
    ui->progress_horizontalSlider->setValue(position);
    // 设置当前时间
    QTime duration(0, position / 60000, qRound((position % 60000) / 1000.0));
    ui->curtime_label->setText(duration.toString(tr("mm:ss")) + "/ ");
}



void Widget::on_love_pushButton_clicked()
{
    /* 点击按钮收藏喜欢的视频 */
    // 点击收藏按钮可以收藏当前播放的视频,并且在收藏列表中添加
    // 在数据库中查找该视频对应项,修改为喜欢该视频,然后添加到收藏列表中
    QSqlQuery query;
    QString sql_find_vedio = QString("UPDATE mlist SET loved='%1' WHERE filename='%2';").arg(true).arg(now_play_vedio);
    query.exec(sql_find_vedio);

    ui->love_listWidget_2->addItem(now_play_vedio);
}

void Widget::on_speed_pushButton_clicked()
{
    /* 点击按钮视频二倍速播放,再次点击视频原速播放 */
    if (is_speed){
        ui->speed_pushButton->setStyleSheet("QPushButton#speed_pushButton{border-image:url"
                                                  "(:/image/加速.png)}");
        my_video_player->setPlaybackRate(1);
        is_speed = false;
    }
    else{
        ui->speed_pushButton->setStyleSheet("QPushButton#speed_pushButton{border-image:url"
                                                  "(:/image/减速.png)}");
        my_video_player->setPlaybackRate(2);
        is_speed = true;
    }
}

改进

  • 美化ui

    进度条美化

  • 抓取帧后将数据信息保存完整,保存到一个指定文件夹中

  • bug

    1. 删除视频列表中视频后收藏列表中还有该视频

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值