Implemented rudimental encoding support.

This commit is contained in:
LoRd_MuldeR 2012-01-29 21:31:09 +01:00
parent 055b9ef673
commit 65c7aba23a
10 changed files with 331 additions and 48 deletions

View File

@ -163,6 +163,63 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="labelBuildDate">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>126</red>
<green>126</green>
<blue>126</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>126</red>
<green>126</green>
<blue>126</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="text">
<string>(Version)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QPushButton" name="buttonStartJob"> <widget class="QPushButton" name="buttonStartJob">
<property name="enabled"> <property name="enabled">

View File

@ -322,7 +322,7 @@ void JobListModel::updateProgress(const QUuid &jobId, unsigned int newProgress)
if((index = m_jobs.indexOf(jobId)) >= 0) if((index = m_jobs.indexOf(jobId)) >= 0)
{ {
m_progress.insert(jobId, newProgress); m_progress.insert(jobId, qBound(0U, newProgress, 100U));
emit dataChanged(createIndex(index, 2), createIndex(index, 2)); emit dataChanged(createIndex(index, 2), createIndex(index, 2));
} }
} }

View File

@ -26,6 +26,8 @@
LogFileModel::LogFileModel(void) LogFileModel::LogFileModel(void)
{ {
m_lines << "Job not started yet.";
m_firstLine = true;
} }
LogFileModel::~LogFileModel(void) LogFileModel::~LogFileModel(void)
@ -81,10 +83,18 @@ QVariant LogFileModel::data(const QModelIndex &index, int role) const
void LogFileModel::addLogMessage(const QUuid &jobId, const QString &text) void LogFileModel::addLogMessage(const QUuid &jobId, const QString &text)
{ {
beginInsertRows(QModelIndex(), m_lines.count(), m_lines.count()); beginInsertRows(QModelIndex(), m_lines.count(), m_lines.count());
if(m_firstLine)
{
m_firstLine = false;
m_lines.clear();
}
QStringList lines = text.split("\n"); QStringList lines = text.split("\n");
for(int i = 0; i < lines.count(); i++) for(int i = 0; i < lines.count(); i++)
{ {
m_lines.append(lines.at(i)); m_lines.append(lines.at(i));
} }
endInsertRows(); endInsertRows();
} }

View File

@ -45,6 +45,7 @@ public:
protected: protected:
QStringList m_lines; QStringList m_lines;
bool m_firstLine;
public slots: public slots:
void addLogMessage(const QUuid &jobId, const QString &text); void addLogMessage(const QUuid &jobId, const QString &text);

View File

