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
建立應用向導->類資訊
注:筆者在這裡強烈建議各位手動寫代碼實作界面,這種方式更迅速、更靈活。
實作下圖所示界面
實作界面的關鍵代碼如下
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("未選擇任何檔案"));
}
}
程式最終的運作效果示例如下
FFmpeg不僅能轉換視訊檔案,還能轉換音頻檔案,讀者可以擴充現有程式,使其支援音頻檔案的轉換。
關于FFmpeg支援的所有的音視訊格式資訊可以參考官網說明 https://www.ffmpeg.org/general.html#File-Formats