天天看點

使用Qt和FFmpeg開發(音)視訊格式轉換應用

作者:樂其觀察

Qt開發實戰(Qt In Action)系列,本系列的源代碼倉庫位址如下

GitHub: https://github.com/osjn/QtInAction.git

Gitee(碼雲):https://gitee.com/jingnen/QtInAction.git

本文将通過Qt和FFmpeg開發一個視訊格式轉換應用。

1、擷取 FFmpeg

參考文章 FFmpeg簡介及使用入門

2、打開QtCreator,建立工程videoconverter

在建立應用向導的類資訊(Class Information)界面不要勾選 生成form(Generate form),mainwindow.ui

使用Qt和FFmpeg開發(音)視訊格式轉換應用

建立應用向導->類資訊

注:筆者在這裡強烈建議各位手動寫代碼實作界面,這種方式更迅速、更靈活。

實作下圖所示界面

使用Qt和FFmpeg開發(音)視訊格式轉換應用

實作界面的關鍵代碼如下

void MainWindow::initWindow()
{
    QWidget *centerWidget = new QWidget(this);
    QVBoxLayout *mainLayout = new QVBoxLayout;
    QHBoxLayout *fileLayout = new QHBoxLayout;
    filePath = new QLineEdit;
    filePath->setReadOnly(true);
    browserButton = new QPushButton(tr("浏覽..."));
    fileLayout->addWidget(filePath);
    fileLayout->addWidget(browserButton);
    mainLayout->addLayout(fileLayout);
    QHBoxLayout *convertLayout = new QHBoxLayout;
    QLabel *labelSelect = new QLabel(tr("請選擇要轉換的格式:"));
    convertLayout->addWidget(labelSelect);
    createFilterList();
    convertLayout->addWidget(filterList);
    convertButton = new QPushButton(tr("轉換"));
    convertLayout->addWidget(convertButton);
    convertLayout->addStretch();
    mainLayout->addLayout(convertLayout);
    outputDisplay = new QTextEdit;
    outputDisplay->setReadOnly(true);
    mainLayout->addWidget(outputDisplay);
    centerWidget->setLayout(mainLayout);
    setCentralWidget(centerWidget);
    resize(600, 680);
    setWindowTitle("視訊格式轉換");

    connect(browserButton, SIGNAL(clicked()), this, SLOT(openFile()));
    connect(convertButton, SIGNAL(clicked()), this, SLOT(convertVideo()));
}

void MainWindow::createFilterList()
{
    filterList = new QComboBox;
    filterList->addItem("avi");
    filterList->addItem("mp4");
    filterList->addItem("mov");
    filterList->addItem("mkv");
}           

建立程序,Qt提供了QProcess類,可以通過該類啟動外部程式,并且和他們通信。

void MainWindow::createProcess()
{
    process = new QProcess(this);
    connect(process, SIGNAL(started()), this, SLOT(processStarted()));
    connect(process, SIGNAL(readyReadStandardOutput()),
            this, SLOT(readyReadStandardOutput()));
    connect(process, SIGNAL(finished(int)), this, SLOT(processFinished()));
}           

在主視窗構造函數調用初始化界面和建立程序函數

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    initWindow();
    createProcess();
}           

實作浏覽選擇檔案功能

void MainWindow::openFile()
{
    QString fileName = QFileDialog::getOpenFileName(
        this,
        "Open Video",
        "",
        "Video Files(*.avi *.mp4 *.mov *.mkv)"
        );
    filePath->setText(fileName);
}           

實作視訊轉換功能

void MainWindow::convertVideo()
{
    QString fileName = filePath->text();
    if (!fileName.isEmpty())
    {
        if (QFile::exists(fileName))
        {
            if (!outputDisplay->toPlainText().isEmpty())
                outputDisplay->setText("");

            QFileInfo fileInfo(fileName);
            QString outputFileName = fileInfo.path() + "/" + fileInfo.completeBaseName();
            int format = filterList->currentIndex();
            switch (format) {
            case 0:
                outputFileName += ".avi";
                break;
            case 1:
                outputFileName += ".mp4";
                break;
            case 2:
                outputFileName += ".mov";
                break;
            case 3:
                outputFileName += ".mkv";
                break;
            default:
                break;
            }

            QStringList arguments;
            arguments << "-i" << fileName << outputFileName;
            process->setProcessChannelMode(QProcess::MergedChannels);
            process->start("d:/app/ffmpeg/bin/ffmpeg", arguments);
        }
        else
        {
            QMessageBox::warning(this, tr("提示資訊"), tr("視訊檔案不存在"));
        }
    }
    else
    {
        QMessageBox::warning(this, tr("提示資訊"), tr("未選擇任何檔案"));
    }
}           

其中,上面代碼片段中34行process的成員函數start的第一個參數是ffmpeg(.exe)所在的絕對路徑,讀者根據自己的實際情況進行修改。

代碼片段中31~32行,在此例中使用的參數(arguments)是相對基礎的,它們隻是告訴了FFmpeg源檔案名和輸出檔案名(完整路徑)。

想了解更多關于FFmpeg參數的資訊,請參考官網的說明 https://www.ffmpeg.org/ffmpeg.html

實作process的幾個槽函數,并在文本框(QTextEdit)中動态顯示轉換輸出資訊

void MainWindow::processStarted()
{
    browserButton->setEnabled(false);
    filterList->setEditable(false);
    convertButton->setEnabled(false);
}

void MainWindow::readyReadStandardOutput()
{
    outputText += process->readAllStandardOutput();
    outputDisplay->setText(outputText);
    outputDisplay->verticalScrollBar()->setSliderPosition(
        outputDisplay->verticalScrollBar()->maximum());
}