@ -26,13 +26,34 @@
#include <QDate> #include <QDate>
#include <QTime> #include <QTime>
#include <QProcess>
#include <QMutex>
#include <QLibrary>
EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options) /*
* Win32 API definitions
*/
typedef HANDLE (WINAPI *CreateJobObjectFun)(__in_opt LPSECURITY_ATTRIBUTES lpJobAttributes, __in_opt LPCSTR lpName);
typedef BOOL (WINAPI *SetInformationJobObjectFun)(__in HANDLE hJob, __in JOBOBJECTINFOCLASS JobObjectInformationClass, __in_bcount(cbJobObjectInformationLength) LPVOID lpJobObjectInformation, __in DWORD cbJobObjectInformationLength);
typedef BOOL (WINAPI *AssignProcessToJobObjectFun)(__in HANDLE hJob, __in HANDLE hProcess);
/*
* Static vars
*/
QMutex EncodeThread::m_mutex_startProcess;
HANDLE EncodeThread::m_handle_jobObject = NULL;
///////////////////////////////////////////////////////////////////////////////
// Constructor & Destructor
///////////////////////////////////////////////////////////////////////////////
EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir)
: :
m_jobId(QUuid::createUuid()), m_jobId(QUuid::createUuid()),
m_sourceFileName(sourceFileName), m_sourceFileName(sourceFileName),
m_outputFileName(outputFileName), m_outputFileName(outputFileName),
m_options(new OptionsModel(*options)) m_options(new OptionsModel(*options)),
m_binDir(binDir)
{ {
m_abort = false; m_abort = false;
} }
@ -62,9 +83,13 @@ void EncodeThread::run(void)
} }
} }
///////////////////////////////////////////////////////////////////////////////
// Encode functions
///////////////////////////////////////////////////////////////////////////////
void EncodeThread::encode(void) void EncodeThread::encode(void)
{ {
Sleep(1500); Sleep(500);
//Print some basic info //Print some basic info
log(tr("Job started at %1, %2.\n").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate))); log(tr("Job started at %1, %2.\n").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
@ -76,51 +101,157 @@ void EncodeThread::encode(void)
log(tr("Tuning: %1").arg(m_options->tune())); log(tr("Tuning: %1").arg(m_options->tune()));
log(tr("Profile: %1").arg(m_options->profile())); log(tr("Profile: %1").arg(m_options->profile()));
log(tr("Custom: %1").arg(m_options->custom().isEmpty() ? tr("(None)") : m_options->custom())); log(tr("Custom: %1").arg(m_options->custom().isEmpty() ? tr("(None)") : m_options->custom()));
//Detect source info
log(tr("\n[Input Properties]")); log(tr("\n[Input Properties]"));
log(tr("Not implemented yet, sorry ;-)\n"));
for(int i = 0; i <= 100; i += 5) QStringList cmdLine;
{ QProcess process;
emit progressChanged(m_jobId, i);
emit statusChanged(m_jobId, (i % 2) ? JobStatus_Indexing : JobStatus_Running_Pass1);
emit messageLogged(m_jobId, QUuid::createUuid().toString());
for(int j = 0; j < 3; j++) cmdLine = buildCommandLine();
{
emit detailsChanged(m_jobId, QUuid::createUuid().toString());
Sleep(120);
}
if(m_abort) log("Creating process:");
if(!startProcess(process, QString("%1/x264.exe").arg(m_binDir), cmdLine))
{ {
Sleep(500); emit statusChanged(m_jobId, JobStatus_Failed);
emit statusChanged(m_jobId, JobStatus_Aborted);
return; return;
} }
}
Sleep(1500); emit statusChanged(m_jobId, JobStatus_Running);
QRegExp regExp("\\[(\\d+)\\.\\d+%\\].+frames");
for(int i = 0; i <= 100; i += 5) bool bTimeout = false;
bool bAborted = false;
while(process.state() != QProcess::NotRunning)
{ {
emit progressChanged(m_jobId, i);
emit statusChanged(m_jobId, (i % 2) ? JobStatus_Indexing : JobStatus_Running_Pass2);
emit messageLogged(m_jobId, QUuid::createUuid().toString());
for(int j = 0; j < 3; j++)
{
emit detailsChanged(m_jobId, QUuid::createUuid().toString());
Sleep(120);
}
if(m_abort) if(m_abort)
{ {
Sleep(500); process.kill();
emit statusChanged(m_jobId, JobStatus_Aborted); bAborted = true;
log("\nABORTED BY USER !!!");
break;
}
process.waitForReadyRead(m_processTimeoutInterval);
if(!process.bytesAvailable() && process.state() == QProcess::Running)
{
process.kill();
qWarning("x264 process timed out <-- killing!");
log("\nPROCESS TIMEOUT !!!");
bTimeout = true;
break;
}
while(process.bytesAvailable() > 0)
{
QByteArray line = process.readLine();
QString text = QString::fromUtf8(line.constData()).simplified();
if(regExp.lastIndexIn(text) >= 0)
{
bool ok = false;
unsigned int progress = regExp.cap(1).toUInt(&ok);
if(ok)
{
emit progressChanged(m_jobId, progress);
emit detailsChanged(m_jobId, line);
}
}
else if(!text.isEmpty())
{
log(text);
}
}
}
process.waitForFinished();
if(process.state() != QProcess::NotRunning)
{
process.kill();
process.waitForFinished(-1);
}
if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
{
emit statusChanged(m_jobId, JobStatus_Failed);
return; return;
} }
}
Sleep(250);
emit progressChanged(m_jobId, 100);
emit statusChanged(m_jobId, JobStatus_Completed); emit statusChanged(m_jobId, JobStatus_Completed);
} }
QStringList EncodeThread::buildCommandLine(void)
{
QStringList cmdLine;
cmdLine << "--crf" << QString::number(m_options->quantizer());
if(m_options->tune().compare("none", Qt::CaseInsensitive))
{
cmdLine << "--tune" << m_options->tune().toLower();
}
cmdLine << "--preset" << m_options->preset().toLower();
cmdLine << "--output" << m_outputFileName;
cmdLine << m_sourceFileName;
return cmdLine;
}
///////////////////////////////////////////////////////////////////////////////
// Misc functions
///////////////////////////////////////////////////////////////////////////////
bool EncodeThread::startProcess(QProcess &process, const QString &program, const QStringList &args)
{
static AssignProcessToJobObjectFun AssignProcessToJobObjectPtr = NULL;
QMutexLocker lock(&m_mutex_startProcess);
log(commandline2string(program, args) + "\n");
if(!AssignProcessToJobObjectPtr)
{
QLibrary Kernel32Lib("kernel32.dll");
AssignProcessToJobObjectPtr = (AssignProcessToJobObjectFun) Kernel32Lib.resolve("AssignProcessToJobObject");
}
process.setProcessChannelMode(QProcess::MergedChannels);
process.setReadChannel(QProcess::StandardOutput);
process.start(program, args);
if(process.waitForStarted())
{
if(AssignProcessToJobObjectPtr)
{
AssignProcessToJobObjectPtr(m_handle_jobObject, process.pid()->hProcess);
}
if(!SetPriorityClass(process.pid()->hProcess, BELOW_NORMAL_PRIORITY_CLASS))
{
SetPriorityClass(process.pid()->hProcess, IDLE_PRIORITY_CLASS);
}
lock.unlock();
return true;
}
log("Process creation has failed :-(");
QString errorMsg= process.errorString().trimmed();
if(!errorMsg.isEmpty()) log(errorMsg);
process.kill();
process.waitForFinished(-1);
return false;
}
QString EncodeThread::commandline2string(const QString &program, const QStringList &arguments)
{
QString commandline = (program.contains(' ') ? QString("\"%1\"").arg(program) : program);
for(int i = 0; i < arguments.count(); i++)
{
commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
}
return commandline;
}

