项目场景:
在文章 qt 实现音视频的分贝检测系统中,实现的是边播放变解析音频数据来统计音频的分贝大小,并不满足实际项目的需求,有的视频声音正常,有的视频声音就偏低,即使放到最大音量声音也是比较小,本文的目的是直接通过对本地视频进行检测,拿出关键指标,来进行对音频处理
关键依赖:ffmpeg
因为依赖于ffmpeg的能力,所以第一步要安装ffmpeg环境,自行百度
步骤1,检测max_volume 值是否小于0dB
ffmpeg -i 1.mp3 -filter_complex volumedetect -c:v copy -f null /dev/null
其中检测的关键指标就是 max_volume, 本次检测和处理的目标就是把 max_volume的值给提高到0dB;
步骤2,如何区分是音频文件还是视频文件
可以有多种方法,1可以通过后缀名来区分; 2 因为代码是用qt写的,可以用qt来实现,代码如下
QFileInfo f(“1.mp3”);
if(f.completeSuffix() == "mp3" || f.completeSuffix() == "aac" ||
f.completeSuffix() == "amr" || f.completeSuffix() == "wav" || f.completeSuffix() == "wma" )
QMediaPlayer p;
p.audioAvailable();
步骤3,处理音频,使max_volume的值接近0dB
音频文件用以下指令
ffmpeg -i 1.mp3-af "volume=5.8dB" out.mp3
如果是视频文件,用以下指令:(保持视频信息不变,当然还设计到音频的编码格式)
ffmpeg -i 1.mp3-af "volume=5.8dB" -c:v copy -c:a aac out.mp3
输出如下
程序设计思路
通过QProcess 调用 ffmpeg指令,检测max_volume小于0的文件,拿到文件列表,再通过ffmpeg指令来提高音频。关键代码如下:
dbdetectthread.h
#ifndef DBDETECTTHREAD_H
#define DBDETECTTHREAD_H
#include <QObject>
#include <QThread>
#include <QProcess>
#include <QStringList>
class DbDetectThread : public QThread
{
Q_OBJECT
public:
explicit DbDetectThread(QObject *parent = nullptr);
void setList(const QStringList &list);
virtual void run();
signals:
void sigPath(const QString &p, float db);
void sigMsg(const QString &p);
void sigEnd();
public slots:
void readStandardOutput();
void readStandardError();
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
void threadFinished(); //线程退出
private:
QProcess *pCmdProcess;
QStringList pathlist;
QString curFile;
};
#endif // DBDETECTTHREAD_H
dbdetectthread.cpp
#include "dbdetectthread.h"
#include <QDebug>
DbDetectThread::DbDetectThread(QObject *parent)
: QThread{parent}
{
qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
}
void DbDetectThread::setList(const QStringList &list)
{
pathlist = list;
}
//ffmpeg -i 2.wav -filter_complex volumedetect -c:v copy -f null /dev/null
//需要计算分贝相差值
//ffmpeg -i 2.wav -filter:a "volume=80dB" output2.wav
void DbDetectThread::run()
{
pCmdProcess = new QProcess(); //不要加this
connect(pCmdProcess, &QProcess::readyReadStandardOutput, this, &DbDetectThread::readStandardOutput);
connect(pCmdProcess, &QProcess::readyReadStandardError, this, &DbDetectThread::readStandardError);
connect(pCmdProcess, QOverload<int , QProcess::ExitStatus >::of(&QProcess::finished), this, &DbDetectThread::processFinished);
connect(this, &QThread::finished, this, &DbDetectThread::threadFinished);
QStringList arguments;
//arguments << "-i" << "C:/Users/wmm/Desktop/DbDetect/voice/input.mp4" << "-c:v" << "h264" << "-c:a" << "aac" << "C:/Users/wmm/Desktop/DbDetect/voice/output.mp4";
//arguments << "-i" << "input.mp4" << "-c:v" << "h264" << "-c:a" << "aac" << "output.mp4";
//arguments << "-i" << p << "-filter_complex"<< "volumedetect" << "-c:v" << "copy" << "-f" << "null" << "/dev/null";
//QString cmd = QString("ffmpeg -i %1 -filter_complex volumedetect -c:v copy -f null /dev/null").arg(p);
//QString cmd = QString("ffmpeg -i C:/Users/wmm/Desktop/DbDetect/voice/input.mp4 -filter_complex volumedetect -c:v copy -f null /dev/null");
//QString cmd = "ping www.baidu.com -w 500";
//QString result;
foreach(auto p, pathlist)
{
curFile = p;
qDebug() << "start detect " << curFile;
QString cmd = QString("ffmpeg -i %1 -filter_complex volumedetect -c:v copy -f null /dev/null").arg(p);
pCmdProcess->start(cmd/*,arguments*/);
pCmdProcess->waitForFinished();
sleep(1);
}
}
void DbDetectThread::readStandardOutput() {
qDebug() << "Standard output: " << pCmdProcess->readAllStandardOutput();
}
void DbDetectThread::readStandardError() {
QString errorMessage = QString::fromUtf8(pCmdProcess->readAllStandardError());
QStringList errlist = errorMessage.split("\r\n");
//qDebug() << errlist.size();
//qDebug() << errlist;
// float num = 3.1415926;
// QString str = QString::number(num, 'f', 2);
foreach (auto p, errlist) {
if(p.contains("max_volume"))
{
qDebug() << p;
int pos = p.indexOf("max_volume");
int pos2 = p.indexOf("dB",pos);
QString val = p.mid(pos+11,pos2-11-pos);
qDebug() <<val;
float fval = val.toFloat();
qDebug() << fval;
if(fval <0)
{
qDebug() << "小于0";
emit sigPath(curFile,fval);
}
else
{
qDebug() << "不小于0";
}
}
}
// 对 errorMessage 进行解析和处理...
//qDebug() << "Standard error: " << pCmdProcess->readAllStandardError();
}
void DbDetectThread::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << "Process finished with exit code" << exitCode << "and exit status" << exitStatus << ((QProcess*)QObject::sender())->arguments();
emit sigMsg(QString("%1 检测完成,exitCode = %2, exitStatus = %3 ").arg(curFile).arg(exitCode).arg(exitStatus));
}
void DbDetectThread::threadFinished()
{
qDebug() << __func__;
emit sigEnd();
pCmdProcess->deleteLater();
}
dbprocessthread.h
#ifndef DBPROCESSTHREAD_H
#define DBPROCESSTHREAD_H
#include <QObject>
#include <QThread>
#include <QProcess>
#include <QMap>
#include <QMediaPlayer>
class DbProcessThread : public QThread
{
Q_OBJECT
public:
explicit DbProcessThread(QObject *parent = nullptr);
void setOutputDir(const QString &dir);
void setMap(const QMap<QString,float> &map);
virtual void run();
signals:
void sigPath(const QString &p, float db);
void sigMsg(const QString &p);
void sigEnd();
public slots:
void readStandardOutput();
void readStandardError();
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
void threadFinished(); //线程退出
private:
QProcess *pCmdProcess;
QMediaPlayer mediaPlayer;
QMap<QString,float> m_needProcessMap;
QString m_outputDir;
QString m_curSrcFile;
QString m_curDesFile;
};
#endif // DBPROCESSTHREAD_H
dbprocessthread.cpp
#include "dbprocessthread.h"
#include <QDebug>
#include <QFileInfo>
DbProcessThread::DbProcessThread(QObject *parent)
: QThread{parent}
{
}
void DbProcessThread::setOutputDir(const QString &dir)
{
m_outputDir = dir;
}
void DbProcessThread::setMap(const QMap<QString, float> &map)
{
m_needProcessMap = map;
}
void DbProcessThread::run()
{
pCmdProcess = new QProcess(); //不要加this
connect(pCmdProcess, &QProcess::readyReadStandardOutput, this, &DbProcessThread::readStandardOutput);
connect(pCmdProcess, &QProcess::readyReadStandardError, this, &DbProcessThread::readStandardError);
connect(pCmdProcess, QOverload<int , QProcess::ExitStatus >::of(&QProcess::finished), this, &DbProcessThread::processFinished);
connect(this, &QThread::finished, this, &DbProcessThread::threadFinished);
QMapIterator<QString, float> i(m_needProcessMap);
while (i.hasNext()) {
i.next();
QFileInfo f(i.key());
qDebug() << f.fileName() << m_outputDir+"/" + f.fileName();
m_curSrcFile = i.key();
m_curDesFile = m_outputDir+"/" + f.fileName();
QStringList arguments;
//arguments << "-i" << i.key() << "-c:v" << "h264" << "-c:a" << "aac" << m_outputDir+"/" + f.fileName();
arguments << "-i" << i.key() << "-af" << QString("volume=%1dB").arg(qAbs(i.value())) << "-c:v copy" << " -c:a aac " << m_outputDir+"/" + f.fileName();
qDebug() << arguments;
emit sigMsg(QString("正在处理...%1").arg(m_curSrcFile));
if(f.completeSuffix() == "mp3" || f.completeSuffix() == "aac" || f.completeSuffix() == "amr" || f.completeSuffix() == "wav"\
|| f.completeSuffix() == "wma" )
{
pCmdProcess->start(QString("ffmpeg -i %1 -af \"volume=%2dB\" %3").arg(i.key()).arg(qAbs(i.value())).arg(m_outputDir+"/" + f.fileName())/*,arguments*/);
}
else {
pCmdProcess->start(QString("ffmpeg -i %1 -af \"volume=%2dB\" -c:v copy %3").arg(i.key()).arg(qAbs(i.value())).arg(m_outputDir+"/" + f.fileName())/*,arguments*/);
}
//pCmdProcess->start(QString("ffmpeg -i %1 -af \"volume=%2dB\" -c:v copy -c:a aac %3").arg(i.key()).arg(qAbs(i.value())).arg(m_outputDir+"/" + f.fileName())/*,arguments*/);
pCmdProcess->waitForFinished();
msleep(500);
}
}
void DbProcessThread::readStandardOutput() {
qDebug() << "Standard output: " << pCmdProcess->readAllStandardOutput();
}
void DbProcessThread::readStandardError() {
qDebug() << "Standard error: " << pCmdProcess->readAllStandardError();
}
void DbProcessThread::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << "Process finished with exit code" << exitCode << "and exit status" << exitStatus << ((QProcess*)QObject::sender())->arguments();
emit sigMsg(QString("%1 处理完成,文件保存为%2,exitCode = %3, exitStatus = %4 ").arg(m_curSrcFile).arg(m_curDesFile).arg(exitCode).arg(exitStatus));
}
void DbProcessThread::threadFinished()
{
qDebug() << __func__;
emit sigEnd();
pCmdProcess->deleteLater();
}
效果如图:
代码上传到此 https://download.csdn.net/download/u011942101/88299291