Qt QML调用FFmpeg命令行(提取封面图)
Qt自带的MediaPlayer虽然能够处理简单的播放,但对兼容性和可操作性上还是差了很多。所以调用FFmpeg命令行能够让我们轻松的到达想要的效果
下面是我调用FFmpeg命令行提取封面图的c++类,希望以此能够了解如何去调用FFmpeg命令行工具
一、创建头文件
首先就是创建头文件,声明函数
//ffmpegsolve.h#ifndef FFMPEGSOLVE_H
#define FFMPEGSOLVE_H#include <QObject>
#include <QProcess>class FFmpegSolve : public QObject
{Q_OBJECT
public:explicit FFmpegSolve(QObject *parent = nullptr);Q_INVOKABLE void getcover(const QString &localpath); //调用ffmpeg处理signals:void coveready(const QString &coverpath); //处理完成private slots:void processfinish(int exitcode,QProcess::ExitStatus exitstatus); //用于发出coveready的槽函数private:QProcess *process; //进程管理器,用于启动监听外部程序QString coverpath; //封面文件路径
};#endif // FFMPEGSOLVE_H
二、函数实现
接着在源文件中实现函数
//ffmpeg.cpp#include "ffmpegsolve.h"
#include <QProcess>
#include <QFileInfo>
#include <QFile>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QUrl>FFmpegSolve::FFmpegSolve(QObject *parent): QObject{parent}
{process=new QProcess(this); //在ffmpegsolve对象析构时自动销毁processconnect(process,&QProcess::finished,this,&FFmpegSolve::processfinish);//当外部程序运行完毕后发出finish信号,执行processfinish
}void FFmpegSolve::getcover(const QString &localpath)
{QFileInfo file(localpath); //QFileInfo能够获取初始化对象的 目录、文件名等信息//例:localpath="C:/Music/test.flac"//file.absolutePath()="C:/Music",file.completeBaseName()="test"(多重扩展名前部分),file.baseName()="test"(只去除最后一个后缀)//file.fileName()="test.flac",file.suffix()="flac",file.completeSuffix="flac"(多重扩展名)coverpath=file.absolutePath()+"/"+file.completeBaseName()+"_cover.jpg";//拼接为"C:/Music/test_cover.jpg"if (QFile::exists(coverpath)) { //检查是否已经存在封面文件qDebug() << "封面已存在,跳过提取:" << coverpath;emit coveready(coverpath); //直接发出完毕信号return; //结束函数,也就不会执行start去调用ffmpeg了}QString prog="ffmpeg"; //定义外部程序名字(Qt会通过系统环境变量去寻找匹配对象)QStringList command; //字符串列表,用于存放 命令行参数command<<"-i"<<localpath<<"-an"<<"-vcodec"<<"copy"<<"-vframes"<<"1"<<coverpath;//截取静音同编码文件的第一帧画面process->start(prog,command); //调用ffmpeg,执行命令行参数if(!process->waitForStarted(5000)){ //等待打开超时报错qWarning()<<"ffmpeg启动失败";}
}void FFmpegSolve::processfinish(int exitcode, QProcess::ExitStatus exitstatus)
{Q_UNUSED(exitstatus)//由于信号void QProcess::finished(int exitcode, QProcess::ExitStatus exitstatus);//而槽函数的参数必须和信号匹配,所以用Q_UNUSED()告诉编译没用此参数,避免报错if(exitcode==0&&QFile::exists(coverpath)){ //若QProcess获取的退出码为0,即正常退出且已存在封面qDebug()<<"封面提取成功:"<<coverpath;emit coveready(coverpath);}else{qWarning()<<"封面提取失败";emit coveready("");}
}
三、调用函数
我是将路径存入数据库的,但也简单写一下调用和路径处理吧
connect(ffmpeg,&FFmpegSolve::coveready,this,[=](const QString &coverpath){
//先建立信号与槽的连接,捕获信号中的coverpath.......
});
ffmpeg->getcover(localpath); //调用函数,当发出信号时执行槽函数......if (!coverpath.startsWith("file:///")) { coverpath = QUrl::fromLocalFile(coverpath).toString(); //转义空格和特殊字符,生成标准file:///URL
}
四、QML中显示
如果是存入数据库的话,记得提前在ListModel中声明字段,不然可能会触发delegate缓存和t异步刷新机制,这块我也没完全弄懂,现在这标记一下了
ListModel{ListElement{.......coverpath:""}
}
......
ListView{......delegate: Rectangle{}Image {......source:model.coverpath?model.coverpath:"C:/Music/test_cover.jpg"......}
}
以上就是我写的完整流程了,虽然只是简单调用了一下ffmpeg,但是也是有所交互了,如果后面有了更深入的理解也会更新一下的