用QT創建一個FFMPEG前端

 原文:

http://www.tuxradar.com/content/code-project-create-ffmpeg-front-end

命令行沒有什麼不好。對於我們很多人來說,這是使用Linux的最佳理由之一。可以通過輸入內容實現幾乎所有功能,而且命令行工具對於它們的運行方式通常能夠提供極好的控制。但是命令行並不適合所有人,覺得命令行難以理解和令人生畏的Linux用戶數量多得令人喫驚,這或許是完全避免使用Linux的理由之一。儘管如今不願意使用命令行的用戶可以不必再使用它,但這仍然意味着他們將遺漏一些很優秀的實用工具。

Qt正好可以扭轉這種局面。它是爲你最喜愛的命令行工具創建友好GUI的理想工具。它不需要任何頂級的編程技巧,而且工作量也不大,但在此過程中你可以爲討厭命令行的朋友提供幫助,並且對開源應用程序開發做出自己的貢獻。爲命令行工具創建GUI是最佳起點之一!

如果你已經完成了我們以前的Qt Creator代碼項目,如何創建自己的媒體播放器,就能夠更加自如地完成這個任務……

如果說有一種工具非常需要GUI,那就是FFMPEG。FFMPEG是一個十分優秀的命令行應用程序,它可以將視頻和電影文件從一種格式轉換爲另一種格式。同時它的複雜程度也令人喫驚。有好幾位開發人員都曾經試過爲這個工具創建一個GUI,但是FFMPEG的發展速度讓人很難駕馭所有額外的組件和用於各個選項的不斷改變的語法。

我們將會構建我們自己的FFMPEG GUI,但本指南的意義並不在於幫助人們瞭解FFMPEG的非凡之處。本指南將展示爲幾乎任意命令行工具創建GUI是多麼輕鬆的事情——只要替換幾行代碼即可,而且結果可能對我們大有好處。

qt_menq_step_01

* 應該只有意志堅定和不怕閱讀符號文字的人才會輸入“ffmpeg -h”

應用程序設計

我們將假定已經啓動並正在運行Creator,而且對於使用開發環境相對比較滿意。如果不滿足上述條件,請複習我們以前的指南。運行應用程序之後,創建一個新的項目並使用“Qt Gui Application”模板,同時保留其他選項的默認值。

和以前的指南一樣,我們的開發工作仍然從GUI開始。點擊“.ui”文件,Designer視圖隨之出現。如果想從空白畫布開始,可在視圖中刪除菜單欄、工具欄和狀態欄,方法是在窗口右上方的對象視圖中選中它們,右鍵單擊每個組件,然後從出現的菜單中選擇刪除。

我們應用程序的GUI佈局顯然依賴於要運行的命令行工具。但對於我們的FFMPEG例子,我們要儘可能嘗試保持內容的開放性和可修改性。我們需要一個按鈕來添加要轉換的源文件,以及一個用於顯示此文件位置的文本字段。我們需要一些途徑來選擇轉換過程的最終格式,並顯示FFMPEG命令的結果。我們還需要另一個按鈕,點擊它便可啓動整個轉換過程。

但是用戶界面的主要部分將會被我們選出讓用戶編輯的FFMPEG選項所佔據。我們沒有理由向普通用戶公開FFMPEG中數以百計的選項,因此這裏只會出現最常用和最簡單的使用選項。

qt_menq_step_02

* 我們的目標是構建出的佈局能夠最好地利用空間。這也正是我們選擇在另一個頁面上隱藏命令行輸出的原因。

GUI的構建

從左邊的Button小部件列表中拖兩個按鈕到空白畫布上,然後雙擊每個按鈕,修改它們的名稱。一個按鈕的名稱定爲“Source”,而另一個用於觸發轉換過程的按鈕則需要取類似於“Go!”的名稱。另外還可以使用圖標代替文本,或者二者同時出現亦無不可(如果這是一種改進的話)。現在從Inputs列表中添加一個Line Edit小部件,然後點擊左下方面板中的“enabled”屬性以禁用它。

由於Line Edit小部件是交互式的——即默認情況下,用戶可以在裏面輸入內容——我們想禁用這項功能,而只使用它來顯示我們要轉換文件的源位置。如果想讓用戶手動輸入或修改文件的名稱和位置,可以保留此字段的值爲enabled。

