<tabstop>buttonBrowseSource</tabstop>
<tabstop>editOutput</tabstop>
<tabstop>buttonBrowseOutput</tabstop>
+ <tabstop>cbxTemplate</tabstop>
+ <tabstop>buttonSaveTemplate</tabstop>
+ <tabstop>buttonDeleteTemplate</tabstop>
<tabstop>cbxRateControlMode</tabstop>
<tabstop>spinQuantizer</tabstop>
<tabstop>spinBitrate</tabstop>
<tabstop>cbxPreset</tabstop>
<tabstop>cbxTuning</tabstop>
<tabstop>cbxProfile</tabstop>
+ <tabstop>editCustomParams</tabstop>
<tabstop>checkBoxRun</tabstop>
<tabstop>buttonAccept</tabstop>
<tabstop>buttonCancel</tabstop>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
- <x>587</x>
- <y>406</y>
+ <x>495</x>
+ <y>558</y>
</hint>
<hint type="destinationlabel">
<x>397</x>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
- <x>721</x>
- <y>406</y>
+ <x>629</x>
+ <y>558</y>
</hint>
<hint type="destinationlabel">
<x>397</x>
<bool>false</bool>
</property>
<widget class="QTableView" name="jobsView">
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<addaction name="actionJob_Start"/>
<addaction name="actionJob_Pause"/>
<addaction name="actionJob_Abort"/>
+ <addaction name="actionJob_Browse"/>
+ <addaction name="actionJob_Delete"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuJob"/>
<string>Preferences</string>
</property>
</action>
+ <action name="actionJob_Delete">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="../res/resources.qrc">
+ <normaloff>:/buttons/trash.png</normaloff>:/buttons/trash.png</iconset>
+ </property>
+ <property name="text">
+ <string>Delete Job</string>
+ </property>
+ </action>
+ <action name="actionJob_Browse">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="../res/resources.qrc">
+ <normaloff>:/buttons/folder_magnify.png</normaloff>:/buttons/folder_magnify.png</iconset>
+ </property>
+ <property name="text">
+ <string>Explore Job</string>
+ </property>
+ </action>
</widget>
<tabstops>
<tabstop>buttonAddJob</tabstop>
<file>buttons/error.png</file>
<file>buttons/exclamation.png</file>
<file>buttons/find.png</file>
+ <file>buttons/folder_magnify.png</file>
<file>buttons/hourglass.png</file>
<file>buttons/information.png</file>
<file>buttons/lightning.png</file>
<file>buttons/play.png</file>
<file>buttons/play_big.png</file>
<file>buttons/suspended.png</file>
+ <file>buttons/trash.png</file>
<file>buttons/world_link.png</file>
<file>buttons/wrench.png</file>
<file>images/x264.png</file>
*/
void x264_init_console(int argc, char* argv[])
{
- bool enableConsole = true; //x264_version_demo();
+ bool enableConsole = x264_is_prerelease();
if(_environ)
{
return g_x264_version.ver_time;
}
+bool x264_is_prerelease(void)
+{
+ return (VER_x264_PRE_RELEASE);
+}
+
/*
* Detect CPU features
*/
unsigned int x264_version_major(void);
unsigned int x264_version_minor(void);
const QDate &x264_version_date(void);
+bool x264_is_prerelease(void);
const char *x264_version_time(void);
const char *x264_version_compiler(void);
const char *x264_version_arch(void);
return false;
}
+bool JobListModel::deleteJob(const QModelIndex &index)
+{
+ if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ {
+ QUuid id = m_jobs.at(index.row());
+ if(m_status.value(id) == EncodeThread::JobStatus_Completed || m_status.value(id) == EncodeThread::JobStatus_Failed ||
+ m_status.value(id) == EncodeThread::JobStatus_Aborted || m_status.value(id) == EncodeThread::JobStatus_Enqueued)
+ {
+ int idx = index.row();
+ QUuid id = m_jobs.at(idx);
+ EncodeThread *thread = m_threads.value(id, NULL);
+ LogFileModel *logFile = m_logFile.value(id, NULL);
+ if((thread == NULL) || (!thread->isRunning()))
+ {
+
+ beginRemoveRows(QModelIndex(), idx, idx);
+ m_jobs.removeAt(index.row());
+ m_name.remove(id);
+ m_threads.remove(id);
+ m_status.remove(id);
+ m_progress.remove(id);
+ m_logFile.remove(id);
+ m_details.remove(id);
+ endRemoveRows();
+ X264_DELETE(thread);
+ X264_DELETE(logFile);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
LogFileModel *JobListModel::getLogFile(const QModelIndex &index)
{
if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
return NULL;
}
+const QString &JobListModel::getJobOutputFile(const QModelIndex &index)
+{
+ static QString nullStr;
+
+ if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ {
+ EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
+ return (thread != NULL) ? thread->outputFileName() : nullStr;
+ }
+
+ return nullStr;
+}
+
EncodeThread::JobStatus JobListModel::getJobStatus(const QModelIndex &index)
{
if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
bool pauseJob(const QModelIndex &index);
bool resumeJob(const QModelIndex &index);
bool abortJob(const QModelIndex &index);
+ bool deleteJob(const QModelIndex &index);
LogFileModel *getLogFile(const QModelIndex &index);
+ const QString &getJobOutputFile(const QModelIndex &index);
EncodeThread::JobStatus getJobStatus(const QModelIndex &index);
unsigned int getJobProgress(const QModelIndex &index);
QModelIndex getJobIndexById(const QUuid &id);
#include <QDir>
#include <QProcess>
#include <QMutex>
-#include <QLibrary>
-
-/*
- * 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
if(m_handle_jobObject)
{
- CloseHandle(m_handle_jobObject);
+ TerminateJobObject(m_handle_jobObject, 42);
m_handle_jobObject = NULL;
}
}
bool EncodeThread::startProcess(QProcess &process, const QString &program, const QStringList &args, bool mergeChannels)
{
- static AssignProcessToJobObjectFun AssignProcessToJobObjectPtr = NULL;
- static CreateJobObjectFun CreateJobObjectPtr = NULL;
- static SetInformationJobObjectFun SetInformationJobObjectPtr = NULL;
-
QMutexLocker lock(&m_mutex_startProcess);
log(commandline2string(program, args) + "\n");
//Create a new job object, if not done yet
if(!m_handle_jobObject)
{
- if(!CreateJobObjectPtr || !SetInformationJobObjectPtr)
+ m_handle_jobObject = CreateJobObject(NULL, NULL);
+ if(m_handle_jobObject == INVALID_HANDLE_VALUE)
{
- QLibrary Kernel32Lib("kernel32.dll");
- CreateJobObjectPtr = (CreateJobObjectFun) Kernel32Lib.resolve("CreateJobObjectA");
- SetInformationJobObjectPtr = (SetInformationJobObjectFun) Kernel32Lib.resolve("SetInformationJobObject");
+ m_handle_jobObject = NULL;
}
- if(CreateJobObjectPtr && SetInformationJobObjectPtr)
+ if(m_handle_jobObject)
{
- m_handle_jobObject = CreateJobObjectPtr(NULL, NULL);
- if(m_handle_jobObject == INVALID_HANDLE_VALUE)
- {
- m_handle_jobObject = NULL;
- }
- if(m_handle_jobObject)
- {
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo;
- memset(&jobExtendedLimitInfo, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
- jobExtendedLimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
- SetInformationJobObjectPtr(m_handle_jobObject, JobObjectExtendedLimitInformation, &jobExtendedLimitInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
- }
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo;
+ memset(&jobExtendedLimitInfo, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
+ jobExtendedLimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
+ SetInformationJobObject(m_handle_jobObject, JobObjectExtendedLimitInformation, &jobExtendedLimitInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
}
}
- //Initialize AssignProcessToJobObject function
- if(!AssignProcessToJobObjectPtr)
- {
- QLibrary Kernel32Lib("kernel32.dll");
- AssignProcessToJobObjectPtr = (AssignProcessToJobObjectFun) Kernel32Lib.resolve("AssignProcessToJobObject");
- }
-
if(mergeChannels)
{
process.setProcessChannelMode(QProcess::MergedChannels);
if(process.waitForStarted())
{
- if(AssignProcessToJobObjectPtr)
+ Q_PID pid = process.pid();
+ AssignProcessToJobObject(m_handle_jobObject, process.pid()->hProcess);
+ if(pid != NULL)
{
- AssignProcessToJobObjectPtr(m_handle_jobObject, process.pid()->hProcess);
- }
- if(!SetPriorityClass(process.pid()->hProcess, BELOW_NORMAL_PRIORITY_CLASS))
- {
- SetPriorityClass(process.pid()->hProcess, IDLE_PRIORITY_CLASS);
+ if(!SetPriorityClass(process.pid()->hProcess, BELOW_NORMAL_PRIORITY_CLASS))
+ {
+ SetPriorityClass(process.pid()->hProcess, IDLE_PRIORITY_CLASS);
+ }
}
lock.unlock();
JobStatus_Paused = 9,
JobStatus_Resuming = 10,
JobStatus_Aborting = 11,
- JobStatus_Aborted = 12
+ JobStatus_Aborted = 12,
+ JobStatus_Undefined = 666
};
EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, bool x64);
#define VER_X264_MINIMUM_REV (2146)
#define VER_X264_CURRENT_API (120)
#define VER_x264_AVS2YUV_VER (242)
+
+#define VER_x264_PRE_RELEASE (0)
if(!editSource->text().isEmpty())
{
generateOutputFileName(QDir::fromNativeSeparators(editSource->text()));
+ buttonAccept->setFocus();
}
}
#include <QUrl>
#include <QDir>
#include <QLibrary>
+#include <QProcess>
-const char *home_url = "http://mulder.brhack.net/";
+//#include <Shellapi.h>
-#define PRE_RELEASE (0)
+const char *home_url = "http://mulder.brhack.net/";
#define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); }
#define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); }
labelBuildDate->setText(tr("Built on %1 at %2").arg(x264_version_date().toString(Qt::ISODate), QString::fromLatin1(x264_version_time())));
labelBuildDate->installEventFilter(this);
setWindowTitle(QString("%1 (%2 Mode)").arg(windowTitle(), m_x64supported ? "64-Bit" : "32-Bit"));
- if(PRE_RELEASE) setWindowTitle(QString("%1 | PRE-RELEASE VERSION").arg(windowTitle()));
+ if(x264_is_prerelease()) setWindowTitle(QString("%1 | PRE-RELEASE VERSION").arg(windowTitle()));
//Create model
//Create context menu
QAction *actionClipboard = new QAction(QIcon(":/buttons/page_paste.png"), tr("Copy to Clipboard"), logView);
+ actionClipboard->setEnabled(false);
logView->addAction(actionClipboard);
connect(actionClipboard, SIGNAL(triggered(bool)), this, SLOT(copyLogToClipboard(bool)));
jobsView->addActions(menuJob->actions());
connect(buttonStartJob, SIGNAL(clicked()), this, SLOT(startButtonPressed()));
connect(buttonAbortJob, SIGNAL(clicked()), this, SLOT(abortButtonPressed()));
connect(buttonPauseJob, SIGNAL(toggled(bool)), this, SLOT(pauseButtonPressed(bool)));
+ connect(actionJob_Delete, SIGNAL(triggered()), this, SLOT(deleteButtonPressed()));
+ connect(actionJob_Browse, SIGNAL(triggered()), this, SLOT(browseButtonPressed()));
//Enable menu
connect(actionAbout, SIGNAL(triggered()), this, SLOT(showAbout()));
m_jobList->startJob(newIndex);
}
- m_label->setVisible(false);
if(ok) *ok = true;
}
}
+ m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0);
X264_DELETE(addDialog);
}
m_jobList->abortJob(jobsView->currentIndex());
}
+void MainWindow::deleteButtonPressed(void)
+{
+ m_jobList->deleteJob(jobsView->currentIndex());
+ m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0);
+}
+
+void MainWindow::browseButtonPressed(void)
+{
+ QString outputFile = m_jobList->getJobOutputFile(jobsView->currentIndex());
+ if((!outputFile.isEmpty()) && QFileInfo(outputFile).exists() && QFileInfo(outputFile).isFile())
+ {
+ QProcess::startDetached(QString::fromLatin1("explorer.exe"), QStringList() << QString::fromLatin1("/select,") << QDir::toNativeSeparators(outputFile), QFileInfo(outputFile).path());
+ }
+ else
+ {
+ QMessageBox::warning(this, tr("Not Found"), tr("Sorry, the output file could not be found!"));
+ }
+}
+
void MainWindow::pauseButtonPressed(bool checked)
{
if(checked)
disconnect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
}
- logView->setModel(m_jobList->getLogFile(current));
- connect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
- QTimer::singleShot(0, logView, SLOT(scrollToBottom()));
+ if(current.isValid())
+ {
+ logView->setModel(m_jobList->getLogFile(current));
+ connect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
+ logView->actions().first()->setEnabled(true);
+ QTimer::singleShot(0, logView, SLOT(scrollToBottom()));
- progressBar->setValue(m_jobList->getJobProgress(current));
- editDetails->setText(m_jobList->data(m_jobList->index(current.row(), 3, QModelIndex()), Qt::DisplayRole).toString());
- updateButtons(m_jobList->getJobStatus(current));
+ progressBar->setValue(m_jobList->getJobProgress(current));
+ editDetails->setText(m_jobList->data(m_jobList->index(current.row(), 3, QModelIndex()), Qt::DisplayRole).toString());
+ updateButtons(m_jobList->getJobStatus(current));
+ }
+ else
+ {
+ logView->setModel(NULL);
+ logView->actions().first()->setEnabled(false);
+ progressBar->setValue(0);
+ editDetails->clear();
+ updateButtons(EncodeThread::JobStatus_Undefined);
+ }
progressBar->repaint();
}
case 0:
{
QString text2;
- text2 += tr("<nobr><tt>x264, the best H.264/AVC encoder. Copyright (c) 2003-2011 x264 project.<br>");
+ text2 += tr("<nobr><tt>x264 - the best H.264/AVC encoder. Copyright (c) 2003-2012 x264 project.<br>");
text2 += tr("Free software library for encoding video streams into the H.264/MPEG-4 AVC format.<br>");
text2 += tr("Released under the terms of the GNU General Public License.<br><br>");
- text2 += tr("Please visit <a href=\"http://x264licensing.com/\">http://x264licensing.com/</a> for obtaining a <u>commercial</u> x264 license!<br></tt></nobr>");
+ text2 += tr("Please visit <a href=\"%1\">%1</a> for obtaining a <u>commercial</u> x264 license!<br></tt></nobr>").arg("http://x264licensing.com/");
QMessageBox::information(this, tr("About x264"), text2.replace("-", "−"), tr("Close"));
}
break;
}
//Pre-release popup
- if(PRE_RELEASE)
+ if(x264_is_prerelease())
{
qsrand(time(NULL)); int rnd = qrand() % 3;
int val = QMessageBox::information(this, tr("Pre-Release Version"), tr("Note: This is a pre-release version. Please do NOT use for production!<br>Click the button #%1 in order to continue...<br><br>(There will be no such message box in the final version of this application)").arg(QString::number(rnd + 1)), tr("(1)"), tr("(2)"), tr("(3)"), qrand() % 3);
void MainWindow::closeEvent(QCloseEvent *e)
{
- if(havePendingJobs())
+ if(haveRunningJobs())
{
e->ignore();
- MessageBeep(MB_ICONWARNING);
+ QMessageBox::warning(this, tr("Jobs Are Running"), tr("Sorry, can not exit while there still are running jobs!"));
return;
}
+
+ if(havePendingJobs())
+ {
+ int ret = QMessageBox::question(this, tr("Jobs Are Pending"), tr("Do you really want to quit and discard the pending jobs?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ if(ret != QMessageBox::Yes)
+ {
+ e->ignore();
+ return;
+ }
+ }
+
+ while(m_jobList->rowCount(QModelIndex()) > 0)
+ {
+ qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
+ if(!m_jobList->deleteJob(m_jobList->index(0, 0, QModelIndex())))
+ {
+ e->ignore();
+ QMessageBox::warning(this, tr("Failed To Exit"), tr("Sorry, at least one job could not be deleted!"));
+ return;
+ }
+ }
+ qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
QMainWindow::closeEvent(e);
}
// Private functions
///////////////////////////////////////////////////////////////////////////////
+/*Jobs that are not completed (or failed, or aborted) yet*/
bool MainWindow::havePendingJobs(void)
{
const int rows = m_jobList->rowCount(QModelIndex());
return false;
}
+/*Jobs that are still active, i.e. not terminated or enqueued*/
+bool MainWindow::haveRunningJobs(void)
+{
+ const int rows = m_jobList->rowCount(QModelIndex());
+
+ for(int i = 0; i < rows; i++)
+ {
+ EncodeThread::JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
+ if(status != EncodeThread::JobStatus_Completed && status != EncodeThread::JobStatus_Aborted && status != EncodeThread::JobStatus_Failed && status != EncodeThread::JobStatus_Enqueued)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void MainWindow::updateButtons(EncodeThread::JobStatus status)
{
qDebug("MainWindow::updateButtons(void)");
buttonPauseJob->setEnabled(status == EncodeThread::JobStatus_Indexing || status == EncodeThread::JobStatus_Running || status == EncodeThread::JobStatus_Paused || status == EncodeThread::JobStatus_Running_Pass1 || status == EncodeThread::JobStatus_Running_Pass2);
buttonPauseJob->setChecked(status == EncodeThread::JobStatus_Paused || status == EncodeThread::JobStatus_Pausing);
+ actionJob_Delete->setEnabled(status == EncodeThread::JobStatus_Completed || status == EncodeThread::JobStatus_Aborted || status == EncodeThread::JobStatus_Failed || status == EncodeThread::JobStatus_Enqueued);
+ actionJob_Browse->setEnabled(status == EncodeThread::JobStatus_Completed);
+
actionJob_Start->setEnabled(buttonStartJob->isEnabled());
actionJob_Abort->setEnabled(buttonAbortJob->isEnabled());
actionJob_Pause->setEnabled(buttonPauseJob->isEnabled());
void updateButtons(EncodeThread::JobStatus status);
bool havePendingJobs(void);
+ bool haveRunningJobs(void);
private slots:
void addButtonPressed(const QString &filePath = QString(), bool *ok = NULL);
void abortButtonPressed(void);
+ void browseButtonPressed(void);
+ void deleteButtonPressed(void);
void copyLogToClipboard(bool checked);
void init(void);
void jobSelected(const QModelIndex & current, const QModelIndex & previous);