您的位置:首页 > 编程语言 > Qt开发

Qt+FFMPEG编写适合自己的批量视频转换程序

2012-04-26 22:54 375 查看
由于在帮人做视频批量转换的工作,所以很需要一个专用的批量视频转换程序。它能够从一个目录中读取要转换的视频,添加水印并转换后自动保存在另一个目录。且输出目录和源目录的子目录结构要相对一致。例如源目录为G:\source\,所有的视频都保存在里面,源文件目录为:G:\source\video1\test.mp4。如果要求输出文件都保存在G:\output\那么转换后的文件就要保存在G:\output\video1\test.flv。这样管理起来比较方便。现在很多视频转换软件都没有添加水印,或批量添加水印的功能。也没有这种保持输出目录与源目录结构相对一致的功能。

1.什么是FFmpeg?

FFmpeg是一个开源的对音频、视频进行合并、截取和转换等处理的程序。它包括了libaccodec编码解码类。虽然FFmpeg是在Linux下开发的,但它也可以在包括Windows在内的系统里运行。

这里我使用的FFmpeg的版本信息如下:

ffmpeg version N-33591-gf884ef0, Copyright (c) 2000-2011 the FFmpeg developers built on Oct 13 2011 02:23:02 with gcc 4.6.1

configuration: --enable-gpl --enable-version3 --enable-runtime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enable-libgsm --enable-libmp3lame --enable-libopenjpeg --enable-librtmp
--enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis--enable-libvpx --enable-libx264 --enable-libxavs --enable-libxvid --enable-zlib

其实我也是初学者,上网一搜就找到了这么好的一个程序。这才发现,原来自己电脑里的QQ影音中的视频转换模块也是调用的这个程序。还有我装的Ulead VideoStudio在运行过程中也会出现它的进程,真是不可思议呀!

2.如何在Qt中调用其他的程序?

QProcess *process;

process = new QProcess(this);

process->start(programPath,args);

3.该程序中FFmpeg参数的设置

>ffmpeg -i G:/test.mp4 -vcodec libx264 -b:v 1000k -s 640x480 -vf "movie=logo.png[logo];[in][logo] overlay=16:16 [out]" -acodec libvo_aacenc -b:a 256k -ac 2 -ar 44100 -y G:/outputPath/test.flv

-i 导入要转换的视频文件

-vcodec 视频编码类

-b:v 视频质量

-s 视频大小

-vf 加入水印通道

-acodec 音频编码

-b:a 音频质量

-ac 声道

-ar 声频波特率

-y 输出文件路径

这些参数我参考了FFmpeg的文档和网上的各种教程。需要注意的就是添加水印的这个参数了。"movie="后面只能带文件名,而不能用文件的路径。也就是说,每次调用ffmpeg时,都要在水印文件所在的工作目录下调用。否则会出现找不到水印文件的情况。大家在写转换程序之前可以先写个*.bat文件试试。测试满意后再写。我在这之前就测试了很多组,这是最满意的一组。网上有其他的命令,应该也可以,只是软件的版本不一样,所以有点出入而已。

4.软件编写

先放上程序最终的效果图:


里面的"Main Source Path“就是存放视频文件的目录,其中每个视频文件又分门别类地放在了各子目录里。而"Main Output Path"就是存放输出文件的地方。每个视频转换后都会自动按照源目录的目录结构存在"Main Output Path"中。这也是这个批量转换程序的特点之一。在"Convert to"中可以设置转换格式。"Video Option"和"Audio Option"可以进行更详细的设置。添加水印的选项也在其中。"Position"用来设置水印图片在视频中显示的位置。用户打开要转换的文件,设置好参数,点击"Convert
All"后就开始自动转换了。转换过程中的信息会在下面的QTextBrouwer中显示。

这是主程序的类:

#ifndef CONVERTVIDEO_H

#define CONVERTVIDEO_H

#include <QtGui>

namespace Ui {

class convertVideo;

}

class convertVideo : public QWidget

{

Q_OBJECT

public:

explicit convertVideo(QWidget *parent = 0);

~convertVideo();

private slots:

void on_pushButton_setMainSourcePath_clicked();

void on_pushButton_setMainOutputPath_clicked();

void on_pushButton_loadFiles_clicked();

void on_pushButton_setWatermarkPath_clicked();

void on_pushButton_openList_clicked();

void on_pushButton_saveList_clicked();

void on_checkBox_hasWatermark_stateChanged(int);

void on_pushButton_convertAll_clicked();

void on_comboBox_videoQuality_currentIndexChanged(int);

void on_lineEdit_videoQuality_textEdited(QString);

void on_comboBox_audioQuality_currentIndexChanged(int);

void on_lineEdit_audioQuality_textEdited(QString);

void continueConvert(int,QProcess::ExitStatus);

void outputCommand();

private:

QStringList getArguments(int row);

void outputLog(QString);

void writeSettings();

void readSettings();

private:

Ui::convertVideo *ui;

QProcess *process;

int convertedFile;

int fileNumber;

QString selfAbPath;

};

#endif // CONVERTVIDEO_H

首先我们来看看构造函数:

convertVideo::convertVideo(QWidget *parent) :

QWidget(parent),

ui(new Ui::convertVideo)