View File

@ -23,8 +23,11 @@
#include <QThread> #include <QThread>
#include <QUuid> #include <QUuid>
#include <QMutex>
#include <QStringList>
class OptionsModel; class OptionsModel;
class QProcess;
class EncodeThread : public QThread class EncodeThread : public QThread
{ {
@ -45,7 +48,7 @@ public:
JobStatus_Aborted = 9 JobStatus_Aborted = 9
}; };
EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options); EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir);
~EncodeThread(void); ~EncodeThread(void);
QUuid getId(void) { return this->m_jobId; }; QUuid getId(void) { return this->m_jobId; };
@ -55,19 +58,29 @@ public:
void abortJob(void) { m_abort = true; } void abortJob(void) { m_abort = true; }
protected: protected:
static QMutex m_mutex_startProcess;
static void *m_handle_jobObject;
static const int m_processTimeoutInterval = 600000;
const QUuid m_jobId; const QUuid m_jobId;
const QString m_sourceFileName; const QString m_sourceFileName;
const QString m_outputFileName; const QString m_outputFileName;
const OptionsModel *m_options; const OptionsModel *m_options;
const QString m_binDir;
volatile bool m_abort; volatile bool m_abort;
virtual void run(void); virtual void run(void);
//Encode functions //Encode functions
void encode(void); void encode(void);
QStringList buildCommandLine(void);
//Auxiallary Stuff //Auxiallary Stuff
void log(const QString &text) { emit messageLogged(m_jobId, text); } void log(const QString &text) { emit messageLogged(m_jobId, text); }
bool startProcess(QProcess &process, const QString &program, const QStringList &args);
static QString commandline2string(const QString &program, const QStringList &arguments);
signals: signals:
void statusChanged(const QUuid &jobId, EncodeThread::JobStatus newStatus); void statusChanged(const QUuid &jobId, EncodeThread::JobStatus newStatus);

View File

