• QProcess 调用 ffmpeg来处理音频


    项目场景:

    在文章 qt 实现音视频的分贝检测系统中,实现的是边播放变解析音频数据来统计音频的分贝大小,并不满足实际项目的需求,有的视频声音正常,有的视频声音就偏低,即使放到最大音量声音也是比较小,本文的目的是直接通过对本地视频进行检测,拿出关键指标,来进行对音频处理

    关键依赖:ffmpeg

    因为依赖于ffmpeg的能力,所以第一步要安装ffmpeg环境,自行百度

    步骤1,检测max_volume 值是否小于0dB

     ffmpeg -i 1.mp3  -filter_complex volumedetect -c:v copy -f null /dev/null
    
    • 1

    在这里插入图片描述
    其中检测的关键指标就是 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" )
    
    • 1
    • 2
    • 3
    QMediaPlayer p;
    p.audioAvailable()
    • 1
    • 2

    步骤3,处理音频,使max_volume的值接近0dB

    音频文件用以下指令

    ffmpeg -i 1.mp3-af  "volume=5.8dB"  out.mp3
    
    • 1

    如果是视频文件,用以下指令:(保持视频信息不变,当然还设计到音频的编码格式)

    ffmpeg -i 1.mp3-af  "volume=5.8dB" -c:v copy -c:a aac out.mp3
    
    • 1

    输出如下
    在这里插入图片描述
    在这里插入图片描述

    程序设计思路

    通过QProcess 调用 ffmpeg指令,检测max_volume小于0的文件,拿到文件列表,再通过ffmpeg指令来提高音频。关键代码如下:

    dbdetectthread.h

    #ifndef DBDETECTTHREAD_H
    #define DBDETECTTHREAD_H
    
    #include 
    #include 
    #include 
    #include 
    
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    dbdetectthread.cpp

    #include "dbdetectthread.h"
    #include 
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    dbprocessthread.h

    #ifndef DBPROCESSTHREAD_H
    #define DBPROCESSTHREAD_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    dbprocessthread.cpp

    #include "dbprocessthread.h"
    #include 
    #include 
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    效果如图:
    在这里插入图片描述
    代码上传到此 https://download.csdn.net/download/u011942101/88299291

  • 相关阅读:
    【C语言】sizeof操作符详解
    深入理解计算机系统——第九章 Virtual Memory
    jQuery、vue、小程序、uni-app中的本地存储数据和接受数据是什么?
    嵌入式开发环境Vscode开发STM32单片机程序
    行驶证识别易语言代码
    Nacos知识点
    HPA控制器
    “新DeFi”生态的构建,流支付协议Zebec或厚积薄发
    前端---ES5知识点小梳理五
    大佬总结的170道 python面试题
  • 原文地址:https://blog.csdn.net/u011942101/article/details/132675196