我們還爲用戶提供了一條選擇輸出格式的方便途徑。我們選擇使用一列單選按鈕,每個單選按鈕代表一種格式。從調色板中拖動Radio Button小部件即可添加這些按鈕。這種方法的好處在於,每次只能選擇一種格式,而這正是我們需要的行爲模式。對於每個單選按鈕,還需要將其標籤的文本修改爲能夠反映適合FFMPEG輸出的設備。例如,如果選擇PSP作爲輸出設備,將禁用iPhone輸出和GP2X輸出。
爲了最大程度利用可用空間,我們準備使用選項卡小部件來保存餘下的參數。就像瀏覽器中的選項卡式web頁面一樣,允許用戶在一塊GUI區域的兩種不同視圖之間進行切換。我們將使用一個選項卡來保存要公開的主要參數,而另一個選項卡可用於保存來自FFMPEG的原始命令輸出。這應該意味着,除非用戶切換選項卡,否則不會看到亂七八糟的FFMPEG輸出。

從Creator中的Containers列表把選項卡小部件拖到畫布上,選中之後,點擊屬性列表中的“currentTabText”屬性,以便修改每個選項卡上顯示的文本。我們選擇了“Options”和“Output”。

被拖到選項卡小部件中的任意小部件都只顯示在當前選項卡上,因此我們給Options選項卡添加了5個組合框和5個標籤。對於像FFMEG這樣的工具而言,組合框是對用戶最友好的選項,因爲它們將可用選項限制爲只出現有效的選項。它對於程序員意味着額外的工作,但這種額外工作可以爲可憐的老用戶節省很多時間。藉助這5個組合框,我們選擇支持如下配置:視頻的分辨率、幀率和比特率,以及視頻的採樣率和壓縮比特率。

我們重新命名了每個組合框小部件,以便在源代碼中能夠更好地區分它們,同時在每個組合框旁邊放置一個標籤,並使用標籤文本對每個選項進行了說明。在組合框下面,我們添加了更多小部件,用於保存目的文件的名稱—— 一個按鈕用於選擇文件,以及一個禁用的線編輯字段用於顯示位置。

最後,切換到選項卡小部件的下一個頁面,並添加一個TextBrowser小部件。我們將使用這個小部件來顯示FFMPEG命令的輸出。

間距器

現在,我們的應用程序看起來應該相當複雜了。我們需要對各個小部件進行排列,讓它們能夠很好地縮放並且看起來間隔均勻。Qt使用一個分組和間距器的系統來創建佈局,但瞭解這個系統的使用原理需要花點功夫。它和DTP應用程序處理對象的方式不同。例如,要在選項頁面中創建良好的佈局,首先shift選擇一個標籤及其相關組合框,然後從工具欄選擇“Lay Out Horizontally”。這將會把兩個小部件鎖定在並排的位置。

對其他小部件做同樣的事情,然後shift選擇用於修改視頻設置的分組對,並選擇“Lay Out Vertically”。這將把每對小部件都對齊到一列中,對音頻設置也要做同樣的事情。我們還爲音頻和視頻列添加了標籤,並在垂直佈局集合中包含了它們。要想正確實現分組選擇,過程更加麻煩,但如果犯錯我們始終可以進行“Undo”和“Redo”操作。

qt_menq_spacers

最好把間距器看作彈簧,並重新複習一些舊的物理課本。

爲了保證我們的GUI不會明顯延伸爲一個大窗口,我們需要使用“間距器”。它們看起來有點像彈簧,可以從小部件調色板添加垂直或水平的間距器。當在水平或垂直佈局中包含一個正確的間距器時,小部件將會與一塊可擴展空間組合在一起,這塊空間就是彈簧所在的位置。如果應用程序窗口擴展或收縮,這塊空間也會根據彈簧長度成比例地伸縮。

這用語言也許很難描繪,但只要動手實踐便不難理解。最後的結果是佈局系統十分靈活,但學習起來卻比較困難。我們使用三個水平彈簧來分開視頻和音頻選項,以及它們與窗口邊界的空間,同時使用一個垂直彈簧來將視頻與音頻編碼選項和目的文件位置分開。間距器還可用於在單選按鈕之間安插一點距離。

當我們大體上安排好所需要的一切小部件後,選擇“Lay Out in a Grid”將所有小部件綁定到主窗口上。此時仍然能夠拖動和添加小部件到佈局上,但如果需要做較大的改動,則需要首先打破這種綁定,即選擇“Break Layout”。

信號與槽

現在,可以把GUI設計與我們將要在源代碼中添加的程序功能聯繫起來了。這要藉助於我們前面提過的Qt的信號與槽機制。一開始,切換到Signals/Slots編輯模式(F4),並且從“Go”按鈕拖出一個連接到窗口的背景畫布上,用於有效地發送信號給MainWindow類。在出現的窗口中,點擊左邊的Edit按鈕,從而打開Signals/Slots編輯窗口。

我們需要添加6個槽:executeCommand()、setSource()、setDestination()、setPSP()、setIPOD() 和 setGP()。點擊OK,選擇新創建的“executeCommand()”槽,然後連接到我們當前正在編輯的Go按鈕的clicked信號。需要對我們剛剛爲其創建了槽的其他每個按鈕做同樣的事情,方法是把它們連接到它們對應的槽。例如,應該把來自PSP單選按鈕的“clicked()”信號連接到“setPSP()”。

qt_menq_step_03

我們從GUI創建我們自己的槽,而且後面需要在源代碼編輯器中爲它們添加代碼。

完成這項任務之後,Designer編輯就結束了,保存項目並切換爲編輯MainWindow.h。我們需要在文件頂部添加幾個頭:

#include <QProcess> #include <QByteArray> #include <QTextBrowser> #include <QFileDialog> #include <QDesktopServices> #include <QComboBox> 

因爲我們要對已經在Designer中添加給應用程序的小部件進行操作,所以這些頭文件中的大部分都是必不可少的。Qprocess和QbyteArray用於在Qt中運行外部的可執行文件,並抓取命令的輸出,但我們很快就會講到這個函數。現在,我們需要爲要編寫的槽添加定義。在“public:”部分下面添加如下代碼:

private slots: void executeCommand(); void outputCommand(); void setSource(); void setDestination(); void setPSP(); void setIPOD(); void setGP(); 

最後,我們需要在頭文件的“private:”部分中添加一個變量。此變量將用於在Qt中處理外部可執行文件(FFMPEG):

QProcess commandProcess; 

qt_menq_step_04

* 我們從GUI創建我們自己的槽,而且後面需要在源代碼編輯器中爲它們添加代碼。

編程

現在我們到了最富於技巧性的部分,即給應用程序添加功能。首先,我們要爲三種要處理的不同輸出格式編寫“set”函數。這些函數都將使用與設備相關的參數填充組合框,而我們最終將採用這些參數來編譯將創建正確輸出的FFMPEG命令行。

當然,在能夠給GUI添加值之前,首先針對每種格式要有一個有效的FFMPEG命令。例如,我們已經找到用於轉換運行在PSP上的視頻的最佳FFMEPG命令是:

ffmpeg -i space.mpg '-vcodec' 'libxvid' -s 320x240 -r 29.97 -b 1500 -acodec libfaac -ac 2 -ar 24000 -ab 65535 -f psp M4V80113.mp4 -y

我們準備採用這些參數中的一部分,並使它們能夠在我們的GUI中進行編輯。根據我們在其他項目中的經驗,可以通過“ui”對象運行屬於GUI中對象的方法,此對象默認是使用標準Creator模板創建的。例如,“ui->comboResolution->clear()”將在comboResolution組合框上執行清除工作。

Creator集成環境的好處在於,可以使用自動完成功能列出每個對象的可用選項,而不用全靠記憶。下面是我們在setPSP函數中換到GUI中的FFMPEG選項。需要把它添加到MainWindow.cpp文件的底部:

void MainWindow::setPSP() { ui->comboResolution->clear(); ui->comboFramerate->clear(); ui->comboBitrate->clear(); ui->comboSamplerate->clear(); ui->comboAbitrate->clear(); ui->comboResolution->addItem("240x320"); ui->comboResolution->addItem("160x120"); ui->comboFramerate->addItem("29.97"); ui->comboBitrate->addItem("1500"); ui->comboSamplerate->addItem("2400"); ui->comboAbitrate->addItem("65535"); ui->lineEdit_2->setText("M4V80113.mp4");  } 

這段代碼的自解釋程度相當高。爲了節省空間,我們將會給其添加多個選項的惟一組合框是分辨率框,但可以很容易地看到如何添加其他選項。我們還需要爲其他兩種預設置創建相同模板,並把它們放在“setIPOD”和“setGP”函數槽中。由於我們只選擇了一個參數集合讓用戶編輯,因此需要將這些參數與FFMPEG命令中的其他參數結合起來,而我們準備在處理運行外部FFMPEG命令的函數中做這件事情,這個函數叫做“executeCommand()”。

執行一個命令