void MainWindow::processFinished()
{
    browserButton->setEnabled(true);
    filterList->setEditable(true);
    convertButton->setEnabled(true);
}           

本工程的關鍵完整代碼如下

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QProcess>
#include <QMessageBox>
#include <QLineEdit>
#include <QPushButton>
#include <QComboBox>
#include <QTextEdit>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void processStarted();
    void readyReadStandardOutput();
    void processFinished();
    void openFile();
    void convertVideo();

private:
    QProcess* process;
    QLineEdit *filePath;
    QPushButton *browserButton;
    QComboBox *filterList;
    QPushButton *convertButton;
    QTextEdit *outputDisplay;
    QString outputText;

    void initWindow();
    void createFilterList();
    void createProcess();
};
#endif // MAINWINDOW_H
           

main.cpp

#include "mainwindow.h"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QScrollBar>
#include <QFileDialog>
#include <QFileInfo>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    initWindow();
    createProcess();
}

MainWindow::~MainWindow()
{
}

void MainWindow::initWindow()
{
    QWidget *centerWidget = new QWidget(this);
    QVBoxLayout *mainLayout = new QVBoxLayout;
    QHBoxLayout *fileLayout = new QHBoxLayout;
    filePath = new QLineEdit;
    filePath->setReadOnly(true);
    browserButton = new QPushButton(tr("浏覽..."));
    fileLayout->addWidget(filePath);
    fileLayout->addWidget(browserButton);
    mainLayout->addLayout(fileLayout);
    QHBoxLayout *convertLayout = new QHBoxLayout;
    QLabel *labelSelect = new QLabel(tr("請選擇要轉換的格式:"));
    convertLayout->addWidget(labelSelect);
    createFilterList();
    convertLayout->addWidget(filterList);
    convertButton = new QPushButton(tr("轉換"));
    convertLayout->addWidget(convertButton);
    convertLayout->addStretch();
    mainLayout->addLayout(convertLayout);
    outputDisplay = new QTextEdit;
    outputDisplay->setReadOnly(true);
    mainLayout->addWidget(outputDisplay);
    centerWidget->setLayout(mainLayout);
    setCentralWidget(centerWidget);
    resize(600, 680);
    setWindowTitle("視訊格式轉換");

    connect(browserButton, SIGNAL(clicked()), this, SLOT(openFile()));
    connect(convertButton, SIGNAL(clicked()), this, SLOT(convertVideo()));
}

void MainWindow::createFilterList()
{
    filterList = new QComboBox;
    filterList->addItem("avi");
    filterList->addItem("mp4");
    filterList->addItem("mov");
    filterList->addItem("mkv");
}

void MainWindow::createProcess()
{
    process = new QProcess(this);
    connect(process, SIGNAL(started()), this, SLOT(processStarted()));
    connect(process, SIGNAL(readyReadStandardOutput()),
            this, SLOT(readyReadStandardOutput()));
    connect(process, SIGNAL(finished(int)), this, SLOT(processFinished()));
}

void MainWindow::processStarted()
{
    browserButton->setEnabled(false);
    filterList->setEditable(false);
    convertButton->setEnabled(false);
}

void MainWindow::readyReadStandardOutput()
{
    outputText += process->readAllStandardOutput();
    outputDisplay->setText(outputText);
    outputDisplay->verticalScrollBar()->setSliderPosition(
        outputDisplay->verticalScrollBar()->maximum());
}

void MainWindow::processFinished()
{
    browserButton->setEnabled(true);
    filterList->setEditable(true);
    convertButton->setEnabled(true);
}

void MainWindow::openFile()
{
    QString fileName = QFileDialog::getOpenFileName(
        this,
        "Open Video",
        "",
        "Video Files(*.avi *.mp4 *.mov *.mkv)"
        );
    filePath->setText(fileName);
}

void MainWindow::convertVideo()
{
    QString fileName = filePath->text();
    if (!fileName.isEmpty())
    {
        if (QFile::exists(fileName))
        {
            if (!outputDisplay->toPlainText().isEmpty())
                outputDisplay->setText("");

            QFileInfo fileInfo(fileName);
            QString outputFileName = fileInfo.path() + "/" + fileInfo.completeBaseName();
            int format = filterList->currentIndex();
            switch (format) {
            case 0:
                outputFileName += ".avi";
                break;
            case 1:
                outputFileName += ".mp4";
                break;
            case 2:
                outputFileName += ".mov";
                break;
            case 3:
                outputFileName += ".mkv";
                break;
            default:
                break;
            }

            QStringList arguments;
            arguments << "-i" << fileName << outputFileName;
            process->setProcessChannelMode(QProcess::MergedChannels);
            process->start("d:/app/ffmpeg/bin/ffmpeg", arguments);
        }
        else
        {
            QMessageBox::warning(this, tr("提示資訊"), tr("視訊檔案不存在"));
        }
    }
    else
    {
        QMessageBox::warning(this, tr("提示資訊"), tr("未選擇任何檔案"));
    }
}
           

程式最終的運作效果示例如下

使用Qt和FFmpeg開發(音)視訊格式轉換應用
使用Qt和FFmpeg開發(音)視訊格式轉換應用
使用Qt和FFmpeg開發(音)視訊格式轉換應用
使用Qt和FFmpeg開發(音)視訊格式轉換應用
使用Qt和FFmpeg開發(音)視訊格式轉換應用

FFmpeg不僅能轉換視訊檔案,還能轉換音頻檔案,讀者可以擴充現有程式,使其支援音頻檔案的轉換。

關于FFmpeg支援的所有的音視訊格式資訊可以參考官網說明 https://www.ffmpeg.org/general.html#File-Formats

繼續閱讀