@ -31,6 +31,7 @@
#include <QFileDialog> #include <QFileDialog>
#include <QDesktopServices> #include <QDesktopServices>
#include <QValidator> #include <QValidator>
#include <QDir>
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Validator // Validator
@ -175,7 +176,7 @@ void AddJobDialog::browseButtonClicked(void)
if(!(filePath.isNull() || filePath.isEmpty())) if(!(filePath.isNull() || filePath.isEmpty()))
{ {
editSource->setText(filePath); editSource->setText(QDir::toNativeSeparators(filePath));
QString path = QFileInfo(filePath).path(); QString path = QFileInfo(filePath).path();
QString name = QFileInfo(filePath).completeBaseName(); QString name = QFileInfo(filePath).completeBaseName();
@ -191,7 +192,7 @@ void AddJobDialog::browseButtonClicked(void)
} }
} }
editOutput->setText(outPath); editOutput->setText(QDir::toNativeSeparators(outPath));
} }
} }
else if(QObject::sender() == buttonBrowseOutput) else if(QObject::sender() == buttonBrowseOutput)
@ -205,11 +206,25 @@ void AddJobDialog::browseButtonClicked(void)
if(!(filePath.isNull() || filePath.isEmpty())) if(!(filePath.isNull() || filePath.isEmpty()))
{ {
editOutput->setText(filePath); editOutput->setText(QDir::toNativeSeparators(filePath));
} }
} }
} }
///////////////////////////////////////////////////////////////////////////////
// Public functions
///////////////////////////////////////////////////////////////////////////////
QString AddJobDialog::sourceFile(void)
{
return QDir::fromNativeSeparators(editSource->text());
}
QString AddJobDialog::outputFile(void)
{
return QDir::fromNativeSeparators(editOutput->text());
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Private functions // Private functions
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -33,8 +33,8 @@ public:
AddJobDialog(QWidget *parent, OptionsModel *options); AddJobDialog(QWidget *parent, OptionsModel *options);
~AddJobDialog(void); ~AddJobDialog(void);
QString sourceFile(void) { return editSource->text(); } QString sourceFile(void);
QString outputFile(void) { return editOutput->text(); } QString outputFile(void);
QString preset(void) { return cbxPreset->itemText(cbxPreset->currentIndex()); } QString preset(void) { return cbxPreset->itemText(cbxPreset->currentIndex()); }
QString tuning(void) { return cbxTuning->itemText(cbxTuning->currentIndex()); } QString tuning(void) { return cbxTuning->itemText(cbxTuning->currentIndex()); }
QString profile(void) { return cbxProfile->itemText(cbxProfile->currentIndex()); } QString profile(void) { return cbxProfile->itemText(cbxProfile->currentIndex()); }

View File

@ -32,6 +32,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QDesktopServices> #include <QDesktopServices>
#include <QUrl> #include <QUrl>
#include <QDir>
const char *home_url = "http://mulder.brhack.net/"; const char *home_url = "http://mulder.brhack.net/";
@ -41,7 +42,9 @@ const char *home_url = "http://mulder.brhack.net/";
MainWindow::MainWindow(bool x64supported) MainWindow::MainWindow(bool x64supported)
: :
m_x64supported(x64supported) m_x64supported(x64supported),
m_appDir(QApplication::applicationDirPath()),
m_firstShow(true)
{ {
//Init the dialog, from the .ui file //Init the dialog, from the .ui file
setupUi(this); setupUi(this);
@ -55,7 +58,7 @@ MainWindow::MainWindow(bool x64supported)
setMinimumSize(size()); setMinimumSize(size());
//Update title //Update title
setWindowTitle(QString("%1 [%2]").arg(windowTitle(), x264_version_date().toString(Qt::ISODate))); labelBuildDate->setText(tr("Built on %1 at %2").arg(x264_version_date().toString(Qt::ISODate), QString::fromLatin1(x264_version_time())));
if(m_x64supported) setWindowTitle(QString("%1 (x64)").arg(windowTitle())); if(m_x64supported) setWindowTitle(QString("%1 (x64)").arg(windowTitle()));
//Create model //Create model
@ -94,6 +97,12 @@ MainWindow::~MainWindow(void)
{ {
X264_DELETE(m_jobList); X264_DELETE(m_jobList);
X264_DELETE(m_options); X264_DELETE(m_options);
while(!m_toolsList.isEmpty())
{
QFile *temp = m_toolsList.takeFirst();
X264_DELETE(temp);
}
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -112,7 +121,8 @@ void MainWindow::addButtonPressed(void)
( (
addDialog->sourceFile(), addDialog->sourceFile(),
addDialog->outputFile(), addDialog->outputFile(),
m_options m_options,
QString("%1/toolset").arg(m_appDir)
); );
QModelIndex newIndex = m_jobList->insertJob(thrd); QModelIndex newIndex = m_jobList->insertJob(thrd);
@ -256,10 +266,48 @@ void MainWindow::launchNextJob(void)
qWarning("No enqueued jobs left!"); qWarning("No enqueued jobs left!");
} }
void MainWindow::init(void)
{
static const char *binFiles = "x264.exe:x264_x64.exe:avs2yuv.exe:pipebuf.exe";
QStringList binaries = QString::fromLatin1(binFiles).split(":", QString::SkipEmptyParts);
while(!binaries.isEmpty())
{
QString current = binaries.takeFirst();
QFile *file = new QFile(QString("%1/toolset/%2").arg(m_appDir, current));
if(file->open(QIODevice::ReadOnly))
{
m_toolsList << file;
}
else
{
X264_DELETE(file);
QMessageBox::critical(this, tr("File Not Found!"), tr("<nobr>At least on required tool could not be found:<br>%1<br><br>Please re-install the program in order to fix the problem!</nobr>").arg(QDir::toNativeSeparators(QString("%1/toolset/%2").arg(m_appDir, current))).replace("-", "&minus;"));
qFatal(QString("Binary not found: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData());
return;
}
}
qsrand(time(NULL)); int rnd = qrand() % 3;
int val = QMessageBox::warning(this, tr("Pre-Release Version"), tr("Note: This is a pre-release version. Please do NOT use for production!<br><br>Click the button #%1 in order to continue...").arg(QString::number(rnd + 1)), tr("(1)"), tr("(2)"), tr("(3)"), qrand() % 3);
if(rnd != val) { close(); }
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Event functions // Event functions
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void MainWindow::showEvent(QShowEvent *e)
{
QMainWindow::showEvent(e);
if(m_firstShow)
{
m_firstShow = false;
QTimer::singleShot(0, this, SLOT(init()));
}
}
void MainWindow::closeEvent(QCloseEvent *e) void MainWindow::closeEvent(QCloseEvent *e)
{ {
if(havePendingJobs()) if(havePendingJobs())

View File

@ -26,6 +26,7 @@
class JobListModel; class JobListModel;
class OptionsModel; class OptionsModel;
class QFile;
class MainWindow: public QMainWindow, private Ui::MainWindow class MainWindow: public QMainWindow, private Ui::MainWindow
{ {
@ -37,11 +38,17 @@ public:
protected: protected:
virtual void closeEvent(QCloseEvent *e); virtual void closeEvent(QCloseEvent *e);
virtual void showEvent(QShowEvent *e);
private: private:
bool m_firstShow;
JobListModel *m_jobList; JobListModel *m_jobList;
OptionsModel *m_options; OptionsModel *m_options;
QList<QFile*> m_toolsList;
const bool m_x64supported; const bool m_x64supported;
const QString m_appDir;
void updateButtons(EncodeThread::JobStatus status); void updateButtons(EncodeThread::JobStatus status);
bool havePendingJobs(void); bool havePendingJobs(void);
@ -50,6 +57,7 @@ private slots:
void addButtonPressed(void); void addButtonPressed(void);
void startButtonPressed(void); void startButtonPressed(void);
void abortButtonPressed(void); void abortButtonPressed(void);
void init(void);
void jobSelected(const QModelIndex & current, const QModelIndex & previous); void jobSelected(const QModelIndex & current, const QModelIndex & previous);
void jobChangedData(const QModelIndex &top, const QModelIndex &bottom); void jobChangedData(const QModelIndex &top, const QModelIndex &bottom);
void jobLogExtended(const QModelIndex & parent, int start, int end); void jobLogExtended(const QModelIndex & parent, int start, int end);