void MainWindow::executeCommand() { QStringList args;   args << "-i"; args << ui->lineEdit->text(); args << "-y"; args << "-s"; args << ui->comboResolution->currentText(); args << "-r"; args << ui->comboFramerate->currentText(); args << "-b"; args << ui->comboBitrate->currentText(); args << "-ar"; args << ui->comboSamplerate->currentText(); args << "-ab"; args << ui->comboBitrate->currentText(); args << ui->lineEdit_2->text();  if (ui->radioButton->isChecked()){ args << "-vcodec"; args << "libxvid"; args << "-acodec"; args << "libfaac"; args << "-ac"; args << "2"; args << "-f"; args << "psp"; } commandProcess.start("ffmpeg", args);  } 

下面是對於以上代碼塊作用的解釋。在Qt中運行外部命令的關鍵是一個叫做“Qprocess”的類。在步驟三結束時,我們在頭文件中使用這個類創建了我們自己的對象,現在正是時候使用它來執行FFMPEG。使用“commandProcess”變量時,我們只要使用兩個變量運行“start”即可——命令本身和我們的參數列表。

我們使用“QstringList”來快速構造這個參數列表,首先從我們GUI中的小部件,然後是包含在有條件的“if”語句中的PSP特定參數(ui->radioPSP->isChecked)。需要爲其他目的設備添加更多參數,才能讓轉換過程開始工作。

qt_menq_step_05

* 來自FFPMEG命令的輸出將顯示在我們的主應用程序的輸出選項卡中。

在用戶點擊我們在GUI中創建的“Go”按鈕時,將執行這個函數,而且由於我們早先配置好的信號和槽,這個過程也是自動的。但是我們還需要捕捉該過程的輸出,以便在文本視圖中顯示出來,同時給用戶提供一些可視的反饋。令人高興的是,由於信號與槽的神奇魔力,Qprocess可以不太費力地完全執行這種操作。

在MainWindow::MainWindow初始化例行程序中,我們需要在Qprocess輸出信號和我們將用於把輸出轉換爲文本以便於顯示的“outputCommand”槽之間手動創建連接。在“ui->setupUi”行前面添加如下兩行:

connect (&commandProcess, SIGNAL(readyReadStandardOutput()),this, SLOT(outputCommand())); connect (&commandProcess, SIGNAL(readyReadStandardError()),this, SLOT(outputCommand())); 

正如我們看到的那樣,從Qprocess發出的有兩種類型的輸出信號,而且我們將來自這兩種信號的輸出都發送給同一個函數“outputCommand”,現在需要把這個函數添加到源代碼中:

void MainWindow::outputCommand() { QByteArray cmdoutput = commandProcess.readAllStandardOutput(); QString txtoutput = cmdoutput; ui->textBrowser->append(txtoutput); cmdoutput = commandProcess.readAllStandardError(); txtoutput = cmdoutput; ui->textBrowser->append(txtoutput);  } 

這是當Qt從運行“FFMPEG”的Qprocess檢測輸出時執行的函數。這有點繁複,因爲我們不能假定命令的輸出是文本,而且輸出數據有兩種流形式——一種用於來自命令的標準輸出,而另一種用於錯誤輸出。我們的安全做法是在使用Qt的優秀轉換例行程序將這些數據轉換爲一個文本字符串之前,將流中的二進制數據複製到一個原始字節數組中。接着把這些數據添加到文本視圖中,而且我們對於命令的錯誤輸出重複這個過程。沒有什麼捷徑可以同時抓取到這兩種流。

最後,在完成我們的應用程序之前,最後一個步驟是添加兩個槽,用於處理源和目的文件位置。這兩個槽幾乎是完全相同的。下面給出了處理目的文件位置的槽函數:

void MainWindow::setDestination() { QString file = QFileDialog::getSaveFileName (this, tr("Select Destination"), QDesktopServices::storageLocation(QDesktopServices::MoviesLocation)); ui->lineEdit_2->setText(file); } 

第一行創建了一個Qt文件請求器,自動指向系統默認的電影位置,而因爲我們已經使用了“getSaveFileName”,用戶將被詢問一個不一定存在的文件的名稱。這與“setSource”完全相反:

void MainWindow::setSource() { QString file = QFileDialog::getOpenFileName(this, tr("Select Source File"), QDesktopServices::storageLocation(QDesktopServices::MoviesLocation)); ui->lineEdit->setText(file);  } 

輸入這最後兩個函數後,我們應用程序的源代碼就已經完成了,應該有一個可用的FFMPEG GUI,可以自定義它以使用所需的任意參數。編譯並運行就可以了。還應該看到,修改這些代碼以使用其他命令行工具是多麼輕鬆的事情。

qt_menq_final_pic

下載此項目的代碼:qt_menq.tar

藉助基於Qt的前端,用戶就不必深入研究FFMPEG的命令行用法了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章