最近心血来潮突然想自己实现一个播放器的功能,说来过程还有丢丢曲折的,一开始是用
QtMultimedia 来实现的,结果发现需要安装解码器,所以又跑去安装了 K-Lite。然后功能都可以使用了,自己就屁颠屁颠的打包成exe放到另一台没有安装解码器的电脑上去运行,结果不能播放视频- -。最后一通搜索,决定使用QTAv来实现。
该实现最大的好处就是打包后可以放到另一台不用搭环境的win上正常运行~
先上贴图:
大致功能点说明:
1: 可打开单个文件,多个文件,和遍历文件夹
2:实现视频文件拖拽到窗口播放,文件拖拽到exe上,直接打开exe和播放文件
3:记录播放历史,上一次设置的音量
4:进程最小化,进程最大化,全屏
5:点击画面可暂停/继续播放
6:音量slider浮动显示,倍速上拉选
7: 快进,上一集,暂停/继续播放,下一集,快退,倍速,音量,截图
8:历史记录可删,双击历史记录播放
9:视频进度条点击,拖拽跳转
上主程序代码(备注还是挺详细的,毕竟QT我也不熟)~
全部源码下载:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget){
ui->setupUi(this);
Widgets::registerRenderers();
showList = false;
timer = new QTimer(this);
setObjectName("myWiget");
setWindowTitle("播放器");
setWindowIcon(QIcon(":/resources/myLogo.ico"));
setStyleSheet("#myWiget{border-image:url(:/resources/backImg.png);}");
setContentsMargins(0,0,0,0);
resize(1280,720);
//实例化布局
this->vbox = new QVBoxLayout(this);// 上下排列布局
vbox->setContentsMargins(0,0,0,0);
// **********************************************************************************初始化管理栏***************
this->hboxTop = new QHBoxLayout(this);
hboxTop->setContentsMargins(0,0,0,0);
bar = new QMenuBar();
bar->setStyleSheet("margin: 2px 0;width:50px;background-color:rgba(0,0,0,0);color:white;");//透明背景色
bar->setAutoFillBackground(false);
bar->setMinimumWidth(70);
bar->setMaximumWidth(70);
// 一级菜单
file = bar->addMenu("文件");
// file->setStyleSheet("background-color:none;");//background-color:green;
file->setMinimumWidth(150);
file->setAutoFillBackground(false);
QList<QAction*> list;
QAction *action1=new QAction("打开文件");//QAction(QIcon(":/resource/image/向右箭头.jpg"),"action1"); 可显示图标
QAction *action2=new QAction("多个文件");
QAction *action3=new QAction("打开文件夹");
QAction *action4=new QAction("退出");
list.append(action1);
list.append(action2);
list.append(action3);
list.append(action4);
file->addActions(list);
// file->addMenu("打开文件");// 下级菜单
hboxTop->addWidget(bar);
fileVal = new QLabel();
fileVal->setStyleSheet("color:white;");
fileVal->setText("播放的文件名..........");
fileVal->setAlignment(Qt::AlignCenter);// 文字 居中
hboxTop->addWidget(fileVal);
hideButton = new QPushButton();
QPixmap pi = QPixmap(":/resources/hide.png");
hideButton->setStyleSheet("background-color:rgba(0,0,0,0);");
QIcon btnBack = QIcon(pi);
hideButton->setIcon(btnBack);
QSize s = QSize(50,25);
hideButton->setIconSize(s);
hideButton->setMaximumWidth(50);
hboxTop->addWidget(hideButton);
maxButton = new QPushButton();
maxButton->setStyleSheet("background-color:rgba(0,0,0,0);");
QPixmap maxpi = QPixmap(":/resources/maxPro.png");
QIcon maxbtnBack = QIcon(maxpi);
maxButton->setIcon(maxbtnBack);
QSize s2 = QSize(50,25);
maxButton->setIconSize(s2);
maxButton->setMaximumWidth(50);
maxButton->setMinimumWidth(50);
hboxTop->addWidget(maxButton);
closeButton = new QPushButton();
// closeButton->setStyleSheet("background-color:rgba(0,0,0,0);");
QPixmap closepi = QPixmap(":/resources/closePro.png");
QIcon closebtnBack = QIcon(closepi);
closeButton->setIcon(closebtnBack);
QSize s3 = QSize(50,25);
closeButton->setIconSize(s3);
closeButton->setMaximumWidth(50);
closeButton->setMinimumWidth(50);
hboxTop->addWidget(closeButton);
// **********************************************************************************初始化播放栏***************
QWidget* video = new QWidget(this);// 使用wiget来实现大小自适应,QHBoxLayout只能被动的被子组件撑起来
video->setContentsMargins(0,0,0,0);
this->hboxVideo = new QHBoxLayout(this);
hboxVideo->setContentsMargins(0,0,0,0);
//实例化媒体播放器及视频播放
mOut = new VideoOutput(this);;
mPlayer = new AVPlayer(this);
mPlayer->setRenderer(mOut);
// 默认实时流 连接方式是UDP模式,可能出现丢包,卡顿等,改为tcp模式
QHash<QString, QVariant> dict;
dict.insert("rtsp_transport", "tcp");
mPlayer->setOptionsForFormat(dict);
// 开启解码加速
mPlayer->setVideoDecoderPriority(QStringList() << "CUDA" << "D3D11" << "DXVA" << "FFmpeg");//"VAAPI"
// 新增一个大小随显示屏走的button,且设置为透明的
QVBoxLayout* gbox = new QVBoxLayout(mOut->widget());
b2 = new QPushButton(this);
QPixmap statpi2 = QPixmap(":/resources/stat2.png");
QIcon statbtnBack2 = QIcon(statpi2);
b2->setIcon(statbtnBack2);
b2->setIconSize(QSize(0,0));
b2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
b2->setStyleSheet("background-color:rgba(0,0,0,0);");
gbox->addWidget(b2);
mOut->widget()->setLayout(gbox);
hboxVideo->addWidget(mOut->widget());
// hboxVideo->setStretchFactor(videoWidget,1);// 设置该控件为可伸缩,其他控件自动设置为不可伸缩
showListButton = new QPushButton(this);
showListButton->setMaximumWidth(20);
// 设置宽度固定,高度随父组件大小
showListButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
showListButton->setStyleSheet("border-image:url(:/resources/showList.png);");
hboxVideo->addWidget(showListButton);
// 历史列表
listWidget = new QListWidget(this);
// listWidget->setStyleSheet("background-color:white;color:black;");
listWidget->setMaximumWidth(270);
listWidget->setMinimumWidth(270);
hboxVideo->addWidget(listWidget);
video->setLayout(hboxVideo);
// **********************************************************************************初始化视频时间显示栏***************
this->hboxVideoTime = new QHBoxLayout(this);
hboxVideoTime->setContentsMargins(0,0,0,0);
timeLable = new QLabel("00:00:00/00:00:00",this);
timeLable->setStyleSheet("color:white;");//margin-bottom:3px;
hboxVideoTime->addWidget(timeLable);
// **********************************************************************************初始化视频进度条显示栏***************
this->hboxVideoIndex = new QHBoxLayout(this);
hboxVideoIndex->setContentsMargins(0,0,0,0);
timeSlider = new QSlider(this);
timeSlider->setContentsMargins(0,0,0,0);
// groove表示槽的部分,handle表示滑块,add-page表示未滑过的槽部分,sub-page 表示已滑过的槽部分
// 横向展示
timeSlider->setOrientation(Qt::Horizontal);// Vertical
QString timeSliderStr = "";//"margin:3px;background-color:white;";
// 主体样式(样式中注意 horizontal 和 vertical)
timeSliderStr += "QSlider{border-color: #bcbcbc;}QSlider::groove:horizontal {border: 1px solid #999999;height: 6px;"
"left: 5px; right: 5px;}";// 左右留出一定位置,这样进度键才能显示全
// 滑块样式
timeSliderStr.append("QSlider::handle:horizontal {border: 0px;border-image: url(:/resources/rate.png);"
"width: 13px;margin: 0px -7px 0px -7px;}");
// 已划过槽的样式
timeSliderStr.append("QSlider::sub-page:horizontal {background-color:#87CEEB;height: 8px;}");//padding: 2px 0;
timeSlider->setStyleSheet(timeSliderStr);
timeSlider->installEventFilter(this);// 重载eventFilter事件
hboxVideoIndex->addWidget(timeSlider);
// **********************************************************************************初始化视频操作显示栏***************
this->hboxVideoWork = new QHBoxLayout(this);
hboxVideoWork->setContentsMargins(0,0,0,0);
// 布局:间隔/组件1/间隔/组件2
hboxVideoWork->addStretch();
btnLeft = new QLabel(" ");
btnLeft->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
hboxVideoWork->addWidget(btnLeft);
hboxVideoWork->addStretch();
backButton = new QPushButton();
backButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap backpi = QPixmap(":/resources/back.png");
QIcon backbtnBack = QIcon(backpi);
backButton->setIcon(backbtnBack);
QSize s4 = QSize(85,38);
backButton->setIconSize(s4);
backButton->setMaximumWidth(90);
backButton->setMinimumWidth(90);
backButton->setMinimumHeight(50);
backButton->setMaximumHeight(50);
hboxVideoWork->addWidget(backButton);
lastButton = new QPushButton();
lastButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap lastpi = QPixmap(":/resources/last.png");
QIcon lastbtnBack = QIcon(lastpi);
lastButton->setIcon(lastbtnBack);
QSize s5 = QSize(85,38);
lastButton->setIconSize(s5);
lastButton->setMaximumWidth(90);
lastButton->setMinimumWidth(90);
lastButton->setMinimumHeight(50);
lastButton->setMaximumHeight(50);
hboxVideoWork->addWidget(lastButton);
statButton = new QPushButton();
statButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap statpi = QPixmap(":/resources/stat.png");
statbtnBack = QIcon(statpi);
QPixmap stoppi = QPixmap(":/resources/stop.png");
stopbtnBack = QIcon(stoppi);
statButton->setIcon(stopbtnBack);
QSize s6 = QSize(110,45);
statButton->setIconSize(s6);
statButton->setMaximumWidth(90);
statButton->setMinimumWidth(90);
statButton->setMinimumHeight(60);
statButton->setMaximumHeight(60);
hboxVideoWork->addWidget(statButton);
nextButton = new QPushButton();
nextButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap nextpi = QPixmap(":/resources/next.png");
QIcon nextbtnBack = QIcon(nextpi);
nextButton->setIcon(nextbtnBack);
QSize s7 = QSize(85,38);
nextButton->setIconSize(s7);
nextButton->setMaximumWidth(90);
nextButton->setMinimumWidth(90);
nextButton->setMinimumHeight(50);
nextButton->setMaximumHeight(50);
hboxVideoWork->addWidget(nextButton);
upButton = new QPushButton();
upButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap uppi = QPixmap(":/resources/up.png");
QIcon upbtnBack = QIcon(uppi);
upButton->setIcon(upbtnBack);
upButton->setIconSize(QSize(85,38));
upButton->setMaximumWidth(90);
upButton->setMinimumWidth(90);
upButton->setMinimumHeight(50);
upButton->setMaximumHeight(50);
hboxVideoWork->addWidget(upButton);
hboxVideoWork->addStretch();
// 倍速按钮
boxBtn = new myBoxButton(this);
boxBtn->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);color:white;");
QStringList l;
l<<"2.0x"<<"1.5x"<<"1.3x"<<"1.0x"<<"0.8x"<<"0.5x"<<"0.3x";
boxBtn->addtextItems(l);
boxBtn->setText("1.0x");
hboxVideoWork->addWidget(boxBtn);
// 音量按钮
soundButton = new QPushButton();
soundButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap soundpi = QPixmap(":/resources/sound.png");
soundbtnBack = QIcon(soundpi);
QPixmap unsoundpi = QPixmap(":/resources/unsound.png");
unsoundbtnBack = QIcon(unsoundpi);
soundButton->setIcon(soundbtnBack);
soundButton->setIconSize(QSize(45,45));
soundButton->setMaximumWidth(50);
soundButton->setMinimumWidth(50);
soundButton->setMinimumHeight(50);
soundButton->setMaximumHeight(50);
soundButton->installEventFilter(this);
hboxVideoWork->addWidget(soundButton);
volumeSlider = new myFloatSlider(this);
// groove表示槽的部分,handle表示滑块,add-page表示未滑过的槽部分,sub-page 表示已滑过的槽部分
// 纵向展示
volumeSlider->setOrientation(Qt::Vertical);
// volumeSlider->setInvertedAppearance(true);// 反向
volumeSlider->installEventFilter(this);// 重载eventFilter事件
volumeSlider->setRange(0,100);
volumeSlider->setValue(100);
volumeSlider->hide();
// 最大化按钮
tomaxButton = new QPushButton();
tomaxButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap toMaxpi = QPixmap(":/resources/max.png");
QIcon toMaxbtnBack = QIcon(toMaxpi);
tomaxButton->setIcon(toMaxbtnBack);
tomaxButton->setIconSize(QSize(70,40));
tomaxButton->setMaximumWidth(60);
tomaxButton->setMinimumWidth(60);
tomaxButton->setMinimumHeight(50);
tomaxButton->setMaximumHeight(50);
hboxVideoWork->addWidget(tomaxButton);
// 截图按钮
cutButton = new QPushButton();
cutButton->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
QPixmap cutpi = QPixmap(":/resources/cut.png");
QIcon cutbtnBack = QIcon(cutpi);
cutButton->setIcon(cutbtnBack);
cutButton->setIconSize(QSize(70,40));
cutButton->setMaximumWidth(60);
cutButton->setMinimumWidth(60);
cutButton->setMinimumHeight(50);
cutButton->setMaximumHeight(50);
hboxVideoWork->addWidget(cutButton);
// 添加一个label,使得btn不贴边
btnRight = new QLabel(" ");
btnRight->setStyleSheet("margin:0 0 5px 0;background-color:rgba(0,0,0,0);");
hboxVideoWork->addWidget(btnRight);
// hboxVideoWork->setAlignment(Qt::AlignCenter);// 所有组件居中,没有设置间隔的时候,如果要组件居中,可以用这个方式
// 从上到下把横向容器放到竖向容器中
vbox->addLayout(hboxTop);
vbox->addWidget(video);
vbox->addLayout(hboxVideoTime);
vbox->addLayout(hboxVideoIndex);
vbox->addLayout(hboxVideoWork);
vbox->setStretchFactor(video,1);
// 设置窗口的容器为竖向容器
setLayout(this->vbox);
connect(showListButton,SIGNAL(clicked()),this,SLOT(showListFc())); //绑定历史列表显隐事件
connect(this->listWidget,SIGNAL(itemDoubleClicked(QListWidgetItem*)),this,SLOT(doubleClickPlay(QListWidgetItem*)));//历史列表双击事件
connect(this->timer,SIGNAL(timeout()),this,SLOT(updateTime())); //显示已播放时间/总时间
connect(this->mPlayer,SIGNAL(durationChanged(qint64)),this,SLOT(setTimeSlider(qint64)));// 总时长变化
connect(b2,SIGNAL(clicked()),this,SLOT(playerClick()));// 屏幕点击
connect(timeSlider,SIGNAL(valueChanged(int)),this,SLOT(timeChanged(int)));
connect(volumeSlider,SIGNAL(valueChanged(int)),this,SLOT(volumeChanged(int)));
connect(backButton,SIGNAL(clicked()),this,SLOT(backFc())); //回退事件
connect(lastButton,SIGNAL(clicked()),this,SLOT(lastFc())); //上一个视频事件
connect(statButton,SIGNAL(clicked()),this,SLOT(statFc())); //暂停/继续事件
connect(nextButton,SIGNAL(clicked()),this,SLOT(nextFc())); //下一个视频事件
connect(upButton,SIGNAL(clicked()),this,SLOT(upFc())); //快进事件
connect(action1,SIGNAL(triggered()),this,SLOT(openFile())); //打开单个文件事件
connect(action2,SIGNAL(triggered()),this,SLOT(openFiles())); //打开多个文件事件
connect(action3,SIGNAL(triggered()),this,SLOT(openDir())); //打开文件夹事件
connect(action4,SIGNAL(triggered()),this,SLOT(closePro())); //退出进程事件
connect(soundButton,SIGNAL(clicked()),this,SLOT(openOrCloseVolume())); //开启或关闭音量
connect(tomaxButton,SIGNAL(clicked()),this,SLOT(tomax())); //开启或关闭音量
connect(hideButton,SIGNAL(clicked()),this,SLOT(hidePro())); //最小化按钮
connect(maxButton,SIGNAL(clicked()),this,SLOT(maxPro())); //程序最大化按钮
connect(closeButton,SIGNAL(clicked()),this,SLOT(closePro())); //退出进程按钮
connect(cutButton,SIGNAL(clicked()),this,SLOT(cutPro())); // 截屏按钮
connect(mPlayer->videoCapture(), SIGNAL(imageCaptured(QImage)), SLOT(saveImage(QImage)));//视频图片抓取成功信号
connect(boxBtn,SIGNAL(currentTextChanged(QString)),this,SLOT(selectSpeed(QString)));// 倍速切换事件
// 初始化历史列表
showListFc();
// 支持悬浮
setAttribute(Qt::WA_Hover);
// 支持键盘事件
setFocusPolicy(Qt::StrongFocus);
// 每0.5s更新一次时间进度
timer->start(700);
// 接收文件放下事件
setAcceptDrops(true);
// 初始化和加载文件地址
data = new QFile(QApplication::applicationDirPath() + "/data");
if(data->open(QIODevice::ReadWrite|QIODevice::Text)){
QTextCodec *codec = QTextCodec::codecForName("GBK");
QByteArray datas = data->readAll();
oldStr = codec->toUnicode(datas);//QString(datas);
QJsonParseError jsonError;
QJsonDocument jspnDoc = QJsonDocument::fromJson(oldStr.toStdString().c_str(),&jsonError);
if (jspnDoc.isEmpty() || (jsonError.error != QJsonParseError::NoError)) {
qDebug()<<"文件内容json反转失败!清空文件数据.....";
nowVolume = 100;
data->close();
}else{
QJsonObject jsonObj = jspnDoc.object();
li = jsonObj.value("list").toArray();
nowVolume = jsonObj.value("volume").toInt();
volumeSlider->setValue(nowVolume);
mPlayer->audio()->setVolume(nowVolume/100.0);
for(int i=0;i<li.size();i++){
QJsonObject fileInfo = li.at(i).toObject();
QString url = fileInfo.value("url").toString();
int nameIndex = url.lastIndexOf("/");
if(nameIndex == -1){
nameIndex = url.lastIndexOf("\\");
}
QString name = url.mid(nameIndex+1);
QListWidgetItem *item = new QListWidgetItem(name,listWidget);
item->setData(Qt::ToolTipRole,url);
item->setText(name);
// listWidget->addItem(item);
QWidget *w = new QWidget(listWidget);
QHBoxLayout *layout=new QHBoxLayout(w);
layout->setAlignment(Qt::AlignRight);
QPushButton *pushButton=new QPushButton("x",w);
pushButton->setMaximumWidth(20);
pushButton->setMinimumWidth(20);
pushButton->setMinimumHeight(20);
pushButton->setMaximumHeight(20);
layout->addStretch();
layout->addWidget(pushButton);
item->setSizeHint(QSize(20,35));
w->show();
w->setLayout(layout);
listWidget->setItemWidget(item,w);
widgetItemMap[pushButton] = item;
connect(pushButton,SIGNAL(clicked()),this,SLOT(delItem())); //删除项
}
data->close();
}
}else{
qDebug()<<"创建文件失败!!";
}
}
Widget::~Widget(){
saveData();
timer->stop();
// mPlayer->stop();
// delete mPlayer;
delete ui;
}
void Widget::dropEvent(QDropEvent *e){
int first = true;
foreach (const QUrl &url, e->mimeData()->urls()){
//加载视频文件
QString urlStr = url.url();
if(first){
first = false;
mPlayer->play(urlStr);
}
if(urlStr.endsWith(".mkv") || urlStr.endsWith(".mp4")
|| urlStr.endsWith(".mp3") || urlStr.endsWith(".avi")){
if(oldStr.contains(urlStr)){
continue;
}
oldStr.append(";"+urlStr);
int nameIndex = urlStr.lastIndexOf("/");
QString name = urlStr.mid(nameIndex+1);
QListWidgetItem *item = new QListWidgetItem(name,this->listWidget);
item->setData(Qt::ToolTipRole,urlStr);
item->setText(name);
fileVal->setText(name);
QWidget *w = new QWidget(listWidget);
QHBoxLayout *layout=new QHBoxLayout(w);
layout->setAlignment(Qt::AlignRight);
QPushButton *pushButton=new QPushButton("x",w);
pushButton->setMaximumWidth(20);
pushButton->setMinimumWidth(20);
pushButton->setMinimumHeight(20);
pushButton->setMaximumHeight(20);
layout->addStretch();
layout->addWidget(pushButton);
item->setSizeHint(QSize(20,35));
w->show();
w->setLayout(layout);
listWidget->setItemWidget(item,w);
widgetItemMap[pushButton] = item;
connect(pushButton,SIGNAL(clicked()),this,SLOT(delItem())); //删除项
QJsonObject fileInfoObj;
fileInfoObj.insert("url",urlStr);
li.append(fileInfoObj);
}
}
}
void Widget::dragEnterEvent(QDragEnterEvent *e){
if (e->mimeData()->hasUrls()){
e->acceptProposedAction();
}
}
void Widget::delItem(){
QObject* widget = sender();
if(widget->inherits("QPushButton")){// 判断类型
QPushButton* btn = static_cast<QPushButton*>(widget);
QObject* p = btn->parent();
QListWidgetItem* item = widgetItemMap[btn];
oldStr.replace(item->toolTip(),"");
for(int i=0;i<li.size();i++){
QJsonValue v = li[i];
if(v.toObject().value("url") == item->toolTip()){
li.removeAt(i);
break;
}
}
QJsonObject dataJson;
dataJson.insert("list",li);
if(data->open(QIODevice::ReadWrite|QIODevice::Text | QIODevice::Truncate)){
QJsonDocument doc(dataJson);
QString strRet = QString(doc.toJson(QJsonDocument::Indented));
QTextStream ins(data);
ins<<strRet<<endl;
data->close();
}
delete btn;
delete item;
widgetItemMap.remove(btn);
}else{
qDebug()<<":is not QPushButton=";
}
}
void Widget::showListFc(){
if(!showList){
showList = true;
listWidget->show();
showListButton->setStyleSheet("border-image:url(:/resources/hideList.png);");
}else{
showList = false;
listWidget->hide();
showListButton->setStyleSheet("border-image:url(:/resources/showList.png);");
}
}
void Widget::doubleClickPlay(QListWidgetItem *item){
/*
* 1、获取listWidget中下标位置
* 2、设置媒体源、播放选中的歌曲
* 3、修改名字
* 4、修改图标
* 5、定时器启动
*/
int inde = this->listWidget->currentRow(); //获取当前下标
QString url = item->data(Qt::ToolTipRole).toString();
fileVal->setText(item->text());
mPlayer->play(url);
}
void Widget::updateTime(){
if(mPlayer->state() == AVPlayer::State::PlayingState || mPlayer->state() == AVPlayer::State::PausedState){
// 播放或暂停状态
//获取总时间跟当前播放时长
qint64 dur = this->mPlayer->duration();
qint64 pos = this->mPlayer->position();
//实例化两个对象,用来显示
QTime durTime(0,0,0);
QTime posTime(0,0,0);
this->timeLable->setText(posTime.addMSecs(int(pos)).toString("HH:mm:ss") + "/" + durTime.addMSecs(int(dur)).toString("HH:mm:ss"));
timeSlider->setValue(pos);
}
}
void Widget::setTimeSlider(qint64 duration){
this->timeSlider->setRange(0,int(duration));
}
void Widget::videoPosChange(qint64 duration){
timeSlider->setValue(duration);
mPlayer->setPosition(duration);
}
void Widget::timeChanged(int value){
}
void Widget::volumeChanged(int value){
volumeSlider->setValue(value);
if(mPlayer->state() == AVPlayer::State::PlayingState || mPlayer->state() == AVPlayer::State::PausedState){
// 范围:0-1
mPlayer->audio()->setVolume(value/100.0);
}
nowVolume = value;
}
bool Widget::eventFilter(QObject *obj, QEvent *e){
if (e->type() == QEvent::MouseButtonPress && obj == this->timeSlider){
timeSliderClick(obj, e);
// 这里不能返回true,不然按住滑块的拖动事件就终止了
// return true;
}else if(e->type() == QEvent::MouseButtonPress && obj == this->volumeSlider){
volumeSliderClick(obj, e);
}else if(e->type() == QEvent::HoverEnter && obj == this->soundButton){
QPushButton* soundBtn = static_cast<QPushButton*>(obj);
int x = soundBtn->x();
int y = soundBtn->y();
// if(volumeSlider->isHidden()){
volumeSlider->show();
volumeSlider->move(x,y-250);
volumeSlider->resize(40,270);
// }
inVolumeSlider = false;
return false;
}else if(e->type() == QEvent::HoverLeave && obj == this->soundButton){
// 异步
QtConcurrent::run([=](){
QThread::sleep(1);
// 一秒后确认是否已进入音量滑动条,没有的话则隐藏音量滑动条
if(!inVolumeSlider){
volumeSlider->hide();
}
});
}else if(e->type() == QEvent::HoverEnter && obj == this->volumeSlider){
inVolumeSlider = true;
volumeSlider->show();
}else if(e->type() == QEvent::HoverLeave && obj == this->volumeSlider){
inVolumeSlider = false;
volumeSlider->hide();
}
return QWidget::eventFilter(obj, e);//返回默认的事件过滤器
}
void Widget::paintEvent(QPaintEvent* event){
QStyleOption opt;
opt.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void Widget::volumeSliderClick(QObject *slider, QEvent *event){
QSlider* sliderCtrl = static_cast<QSlider*>(slider);
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
if (Qt::LeftButton == mouseEvent->button()){
int mxpos = 0; // 鼠标按下的位置
int cxwnd = 0;// 滑槽长度
int cxctl = 0;// 滑块宽度
// 竖向进度条
cxctl = sliderCtrl->minimumSizeHint().height(); // 滑块宽度
cxwnd = sliderCtrl->height();
if (sliderCtrl->invertedAppearance()){
mxpos = mouseEvent->y();
}else{
mxpos = cxwnd - mouseEvent->y();
}
// 计算结果,并设置新计算得到的 position 值
int scpos = sliderCtrl->minimum() +
(int)((sliderCtrl->maximum() - sliderCtrl->minimum()) *
((mxpos - cxctl / 2.0) / (cxwnd - cxctl)));
if(sliderCtrl->sliderPosition() != scpos){
sliderCtrl->setSliderPosition(scpos);
mPlayer->audio()->setVolume(scpos/100.0);
}
if(sliderCtrl->value() <= 0){
soundButton->setIcon(unsoundbtnBack);
}else{
soundButton->setIcon(soundbtnBack);
}
}
}
void Widget::timeSliderClick(QObject *slider, QEvent *event){
QSlider* sliderCtrl = static_cast<QSlider*>(slider);
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
// mouseEvent的x(),y() 可得知鼠标事件指针相对当前窗口的位置; (globalX()、globalY() 返回的是全局坐标)
if (Qt::LeftButton == mouseEvent->button()){
int mxpos = 0; // 鼠标按下的位置
int cxwnd = 0;// 滑槽长度
int cxctl = 0;// 滑块宽度
if (Qt::Horizontal == sliderCtrl->orientation()){
// 横向进度条
cxwnd = sliderCtrl->width();
if (sliderCtrl->invertedAppearance()){
// 判断进度条是否反转类型(即最大最小值相反)
mxpos = cxwnd - mouseEvent->x();
}else{
mxpos = mouseEvent->x();
}
}else{
// 竖向进度条
cxctl = sliderCtrl->minimumSizeHint().height(); // 滑块宽度
cxwnd = sliderCtrl->height();
if (sliderCtrl->invertedAppearance()){
mxpos = mouseEvent->y();
}else{
mxpos = cxwnd - mouseEvent->y();
}
}
// 计算结果,并设置新计算得到的 position 值
int scpos = sliderCtrl->minimum() +
(int)((sliderCtrl->maximum() - sliderCtrl->minimum()) *
((mxpos - cxctl / 2.0) / (cxwnd - cxctl)));
if(sliderCtrl->sliderPosition() != scpos){
sliderCtrl->setSliderPosition(scpos);
mPlayer->setPosition(scpos);
}
}
}
void Widget::backFc(){
// 获取当前播放时长
qint64 pos = this->mPlayer->position();
long long time = QDateTime::currentDateTime().toMSecsSinceEpoch();// 获取时间戳
if(time > leatFreTime + 1500){
// 上次快退间隔超过5秒,则一级快退
freLevel = 0;
}else{
freLevel ++;
if(freLevel > 2){
freLevel = 2;
}
}
if(pos <= 5000+10000*freLevel){
// 回退到最初
mPlayer->setPosition(0);
}else{
mPlayer->setPosition(pos - (5000+10000*freLevel));
}
leatFreTime = time;
}
void Widget::lastFc(){
int num = listWidget->count();
if(num <= 0){
return;
}
QListWidgetItem* item = NULL;
if(num == 1){
item = listWidget->item(0);
}else{
int nowFileIndex = -1;
QString nowName = fileVal->text();
for(int i=0;i<num;i++){
QListWidgetItem* pItem = listWidget->item(i);
if(pItem->text() == nowName){
nowFileIndex = i-1;
break;
}
}
if(nowFileIndex == -1 || nowFileIndex >= num){
nowFileIndex = 0;
}
item = listWidget->item(nowFileIndex);
}
if(item != NULL){
fileVal->setText(item->text());
item->setSelected(true);
}
}
void Widget::statFc(){
int state = this->mPlayer->state();
if(state == AVPlayer::State::PlayingState){
// 暂停,修改图标
mPlayer->pause(!mPlayer->isPaused());
b2->setIconSize(QSize(150,75));
statButton->setIcon(statbtnBack);
}else if(state == AVPlayer::State::PausedState){
// 继续播放,修改图标
mPlayer->pause(!mPlayer->isPaused());
b2->setIconSize(QSize(0,0));
statButton->setIcon(stopbtnBack);
}
}
void Widget::openOrCloseVolume(){
int num = volumeSlider->value();
if(num <= 0){
volumeSlider->setValue(oldVolume);
soundButton->setIcon(soundbtnBack);
}else{
oldVolume = num;
volumeSlider->setValue(0);
soundButton->setIcon(unsoundbtnBack);
}
}
void Widget::nextFc(){
int num = listWidget->count();
if(num <= 0){
return;
}
QListWidgetItem* item = NULL;
if(num == 1){
item = listWidget->item(0);
}else{
int nowFileIndex = -1;
QString nowName = fileVal->text();
for(int i=0;i<num;i++){
QListWidgetItem* pItem = listWidget->item(i);
if(pItem->text() == nowName){
nowFileIndex = i+1;
break;
}
}
if(nowFileIndex == -1 || nowFileIndex >= num){
nowFileIndex = 0;
}
item = listWidget->item(nowFileIndex);
}
if(item != NULL){
fileVal->setText(item->text());
item->setSelected(true);
// item->setCheckState(Qt::Checked);//Qt::Unchecked
}
}
void Widget::upFc(){
// 获取当前播放时长
qint64 pos = this->mPlayer->position();
// 获取总时长
qint64 all = this->mPlayer->duration();
long long time = QDateTime::currentDateTime().toMSecsSinceEpoch();// 获取时间戳
if(time > leatFreTime + 1500){
// 上次快进间隔超过5秒,则一级快退
freLevel = 0;
}else{
freLevel ++;
if(freLevel > 2){
freLevel = 2;
}
}
if(pos + 5000+10000*freLevel >= all){
// 快进到结束
mPlayer->setPosition(all);
}else{
mPlayer->setPosition(pos + (5000+10000*freLevel));
}
leatFreTime = time;
}
void Widget::openFile(){
QString url = QFileDialog::getOpenFileName(this,"选择文件","/","音频(*.mkv *.mp4 *.mp3 *.avi)");// 父组件,标题,初始目录,文件过滤器
if(url.isEmpty()){
return;
}
int nameIndex = url.lastIndexOf("/");
QString name = url.mid(nameIndex+1);
fileVal->setText(name);
mPlayer->play(url);
if(!oldStr.contains(url)){
qDebug()<<"oldStr....."<<oldStr<<";url="<<url;
QListWidgetItem *item = new QListWidgetItem(name,this->listWidget);
item->setData(Qt::ToolTipRole,url);
item->setText(name);
QWidget *w = new QWidget(listWidget);
QHBoxLayout *layout=new QHBoxLayout(w);
layout->setAlignment(Qt::AlignRight);
QPushButton *pushButton=new QPushButton("x",w);
pushButton->setMaximumWidth(20);
pushButton->setMinimumWidth(20);
pushButton->setMinimumHeight(20);
pushButton->setMaximumHeight(20);
layout->addStretch();
layout->addWidget(pushButton);
item->setSizeHint(QSize(20,35));
w->show();
w->setLayout(layout);
listWidget->setItemWidget(item,w);
widgetItemMap[pushButton] = item;
connect(pushButton,SIGNAL(clicked()),this,SLOT(delItem())); //删除项
// listWidget->addItem(item);
QJsonObject fileInfo;
fileInfo.insert("url",url);
li.append(fileInfo);
saveData();
oldStr.append(";"+url);
item->setSelected(true);
}
}
void Widget::saveData(){
QJsonObject dataJson;
dataJson.insert("list",li);
dataJson.insert("volume",nowVolume);
if(data->open(QIODevice::ReadWrite|QIODevice::Text | QIODevice::Truncate)){
QJsonDocument doc(dataJson);
QString strRet = QString(doc.toJson(QJsonDocument::Indented));
QTextStream ins(data);
ins<<strRet<<endl;
data->close();
}
}
void Widget::openFiles(){
QStringList urlList = QFileDialog::getOpenFileNames(this,"选择文件","/","音频(*.mkv *.mp4 *.mp3 *.avi)");//Image Files(*.jpg *.png);;MP4(*.mp4)
if(urlList.size() == 0){
return;
}
for(int i = 0;i < urlList.size();i++){
int nameIndex = urlList.at(i).lastIndexOf("/");
QString name = urlList.at(i).mid(nameIndex+1);
fileVal->setText(name);
if(i==0){
mPlayer->play(urlList.at(i));
}
if(!oldStr.contains(urlList.at(i))){
oldStr.append(";"+urlList.at(i));
QListWidgetItem *item = new QListWidgetItem(name,this->listWidget);
item->setData(Qt::ToolTipRole,urlList.at(i));
item->setText(name);
QWidget *w = new QWidget(listWidget);
QHBoxLayout *layout=new QHBoxLayout(w);
layout->setAlignment(Qt::AlignRight);
QPushButton *pushButton=new QPushButton("x",w);
pushButton->setMaximumWidth(20);
pushButton->setMinimumWidth(20);
pushButton->setMinimumHeight(20);
pushButton->setMaximumHeight(20);
layout->addStretch();
layout->addWidget(pushButton);
item->setSizeHint(QSize(20,35));
w->show();
w->setLayout(layout);
listWidget->setItemWidget(item,w);
widgetItemMap[pushButton] = item;
connect(pushButton,SIGNAL(clicked()),this,SLOT(delItem())); //删除项
// listWidget->addItem(item);
QJsonObject fileInfo;
fileInfo.insert("url",urlList.at(i));
li.append(fileInfo);
saveData();
}
}
}
void Widget::openDir(){
QString dirUrl = QFileDialog::getExistingDirectory(this,"选择文件夹","/");
if(dirUrl.isEmpty()){
return;
}
QDir dir;
dir.setFilter(QDir::Files | QDir::Hidden | QDir::Dirs);
dir.setSorting(QDir::Size | QDir::Reversed);
dir.setPath(dirUrl);
int oldNum = li.size();
readDir(dir);
if(li.size() > oldNum){
// 数据落地
saveData();
}
}
void Widget::closePro(){
QApplication* app;
app->quit();
}
void Widget::cutPro(){
if(mPlayer->state() == AVPlayer::State::PlayingState || mPlayer->state() == AVPlayer::State::PausedState){
// 不自动保存
mPlayer->videoCapture()->setAutoSave(false);
QDir d;
if(d.mkpath(QApplication::applicationDirPath()+"/cutData")){
mPlayer->videoCapture()->setCaptureDir(QApplication::applicationDirPath()+"/cutData");
mPlayer->videoCapture()->capture();
}
}
}
void Widget::saveImage(QImage img){
QString timeStr = QDateTime::currentDateTime().toString("yyyyMMddHHmmss");
// 100: 质量因数,0~100或-1,100表示大型未压缩
img.save(QApplication::applicationDirPath()+"/cutData/"+timeStr+".jpg","JPG",100);
}
void Widget::playerClick(){
int state = this->mPlayer->state();
if(state == AVPlayer::State::PlayingState){
// 暂停,修改图标
mPlayer->pause(!mPlayer->isPaused());
b2->setIconSize(QSize(150,75));
statButton->setIcon(statbtnBack);
}else if(state == AVPlayer::State::PausedState){
// 继续播放,修改图标
mPlayer->pause(!mPlayer->isPaused());
b2->setIconSize(QSize(0,0));
statButton->setIcon(stopbtnBack);
}
}
void Widget::readDir(QDir dir){
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
if(list.at(i).isDir()){
// 目录
if(!list.at(i).filePath().endsWith(".")){
QDir loadDir(list.at(i).filePath());
if(loadDir.exists()){
loadDir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::Dirs);
readDir(loadDir);
}
}
}else{
// 文件
QFileInfo fileInfo = list.at(i);
if(fileInfo.filePath().endsWith(".mkv") || fileInfo.filePath().endsWith(".mp4")
|| fileInfo.filePath().endsWith(".mp3") || fileInfo.filePath().endsWith(".avi")){
if(oldStr.contains(fileInfo.filePath())){
continue;
}
oldStr.append(";"+fileInfo.filePath());
int nameIndex = fileInfo.filePath().lastIndexOf("/");
QString name = fileInfo.filePath().mid(nameIndex+1);
QListWidgetItem *item = new QListWidgetItem(name,this->listWidget);
item->setData(Qt::ToolTipRole,fileInfo.filePath());
item->setText(name);
QWidget *w = new QWidget(listWidget);
QHBoxLayout *layout=new QHBoxLayout(w);
layout->setAlignment(Qt::AlignRight);
QPushButton *pushButton=new QPushButton("x",w);
pushButton->setMaximumWidth(20);
pushButton->setMinimumWidth(20);
pushButton->setMinimumHeight(20);
pushButton->setMaximumHeight(20);
layout->addStretch();
layout->addWidget(pushButton);
item->setSizeHint(QSize(20,35));
w->show();
w->setLayout(layout);
listWidget->setItemWidget(item,w);
widgetItemMap[pushButton] = item;
connect(pushButton,SIGNAL(clicked()),this,SLOT(delItem())); //删除项
QJsonObject fileInfoObj;
fileInfoObj.insert("url",fileInfo.filePath());
li.append(fileInfoObj);
}
// qDebug() << qPrintable(QString("%1").arg(fileInfo.size(), 10))<<";;;;;;;;path="<<fileInfo.filePath();
}
}
}
void Widget::keyPressEvent(QKeyEvent *event){
if(event->key()==Qt::Key_Left){
if(!event->isAutoRepeat()){
backFc();
}
}
if(event->key()==Qt::Key_Right){
if(!event->isAutoRepeat()){
upFc();
}
}
if(event->key()==Qt::Key_Up){
addVolume();
}
if(event->key()==Qt::Key_Down){
subVolume();
}
if(event->key()==Qt::Key_Escape){
fileVal->show();
hideButton->show();
maxButton->show();
closeButton->show();
backButton->show();
lastButton->show();
statButton->show();
nextButton->show();
upButton->show();
tomaxButton->show();
timeSlider->show();
timeLable->show();
soundButton->show();
bar->show();
btnRight->show();
btnLeft->show();
boxBtn->show();
cutButton->show();
}
}
void Widget::subVolume(){
int num = volumeSlider->value();
if(num <= 0){
soundButton->setIcon(soundbtnBack);
return;
}
num -= 15;
if(num <= 0){
num = 0;
}
nowVolume = num;
volumeSlider->setValue(num);
mPlayer->audio()->setVolume(num/100.0);
}
void Widget::addVolume(){
int num = volumeSlider->value();
if(num <= 0){
soundButton->setIcon(soundbtnBack);
}
if(num >= 100){
return;
}
num += 15;
if(num > 100){
num = 100;
}
nowVolume = num;
volumeSlider->setValue(num);
mPlayer->audio()->setVolume(num/100.0);
}
void Widget::tomax(){
setWindowState(Qt::WindowMaximized);
fileVal->hide();
hideButton->hide();
maxButton->hide();
closeButton->hide();
backButton->hide();
lastButton->hide();
statButton->hide();
nextButton->hide();
upButton->hide();
tomaxButton->hide();
volumeSlider->hide();
timeSlider->hide();
timeLable->hide();
soundButton->hide();
bar->hide();
btnRight->hide();
btnLeft->hide();
boxBtn->hide();
cutButton->hide();
showList = false;
listWidget->hide();
showListButton->setStyleSheet("border-image:url(:/resources/hideList.png);");
}
void Widget::hidePro(){
setWindowState(Qt::WindowMinimized);
}
void Widget::maxPro(){
setWindowState(Qt::WindowMaximized);
}
void Widget::filePlay(QString url){
if(url.endsWith(".mkv") || url.endsWith(".mp4")
|| url.endsWith(".mp3") || url.endsWith(".avi")){
int nameIndex = url.lastIndexOf("\\");
QString name = url.mid(nameIndex+1);
fileVal->setText(name);
mPlayer->play(url);
if(!oldStr.contains(url) && !oldStr.contains(name)){
QListWidgetItem *item = new QListWidgetItem(name,this->listWidget);
item->setData(Qt::ToolTipRole,url);
item->setText(name);
QWidget *w = new QWidget(listWidget);
QHBoxLayout *layout=new QHBoxLayout(w);
layout->setAlignment(Qt::AlignRight);
QPushButton *pushButton=new QPushButton("x",w);
pushButton->setMaximumWidth(20);
pushButton->setMinimumWidth(20);
pushButton->setMinimumHeight(20);
pushButton->setMaximumHeight(20);
layout->addStretch();
layout->addWidget(pushButton);
item->setSizeHint(QSize(20,35));
w->show();
w->setLayout(layout);
listWidget->setItemWidget(item,w);
widgetItemMap[pushButton] = item;
connect(pushButton,SIGNAL(clicked()),this,SLOT(delItem())); //删除项
QJsonObject fileInfo;
fileInfo.insert("url",url);
li.append(fileInfo);
saveData();
oldStr.append(";"+url);
item->setSelected(true);
}
}
}
void Widget::selectSpeed(QString sp){
QString s= sp.replace("x","");
speed = s.toDouble();
mPlayer->setSpeed(speed);
}