{

ui->setupUi(this);

……

process=new QProcess;

connect(process,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(continueConvert(int,QProcess::ExitStatus)));

connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(outputCommand()));

connect(process,SIGNAL(readyReadStandardError()),this,SLOT(outputCommand()));

……

}

这样每当一个转换完一个视频,process就会发出个finished信号,调用continueConvert槽。在continueConvert这个函数中会判断上一个转换是否正常结束。如果是正常结束,且还有未转换的视频就继续转换。outputCommand()这个槽实现了将FFmpeg执行过程中的信息输出到QTextBrouwer中。为了简便,我将标准输出和错误输出都连接到这里来了。反正这程序也是自己用嘛,呵呵。

void convertVideo::outputCommand(){

QByteArray cmdoutput = process->readAllStandardOutput();

char *t=cmdoutput.data();

QString txtoutput=QString::fromLocal8Bit(t);

ui->textBrowser->append(txtoutput);

cmdoutput = process->readAllStandardError();

t=cmdoutput.data();

txtoutput=QString::fromLocal8Bit(t);

ui->textBrowser->append(txtoutput);

}

在用户点击"Convert All"之后,就会调用on_pushButton_convertAll_clicked()槽,在这里会初次调用FFmpeg来转换列表中的第一个视频。

void convertVideo::on_pushButton_convertAll_clicked(){

if(convertedFile < fileNumber){

while(ui->tableWidget->formula(convertedFile,1)=="had been converted")

convertedFile++;

QStringList args=getArguments(convertedFile);

QDir::setCurrent(QFileInfo(ui->lineEdit_watermarkPath->text()).absolutePath());

//qDebug() << "watermark path: " << QDir::currentPath() << endl;

process->start(tr("ffmpeg"),args);

ui->tableWidget->setFormula(convertedFile,1,tr("had been converted"));

}

else

QMessageBox::information(this,tr("Information"),tr("There is no file to convert."),QMessageBox::Yes);

}

我写了getArguments(int i)函数来获取视频列表中每一个视频的转换参数。这里注意在执行FFmpeg之前,一定要用QDir::setCurrent将当前工作目录设置为水印文件所在的目录,否则会出现找不到水印文件的情况。而ffmpeg只要放在当前视频转换程序的根目录就行了。

QStringList convertVideo::getArguments(int row){

QStringList args;

QString filepath=ui->tableWidget->formula(row,0);

args << "-i" << filepath;

if(ui->comboBox_vcodec->currentIndex()!=2){

args << "-vcodec" << ui->comboBox_vcodec->currentText();

}

switch(ui->comboBox_videoQuality->currentIndex()){

case 0:args << "-b:v" << "2000k";break;

case 1:args << "-b:v" << "1000k";break;

case 2:args << "-b:v" << "500k";break;

case 3:args << "-b:v" << "250k";break;

case 4:args << "-b:v" << ui->lineEdit_videoQuality->text();break;

}

args << "-s" << ui->comboBox_videoScale->currentText();

if(ui->checkBox_hasWatermark->isChecked()){

args << "-vf" << tr("movie=%1[logo];[in][logo] overlay=%2:%3 [out]").arg(QFileInfo(ui->lineEdit_watermarkPath->text()).fileName())

.arg(ui->lineEdit_watermark_x->text())

.arg(ui->lineEdit_watermark_y->text());

}

if(ui->comboBox_audioCodec->currentIndex()!=2)

args << "-acodec" << ui->comboBox_audioCodec->currentText();

switch(ui->comboBox_audioQuality->currentIndex()){

case 0:args << "-b:a" << "256k";break;

case 1:args << "-b:a" << "192k";break;

case 2:args << "-b:a" << "125k";break;

case 3:args << "-b:a" << "96k";break;

case 4:args << "-b:a" << ui->lineEdit_audioQuality->text();break;

}

args << "-ac" << QString::number(ui->comboBox_audioChannel->currentIndex()+1);

args << "-ar" << ui->comboBox_audioRate->currentText();

args << "-y";

QString output=ui->lineEdit_mainOutputPath->text()

+filepath.right(filepath.count()-ui->lineEdit_mainSourcePath->text().count());

output=output.left(output.count()-3)+ui->comboBox_convertTo->currentText();

QDir outputPath;outputPath.mkpath(QFileInfo(output).absolutePath());

args << output;

qDebug() << args << endl;

return args;

}

获取参数的函数写得比较多,不过很好理解,基本都差不多,只是参数多所以长。注意对于输出目录,如果不存在的话,就要用QDir::mkpath(QString)函数。否则也会出现找不到路径的情况。

=================================================================================

重要的就这几步啦,细节方面还要慢慢打磨。比如我的总会在所有视频转换完后,再继续转换一个不存在的空视频。当时自己赶着用就懒得改了。反正不影响整体功能,再加上也不发布,就是自己用。

除了这些基本功能外,软件对中文路径和文件的支持也还不错。当然,的确出现了个别以生僻字命名的文件无法识别的情况。这也是在我测试很多个文件后无意发现的。软件每次退出都会保存上一次的设置,方便下次载入。它每转换成功一个,就会在程序根目录的日志文件里添加一条记录。

首次写这样的文章,还是新手,大家海涵:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: