tsMuxer/tsMuxerGUI/tsmuxerwindow.cpp
2019-08-10 18:36:52 +01:00

2661 lines
92 KiB
C++

#include "tsmuxerwindow.h"
#include "lang_codes.h"
#include <QApplication>
#include <QUrl>
#include <QSettings>
#include <QMessageBox>
#include <QFontDialog>
#include <QColorDialog>
#ifdef WIN32
#include "windows.h"
#endif
const char fileDialogFilter[] =
"All supported media files (*.aac *.mpv *.mpa *.avc *.mvc *.264 *.h264 *.ac3 *.dts *.ts *.m2ts *.mts *.ssif *.mpg *.mpeg *.vob *.evo *.mkv *.mka *.mks *.mp4 *.m4a *.mov *.sup *.wav *.w64 *.pcm *.m1v *.m2v *.vc1 *.hevc *.hvc *.265 *.h265 *.mpls *.mpl *.srt);;\
AC3/E-AC3 (*.ac3 *.ddp);;\
AAC (advanced audio coding) (*.aac);;\
AVC/MVC/H.264 elementary stream (*.avc *.mvc *.264 *.h264);;\
HEVC (High Efficiency Video Codec) (*.hevc *.hvc *.265 *.h265);;\
Digital Theater System (*.dts);;\
Mpeg video elementary stream (*.mpv *.m1v *.m2v);;\
Mpeg audio elementary stream (*.mpa);;\
Transport Stream (*.ts);;\
BDAV Transport Stream (*.m2ts *.mts *.ssif);;\
Program Stream (*.mpg *.mpeg *.vob *.evo);;\
Matroska audio/video files (*.mkv *.mka *.mks);;\
MP4 audio/video files (*.mp4 *.m4a);;\
Quick time audio/video files (*.mov);;\
Blu-ray play list (*.mpls *.mpl);;\
Blu-ray PGS subtitles (*.sup);;\
Text subtitles (*.srt);;\
WAVE - Uncompressed PCM audio (*.wav *.w64);;\
RAW LPCM Stream (*.pcm);;\
All files (*.*)";
const char saveMetaFilter[] = "tsMuxeR project file (*.meta);;All files (*.*)";
const double EPS = 1e-6;
const char TI_DEFAULT_TAB_NAME[] = "General track options";
const char TI_DEMUX_TAB_NAME[] = "Demux options";
const char TS_SAVE_DIALOG_FILTER[] = "Transport stream (*.ts);;all files (*.*)";
const char M2TS_SAVE_DIALOG_FILTER[] = "BDAV Transport Stream (*.m2ts);;all files (*.*)";
const char ISO_SAVE_DIALOG_FILTER[] = "Disk image (*.iso);;all files (*.*)";
QSettings* settingsIniFile = nullptr;
QSettings* settings = nullptr;
enum FileCustomData
{
MplsItemRole = Qt::UserRole,
FileNameRole,
ChaptersRole,
FileDurationRole
};
static const QString FILE_JOIN_PREFIX(" ++ ");
bool doubleCompare(double a, double b)
{
return qAbs(a - b) < 1e-6;
}
QLocale locale;
QString closeDirPath(const QString& src)
{
if (src.isEmpty())
return src;
if (src[src.length()-1] == L'/' || src[src.length()-1] == L'\\')
return src;
return src + QDir::separator();
}
QString unquoteStr(QString val)
{
val = val.trimmed();
if (val.isEmpty())
return val;
if (val.at(0) == '\"') {
if (val.right(1) == "\"")
return val.mid(1, val.length()-2);
else
return val.mid(1, val.length()-1);
}
else {
if (val.right(1) == "\"")
return val.mid(0, val.length()-1);
else
return val;
}
}
bool isVideoCodec(const QString& displayName)
{
return displayName == "H.264" || displayName == "MVC" || displayName == "VC-1" || displayName == "MPEG-2" || displayName == "HEVC";
}
QString floatToTime(double time, char msSeparator = '.')
{
int iTime = (int) time;
int hour =iTime / 3600;
iTime -= hour*3600;
int min =iTime / 60;
iTime -= min*60;
int sec =iTime;
int msec = (int) ((time - (int)time)*1000.0);
QString str;
str += QString::number(hour).rightJustified(2,'0');
str +=':';
str += QString::number(min).rightJustified(2,'0');
str +=':';
str += QString::number(sec).rightJustified(2,'0');
str += msSeparator;
str += QString::number(msec).rightJustified(3,'0');
return str;
}
QTime qTimeFromFloat(double secondsF)
{
int seconds = (int) secondsF;
int ms = (secondsF - seconds) * 1000.0;
int hours = seconds / 3600;
seconds -= hours*3600;
int minute = seconds / 60;
seconds -= minute * 60;
return QTime(hours, minute, seconds, ms);
}
int qTimeToMsec(const QTime& time)
{
return (time.hour() * 3600 + time.minute() * 60 + time.second()) * 1000 + time.msec();
}
double timeToFloat(const QString& chapterStr)
{
if (chapterStr.size() == 0)
return 0;
QStringList timeParts = chapterStr.split(':');
double sec = 0;
if (timeParts.size() > 0)
sec = timeParts[timeParts.size()-1].toDouble();
int min = 0;
if (timeParts.size() > 1)
min = timeParts[timeParts.size()-2].toInt();
int hour = 0;
if (timeParts.size() > 2)
hour = timeParts[timeParts.size()-3].toInt();
return hour*3600 + min*60 + sec;
}
QString changeFileExt(const QString& value, const QString& newExt)
{
QFileInfo fi(unquoteStr(value));
if (fi.suffix().length() > 0 || (!value.isEmpty() && value.right(1) == "."))
return unquoteStr(value.left(value.length() - fi.suffix().length()) + newExt);
else
return unquoteStr(value) + "." + newExt;
}
QString toFixedDecPoint(QString str)
{
for (int i = 0; i < str.length(); ++i)
if (str[i] == ',')
str[i] = '.';
return str;
}
QString toNativeDecPoint(QString str)
{
for (int i = 0; i < str.length(); ++i)
if (str[i] == ',' || str[i] == '.')
str[i] = locale.decimalPoint();
return str;
}
float myStrToFloat(QString str)
{
for (int i = 0; i < str.length(); ++i)
if (str[i] == ',' || str[i] == '.')
str[i] = locale.decimalPoint();
return str.toFloat();
}
QString myFloatToStr(float val)
{
QString str = QString::number(double(val));
for (int i = 0; i < str.length(); ++i)
if (str[i] == locale.decimalPoint())
str[i] = '.';
return str;
}
QString fpsTextToFpsStr(const QString& fpsText)
{
int p = fpsText.indexOf('/');
if (p >= 0) {
QString leftStr = fpsText.mid(0, p);
QString rightStr = fpsText.mid(p+1);
return toFixedDecPoint(QString::number(double(myStrToFloat(leftStr) / myStrToFloat(rightStr)), 'f', 3));
}
else
return myFloatToStr(myStrToFloat(fpsText));
}
float extractFloatFromDescr(const QString& str, const QString& pattern)
{
try {
int p = 0;
if (!pattern.isEmpty())
p = str.indexOf(pattern);
if (p >= 0) {
p += pattern.length();
int p2 = p;
while (p2 < str.length() && ( (str.at(p2) >= '0' && str.at(p2) <= '9') || str.at(p2) == '.' || str.at(p2) == '-'))
p2++;
QString tmp = str.mid(p, p2-p);
return myStrToFloat(tmp);
}
} catch(...) {
return 0;
}
return 0;
}
QString quoteStr(const QString& val)
{
QString rez;
if(val.isEmpty())
return "";
if (val.at(0) != '\"')
rez = '\"' + val;
else
rez = val;
if (val.at(val.length()-1) != '\"')
rez += '\"';
return rez;
}
QString myUnquoteStr(const QString& val)
{
return unquoteStr(val);
}
// -------------------------------------------------------------------------- //
// QnCheckBoxedHeaderView
// -------------------------------------------------------------------------- //
QnCheckBoxedHeaderView::QnCheckBoxedHeaderView(QWidget *parent):
base_type(Qt::Horizontal, parent),
m_checkState(Qt::Unchecked),
m_checkColumnIndex(0)
{
connect(this, SIGNAL(sectionClicked(int)), this, SLOT(at_sectionClicked(int)));
}
Qt::CheckState QnCheckBoxedHeaderView::checkState() const {
return m_checkState;
}
void QnCheckBoxedHeaderView::setCheckState(Qt::CheckState state) {
if (state == m_checkState)
return;
m_checkState = state;
emit checkStateChanged(state);
viewport()->update();
}
void QnCheckBoxedHeaderView::paintEvent(QPaintEvent *e) {
base_type::paintEvent(e);
}
void QnCheckBoxedHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
painter->save();
base_type::paintSection(painter, rect, logicalIndex);
painter->restore();
if (logicalIndex == m_checkColumnIndex) {
if (!rect.isValid())
return;
QStyleOptionButton opt;
opt.initFrom(this);
QStyle::State state = QStyle::State_Raised;
if (isEnabled())
state |= QStyle::State_Enabled;
if (window()->isActiveWindow())
state |= QStyle::State_Active;
switch(m_checkState) {
case Qt::Checked:
state |= QStyle::State_On;
break;
case Qt::Unchecked:
state |= QStyle::State_Off;
break;
default:
state |= QStyle::State_NoChange;
break;
}
opt.rect = rect.adjusted(4, 0, 0, 0);
opt.state |= state;
opt.text = QString();
painter->save();
style()->drawControl(QStyle::CE_CheckBox, &opt, painter, this);
painter->restore();
return;
}
}
QSize QnCheckBoxedHeaderView::sectionSizeFromContents(int logicalIndex) const {
QSize size = base_type::sectionSizeFromContents(logicalIndex);
if (logicalIndex != m_checkColumnIndex)
return size;
size.setWidth(15);
return size;
}
void QnCheckBoxedHeaderView::at_sectionClicked(int logicalIndex) {
if (logicalIndex != m_checkColumnIndex)
return;
if (m_checkState != Qt::Checked)
setCheckState(Qt::Checked);
else
setCheckState(Qt::Unchecked);
}
// ----------------------- TsMuxerWindow -------------------------------------
QString TsMuxerWindow::getOutputDir() const
{
QString result = ui.radioButtonOutoutInInput->isChecked() ? lastSourceDir : lastOutputDir;
if (!result.isEmpty())
result = QDir::toNativeSeparators(closeDirPath(result));
return result;
}
QString TsMuxerWindow::getDefaultOutputFileName() const
{
QString prefix = getOutputDir();
if (ui.radioButtonTS->isChecked())
return prefix + QString("default.ts");
else if (ui.radioButtonM2TS->isChecked())
return prefix + QString("default.m2ts");
else if (ui.radioButtonBluRayISO->isChecked())
return prefix + QString("default.iso");
else
return prefix;
}
TsMuxerWindow::TsMuxerWindow():
disableUpdatesCnt(0),
outFileNameModified(false),
outFileNameDisableChange(false),
muxForm(this),
tempSoundFile(0),
sound(0),
m_updateMeta(true),
m_3dMode(false)
{
ui.setupUi(this);
lastInputDir = QDir::homePath();
lastOutputDir = QDir::homePath();
QString path = QFileInfo(QApplication::arguments()[0]).absolutePath();
QString iniName = QDir::toNativeSeparators(path) + QDir::separator() + QString("tsMuxerGUI.ini");
settings = new QSettings(QSettings::UserScope, "Network Optix", "tsMuxeR");
readSettings();
if (QFile::exists(iniName)) {
delete settings;
settings = new QSettings(iniName, QSettings::IniFormat);
settings->setIniCodec("UTF-8");
if (!readSettings())
writeSettings(); // copy current registry settings to the ini file
}
ui.outFileName->setText(getDefaultOutputFileName());
// next properties supported by Designer in version 4.5 only.
ui.listViewFont->horizontalHeader()->setVisible(false);
ui.listViewFont->verticalHeader()->setVisible(false);
ui.listViewFont->horizontalHeader()->setStretchLastSection(true);
m_header = new QnCheckBoxedHeaderView(this);
ui.trackLV->setHorizontalHeader(m_header);
ui.trackLV->horizontalHeader()->setStretchLastSection(true);
//ui.trackLV->model()->setHeaderData(0, Qt::Vertical, "#");
m_header->setVisible(true);
m_header->setSectionsClickable(true);
connect(m_header, SIGNAL(checkStateChanged(Qt::CheckState)), this, SLOT(at_sectionCheckstateChanged(Qt::CheckState)));
/////////////////////////////////////////////////////////////
for (int i = 0; i <= 3600; i+= 5*60)
ui.memoChapters->insertPlainText(floatToTime(i,'.') + '\n');
saveDialogFilter = TS_SAVE_DIALOG_FILTER;
const static int colWidths[] =
{
28, 200, 62, 38, 10
};
for (int i = 0; i < sizeof(colWidths) / sizeof(int); ++i)
ui.trackLV->horizontalHeader()->resizeSection(i, colWidths[i]);
ui.listViewFont->horizontalHeader()->resizeSection(0, 65);
ui.listViewFont->horizontalHeader()->resizeSection(1, 185);
for (int i = 0; i < ui.listViewFont->rowCount(); ++i) {
ui.listViewFont->setRowHeight(i, 16);
ui.listViewFont->item(i,0)->setFlags(ui.listViewFont->item(i,0)->flags() & (~Qt::ItemIsEditable));
ui.listViewFont->item(i,1)->setFlags(ui.listViewFont->item(i,0)->flags() & (~Qt::ItemIsEditable));
}
connect(&opacityTimer, SIGNAL(timeout()), this, SLOT(onOpacityTimer()));
connect(ui.trackLV, SIGNAL(itemSelectionChanged()), this, SLOT(trackLVItemSelectionChanged()));
connect(ui.trackLV, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(trackLVItemChanged(QTableWidgetItem*)));
connect(ui.inputFilesLV, SIGNAL(currentRowChanged(int)), this, SLOT(inputFilesLVChanged()));
connect(ui.addBtn, SIGNAL(clicked()), this, SLOT(onAddBtnClick()));
connect(ui.btnAppend, SIGNAL(clicked()), this, SLOT(onAppendButtonClick()));
connect(ui.removeFile, SIGNAL(clicked()), this, SLOT(onRemoveBtnClick()));
connect(ui.removeTrackBtn, SIGNAL(clicked()), this, SLOT(onRemoveTrackButtonClick()));
connect(ui.moveupBtn, SIGNAL(clicked()), this, SLOT(onMoveUpButtonCLick()));
connect(ui.movedownBtn, SIGNAL(clicked()), this, SLOT(onMoveDownButtonCLick()));
connect(ui.checkFPS, SIGNAL(stateChanged(int)), this, SLOT(onVideoCheckBoxChanged(int)));
connect(ui.checkBoxLevel, SIGNAL(stateChanged(int)), this, SLOT(onVideoCheckBoxChanged(int)));
connect(ui.comboBoxSEI, SIGNAL(currentIndexChanged(int)), this, SLOT(onVideoCheckBoxChanged(int)));
connect(ui.checkBoxSecondaryVideo, SIGNAL(stateChanged(int)), this, SLOT(onVideoCheckBoxChanged(int)));
connect(ui.checkBoxSPS, SIGNAL(stateChanged(int)), this, SLOT(onVideoCheckBoxChanged(int)));
connect(ui.checkBoxRemovePulldown, SIGNAL(stateChanged(int)), this, SLOT(onPulldownCheckBoxChanged(int)));
connect(ui.comboBoxFPS, SIGNAL(currentIndexChanged(int)), this, SLOT(onVideoComboBoxChanged(int)));
connect(ui.comboBoxLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(onVideoComboBoxChanged(int)));
connect(ui.comboBoxAR, SIGNAL(currentIndexChanged(int)), this, SLOT(onVideoComboBoxChanged(int)));
connect(ui.checkBoxKeepFps, SIGNAL(stateChanged(int)), this, SLOT(onAudioSubtitlesParamsChanged()));
connect(ui.dtsDwnConvert, SIGNAL(stateChanged(int)), this, SLOT(onAudioSubtitlesParamsChanged()));
connect(ui.secondaryCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onAudioSubtitlesParamsChanged()));
connect(ui.langComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onAudioSubtitlesParamsChanged()));
connect(ui.offsetsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onAudioSubtitlesParamsChanged()));
connect(ui.comboBoxPipCorner, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.comboBoxPipSize, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.spinBoxPipOffsetH, SIGNAL(valueChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.spinBoxPipOffsetV, SIGNAL(valueChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.checkBoxSound, SIGNAL(stateChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.editDelay, SIGNAL(valueChanged (int)), this, SLOT(onEditDelayChanged(int)));
connect(ui.muxTimeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(updateMuxTime1()));
connect(ui.muxTimeClock, SIGNAL(valueChanged(int)), this, SLOT(updateMuxTime2()));
connect(ui.fontButton, SIGNAL(clicked()), this, SLOT(onFontBtnClicked()));
connect(ui.colorButton, SIGNAL(clicked()), this, SLOT(onColorBtnClicked()));
connect(ui.checkBoxVBR, SIGNAL(clicked()), this, SLOT(onGeneralCheckboxClicked()));
connect(ui.spinBoxMplsNum, SIGNAL(valueChanged(int)), this, SLOT(onGeneralCheckboxClicked()));
connect(ui.spinBoxM2tsNum, SIGNAL(valueChanged(int)), this, SLOT(onGeneralCheckboxClicked()));
connect(ui.checkBoxBlankPL, SIGNAL(clicked()), this, SLOT(onSavedParamChanged()));
connect(ui.BlackplaylistCombo, SIGNAL(valueChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.checkBoxNewAudioPes, SIGNAL(clicked()), this, SLOT(onSavedParamChanged()));
connect(ui.checkBoxCrop, SIGNAL(stateChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.checkBoxRVBR, SIGNAL(clicked()), this, SLOT(onGeneralCheckboxClicked()));
connect(ui.checkBoxCBR, SIGNAL(clicked()), this, SLOT(onGeneralCheckboxClicked()));
//connect(ui.checkBoxuseAsynIO, SIGNAL(stateChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.radioButtonStoreOutput, SIGNAL(clicked()), this, SLOT(onSavedParamChanged()));
connect(ui.radioButtonOutoutInInput,SIGNAL(clicked()), this, SLOT(onSavedParamChanged()));
connect(ui.editVBVLen, SIGNAL(valueChanged(int)), this, SLOT(onGeneralSpinboxValueChanged()));
connect(ui.editMaxBitrate, SIGNAL(valueChanged(double)), this, SLOT(onGeneralSpinboxValueChanged()));
connect(ui.editMinBitrate, SIGNAL(valueChanged(double)), this, SLOT(onGeneralSpinboxValueChanged()));
connect(ui.editCBRBitrate, SIGNAL(valueChanged(double)), this, SLOT(onGeneralSpinboxValueChanged()));
connect(ui.rightEyeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(updateMetaLines()));
connect(ui.radioButtonAutoChapter, SIGNAL(clicked()), this, SLOT(onChapterParamsChanged()));
connect(ui.radioButtonNoChapters, SIGNAL(clicked()), this, SLOT(onChapterParamsChanged()));
connect(ui.radioButtonCustomChapters, SIGNAL(clicked()), this, SLOT(onChapterParamsChanged()));
connect(ui.spinEditChapterLen, SIGNAL(valueChanged(int)), this, SLOT(onChapterParamsChanged()));
connect(ui.memoChapters, SIGNAL(textChanged()), this, SLOT(onChapterParamsChanged()));
connect(ui.noSplit, SIGNAL(clicked()), this, SLOT(onSplitCutParamsChanged()));
connect(ui.splitByDuration, SIGNAL(clicked()), this, SLOT(onSplitCutParamsChanged()));
connect(ui.splitBySize, SIGNAL(clicked()), this, SLOT(onSplitCutParamsChanged()));
connect(ui.spinEditSplitDuration, SIGNAL(valueChanged(int)), this, SLOT(onSplitCutParamsChanged()));
connect(ui.editSplitSize, SIGNAL(valueChanged(double)), this, SLOT(onSplitCutParamsChanged()));
connect(ui.comboBoxMeasure, SIGNAL(currentIndexChanged(int)), this, SLOT(onSplitCutParamsChanged()));
connect(ui.checkBoxCut, SIGNAL(stateChanged(int)), this, SLOT(onSplitCutParamsChanged()));
connect(ui.cutStartTimeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(onSplitCutParamsChanged()));
connect(ui.cutEndTimeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(onSplitCutParamsChanged()));
connect(ui.spinEditBorder, SIGNAL(valueChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.comboBoxAnimation, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.lineSpacing, SIGNAL(valueChanged(double)), this, SLOT(onSavedParamChanged()));
connect(ui.spinEditOffset, SIGNAL(valueChanged(int)), this, SLOT(onSavedParamChanged()));
connect(ui.radioButtonTS, SIGNAL(clicked()), this, SLOT(RadioButtonMuxClick()));
connect(ui.radioButtonM2TS, SIGNAL(clicked()), this, SLOT(RadioButtonMuxClick()));
connect(ui.radioButtonBluRay, SIGNAL(clicked()), this, SLOT(RadioButtonMuxClick()));
connect(ui.radioButtonBluRayISO, SIGNAL(clicked()), this, SLOT(RadioButtonMuxClick()));
connect(ui.radioButtonAVCHD, SIGNAL(clicked()), this, SLOT(RadioButtonMuxClick()));
connect(ui.radioButtonDemux, SIGNAL(clicked()), this, SLOT(RadioButtonMuxClick()));
connect(ui.outFileName, SIGNAL(textChanged(const QString&)), this, SLOT(outFileNameChanged()));
connect(ui.DiskLabelEdit, SIGNAL(textChanged(const QString&)), this, SLOT(onGeneralCheckboxClicked()));
connect(ui.btnBrowse, SIGNAL(clicked()), this, SLOT(saveFileDialog()));
connect(ui.buttonMux, SIGNAL(clicked()), this, SLOT(startMuxing()));
connect(ui.buttonSaveMeta, SIGNAL(clicked()), this, SLOT(saveMetaFileBtnClick()));
connect(ui.radioButtonOutoutInInput, SIGNAL(stateChanged(int)), this, SLOT(onSavedParamChanged()));
connect( &proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout()) );
connect( &proc, SIGNAL(readyReadStandardError()), this, SLOT(readFromStderr()) );
connect( &proc, SIGNAL(finished(int, QProcess::ExitStatus )), this, SLOT(onProcessFinished(int, QProcess::ExitStatus )) );
connect( &proc, SIGNAL(error( QProcess::ProcessError)), this, SLOT(onProcessError(QProcess::ProcessError)) );
ui.DiskLabel->setVisible(false);
ui.DiskLabelEdit->setVisible(false);
ui.label_Donate->installEventFilter(this);
ui.langComboBox->addItem("und (Undetermined)");
ui.langComboBox->addItem("--------- common ---------");
for (int i = 0; i < sizeof(shortLangList)/2/sizeof(char*); ++i) {
const char* addr = shortLangList[i][0];
ui.langComboBox->addItem(QString(shortLangList[i][0]) + " (" + shortLangList[i][1] + ")", (qlonglong) (void*) addr);
}
ui.langComboBox->addItem("---------- all ----------");
for (int i = 0; i < sizeof(fullLangList)/2/sizeof(char*); ++i) {
const char* addr = fullLangList[i][0];
ui.langComboBox->addItem(QString(fullLangList[i][0]) + " (" + fullLangList[i][1] + ")", (qlonglong) (void*) addr);
}
trackLVItemSelectionChanged();
ui.trackSplitter->setStretchFactor(0, 10);
ui.trackSplitter->setStretchFactor(1, 100);
writeSettings();
}
TsMuxerWindow::~TsMuxerWindow()
{
delete sound;
delete tempSoundFile;
}
void TsMuxerWindow::getCodecInfo()
{
m_updateMeta = false;
codecList.clear();
int p;
QtvCodecInfo* codecInfo = 0;
int lastTrackID = 0;
QString tmpStr;
bool firstMark = true;
codecList.clear();
mplsFileList.clear();
chapters.clear();
fileDuration = 0;
for (int i = 0; i < procStdOutput.size(); ++i)
{
p = procStdOutput[i].indexOf("Track ID: ");
if (p >= 0) {
lastTrackID = procStdOutput[i].mid(QString("Track ID: ").length()).toInt();
codecList << QtvCodecInfo();
codecInfo = &(codecList.back());
codecInfo->trackID = lastTrackID;
}
p = procStdOutput[i].indexOf("Stream type: ");
if (p >= 0) {
if (lastTrackID == 0) {
codecList << QtvCodecInfo();
codecInfo = &(codecList.back());
}
codecInfo->descr = "Can't detect codec";
codecInfo->displayName = procStdOutput[i].mid(QString("Stream type: ").length());
if (codecInfo->displayName != "H.264" && codecInfo->displayName != "MVC") {
codecInfo->addSEIMethod = 0;
codecInfo->addSPS = false;
}
lastTrackID = 0;
}
p = procStdOutput[i].indexOf("Stream ID: ");
if (p >= 0)
codecInfo->programName = procStdOutput[i].mid(QString("Stream ID: ").length());
p = procStdOutput[i].indexOf("Stream info: ");
if (p >= 0)
codecInfo->descr = procStdOutput[i].mid(QString("Stream info: ").length());
p = procStdOutput[i].indexOf("Stream lang: ");
if (p >= 0)
codecInfo->lang = procStdOutput[i].mid(QString("Stream lang: ").length());
p = procStdOutput[i].indexOf("Stream delay: ");
if (p >= 0) {
tmpStr = procStdOutput[i].mid(QString("Stream delay: ").length());
codecInfo->delay = tmpStr.toInt();
}
p = procStdOutput[i].indexOf("subTrack: ");
if (p >= 0) {
tmpStr = procStdOutput[i].mid(QString("subTrack: ").length());
codecInfo->subTrack = tmpStr.toInt();
}
p = procStdOutput[i].indexOf("Secondary: 1");
if (p == 0) {
codecInfo->isSecondary = true;
}
p = procStdOutput[i].indexOf("Unselected: 1");
if (p == 0) {
codecInfo->enabledByDefault = false;
}
if (procStdOutput[i].startsWith("Error: ")) {
tmpStr = procStdOutput[i].mid(QString("Error: ").length());
QMessageBox msgBox(this);
msgBox. setWindowTitle("Not supported");
msgBox.setText(tmpStr);
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
else if (procStdOutput[i].startsWith("File #")) {
tmpStr = procStdOutput[i].mid(17); // todo: check here
mplsFileList << MPLSFileInfo(tmpStr, 0.0);
}
else if (procStdOutput[i].startsWith("Duration:")) {
tmpStr = procStdOutput[i].mid(10); // todo: check here
if (!mplsFileList.empty()) {
mplsFileList.last().duration = timeToFloat(tmpStr);
}
else {
fileDuration = timeToFloat(tmpStr);
}
}
else if (procStdOutput[i].startsWith("Base view: ")) {
tmpStr = procStdOutput[i].mid(11); // todo: check here
ui.rightEyeCheckBox->setChecked(tmpStr.trimmed() == "right-eye");
}
else if (procStdOutput[i].startsWith("start-time: ")) {
tmpStr = procStdOutput[i].mid(12);
if (tmpStr.contains(':')) {
double secondsF = timeToFloat(tmpStr);
ui.muxTimeEdit->setTime(qTimeFromFloat(secondsF));
}
else {
ui.muxTimeClock->setValue(tmpStr.toInt());
}
}
p = procStdOutput[i].indexOf("Marks: ");
if (p == 0) {
if (firstMark) {
firstMark = false;
ui.radioButtonCustomChapters->setChecked(true);
}
QStringList stringList = procStdOutput[i].mid(7).split(' ');
for (int k = 0; k < stringList.size(); ++k)
if (!stringList[k].isEmpty())
chapters << timeToFloat(stringList[k]);
}
}
if (fileDuration == 0 && !mplsFileList.isEmpty())
{
foreach(const MPLSFileInfo& mplsFile, mplsFileList)
fileDuration += mplsFile.duration;
}
m_updateMeta = true;
if (codecList.isEmpty()) {
QMessageBox msgBox(this);
msgBox.setWindowTitle("Unsupported format");
msgBox.setText(QString("Can't detect stream type. File name: \"") + newFileName + "\"");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return;
}
updateMetaLines();
emit codecListReady();
}
int TsMuxerWindow::findLangByCode(const QString& code)
{
QString addr;
for (int i = 0; i < ui.langComboBox->count(); ++i) {
addr = ui.langComboBox->itemData(i).toString();
if (!addr.isEmpty() && code == addr) {
return i;
}
}
return 0;
}
QtvCodecInfo* TsMuxerWindow::getCurrentCodec()
{
if (ui.trackLV->currentRow() == -1)
return nullptr;
long iCodec = long(ui.trackLV->item(ui.trackLV->currentRow(), 0)->data(Qt::UserRole).toLongLong());
return iCodec ? (QtvCodecInfo*) (void*) iCodec : nullptr;
}
void TsMuxerWindow::onVideoComboBoxChanged(int index)
{
Q_UNUSED(index);
if (disableUpdatesCnt)
return;
QtvCodecInfo* codecInfo = getCurrentCodec();
if(!codecInfo)
return;
codecInfo->fpsText = ui.comboBoxFPS->itemText(ui.comboBoxFPS->currentIndex());
codecInfo->levelText = ui.comboBoxLevel->itemText(ui.comboBoxLevel->currentIndex());
codecInfo->arText = ui.comboBoxAR->itemText(ui.comboBoxAR->currentIndex());
updateMetaLines();
}
void TsMuxerWindow::onVideoCheckBoxChanged(int state)
{
Q_UNUSED(state);
if (disableUpdatesCnt)
return;
QtvCodecInfo* codecInfo = getCurrentCodec();
if(codecInfo == nullptr)
return;
ui.comboBoxFPS->setEnabled(ui.checkFPS->isChecked());
codecInfo->checkFPS = ui.checkFPS->isChecked();
ui.comboBoxLevel->setEnabled(ui.checkBoxLevel->isChecked());
codecInfo->checkLevel = ui.checkBoxLevel->isChecked();
codecInfo->addSEIMethod = ui.comboBoxSEI->currentIndex();
codecInfo->addSPS = ui.checkBoxSPS->isChecked();
codecInfo->isSecondary = ui.checkBoxSecondaryVideo->isChecked();
colorizeCurrentRow(codecInfo);
updateMetaLines();
}
void TsMuxerWindow::updateCurrentColor(int dr, int dg, int db, int row)
{
if (row == -1)
row = ui.trackLV->currentRow();
if (row == -1)
return;
QColor color = ui.trackLV->palette().color(QPalette::Base);
color.setRed(qBound(0, color.red() + dr, 255));
color.setGreen(qBound(0, color.green() + dg, 255));
color.setBlue(qBound(0, color.blue() + db, 255));
for (int i = 0; i < 5; ++i)
{
QModelIndex index = ui.trackLV->model()->index(row, i);
ui.trackLV->model()->setData(index, QBrush(color), Qt::BackgroundColorRole);
}
}
void TsMuxerWindow::colorizeCurrentRow(const QtvCodecInfo* codecInfo, int rowIndex)
{
if (codecInfo->isSecondary)
updateCurrentColor(-40, 0, 0, rowIndex);
else
updateCurrentColor(0, 0, 0, rowIndex);
}
void TsMuxerWindow::onAudioSubtitlesParamsChanged()
{
if (disableUpdatesCnt)
return;
QtvCodecInfo* codecInfo = getCurrentCodec();
if(!codecInfo)
return;
codecInfo->bindFps = ui.checkBoxKeepFps->isChecked();
codecInfo->dtsDownconvert = ui.dtsDwnConvert->isChecked();
codecInfo->isSecondary = ui.secondaryCheckBox->isChecked();
codecInfo->offsetId = ui.offsetsComboBox->currentIndex() - 1;
QString addr = ui.langComboBox->itemData(ui.langComboBox->currentIndex()).toString();
if (!addr.isEmpty()) {
codecInfo->lang = addr;
} else {
codecInfo->lang.clear();
}
ui.trackLV->item(ui.trackLV->currentRow(),3)->setText(codecInfo->lang);
colorizeCurrentRow(codecInfo);
updateMetaLines();
}
void TsMuxerWindow::onEditDelayChanged(int i)
{
Q_UNUSED(i);
if (disableUpdatesCnt)
return;
QtvCodecInfo* codecInfo = getCurrentCodec();
if(!codecInfo)
return;
codecInfo->delay = ui.editDelay->value();
updateMetaLines();
}
void TsMuxerWindow::onPulldownCheckBoxChanged(int state)
{
Q_UNUSED(state);
if (disableUpdatesCnt)
return;
QtvCodecInfo* codecInfo = getCurrentCodec();
if(!codecInfo)
return;
if (ui.checkBoxRemovePulldown->isEnabled())
{
if (ui.checkBoxRemovePulldown->isChecked()) {
codecInfo->delPulldown = 1;
if (codecInfo->fpsTextOrig == "29.97")
{
ui.checkFPS->setChecked(true);
ui.checkFPS->setEnabled(true);
codecInfo->checkFPS = true;
ui.comboBoxFPS->setEnabled(true);
ui.comboBoxFPS->setCurrentIndex(3);
codecInfo->fpsText = "24000/1001";
setComboBoxText(ui.comboBoxFPS, "24000/1001");
}
}
else
codecInfo->delPulldown = 0;
}
else
codecInfo->delPulldown = -1;
updateMetaLines();
}
void TsMuxerWindow::addFiles(const QList<QUrl>& files)
{
addFileList.clear();
addFileList = files;
addFile();
}
void TsMuxerWindow::onAddBtnClick()
{
QString fileName = QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Add media file"), lastInputDir, tr(fileDialogFilter)));
if (fileName.isEmpty())
return;
lastInputDir = fileName;
//disconnect();
addFileList.clear();
addFileList << QUrl::fromLocalFile(fileName);
addFile();
}
void TsMuxerWindow::addFile()
{
if (addFileList.isEmpty())
return;
newFileName = QDir::toNativeSeparators(addFileList[0].toLocalFile());
if (lastSourceDir.isEmpty())
lastSourceDir = QFileInfo(newFileName).absolutePath();
addFileList.removeAt(0);
if (!checkFileDuplicate(newFileName))
return;
//disconnect(this, SIGNAL(tsMuxerSuccessFinished()));
//disconnect(this, SIGNAL(codecListReady()));
disconnect();
connect(this, SIGNAL(tsMuxerSuccessFinished()), this, SLOT(getCodecInfo()));
connect(this, SIGNAL(codecListReady()), this, SLOT(continueAddFile()));
connect(this, SIGNAL(fileAdded()), this, SLOT(addFile()));
runInMuxMode = false;
shellExecute(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + QDir::separator() + "tsMuxeR", QStringList() << newFileName);
}
bool TsMuxerWindow::checkFileDuplicate(const QString& fileName)
{
QString t = myUnquoteStr(fileName).trimmed();
for (int i = 0; i < ui.inputFilesLV->count(); ++i)
if (myUnquoteStr(ui.inputFilesLV->item(i)->data(FileNameRole).toString()) == t)
{
QMessageBox msgBox(this);
msgBox. setWindowTitle("File already exist");
msgBox.setText("File \"" + fileName + "\" already exist");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return false;
}
return true;
}
void TsMuxerWindow::setComboBoxText(QComboBox* comboBox, const QString& text)
{
for (int k = 0; k < comboBox->count(); ++k)
{
if (comboBox->itemText(k) == text) {
comboBox->setCurrentIndex(k);
return;
}
}
comboBox->addItem(text);
comboBox->setCurrentIndex(comboBox->count()-1);
}
void TsMuxerWindow::trackLVItemSelectionChanged()
{
if (disableUpdatesCnt)
return;
while(ui.tabWidgetTracks->count())
ui.tabWidgetTracks->removeTab(0);
if (ui.trackLV->currentRow() == -1)
{
ui.tabWidgetTracks->addTab(ui.tabSheetFake, TI_DEFAULT_TAB_NAME);
return;
}
QtvCodecInfo* codecInfo = getCurrentCodec();
if(!codecInfo)
return;
disableUpdatesCnt++;
if (ui.trackLV->rowCount() >= 1)
{
if (isVideoCodec(codecInfo->displayName))
{
ui.tabWidgetTracks->addTab(ui.tabSheetVideo, TI_DEFAULT_TAB_NAME);
ui.checkFPS->setChecked(codecInfo->checkFPS);
ui.checkBoxLevel->setChecked(codecInfo->checkLevel);
ui.comboBoxFPS->setEnabled(ui.checkFPS->isChecked());
ui.comboBoxLevel->setEnabled(ui.checkBoxLevel->isChecked());
ui.comboBoxSEI->setCurrentIndex(codecInfo->addSEIMethod);
ui.checkBoxSecondaryVideo->setChecked(codecInfo->isSecondary);
ui.checkBoxSPS->setChecked(codecInfo->addSPS);
ui.checkBoxRemovePulldown->setChecked(codecInfo->delPulldown == 1);
ui.checkBoxRemovePulldown->setEnabled(codecInfo->delPulldown >=0 );
setComboBoxText(ui.comboBoxFPS, codecInfo->fpsText);
if (!codecInfo->arText.isEmpty())
setComboBoxText(ui.comboBoxAR, codecInfo->arText);
else
ui.comboBoxAR->setCurrentIndex(0);
ui.checkBoxLevel->setEnabled(codecInfo->displayName == "H.264" || codecInfo->displayName == "MVC");
if (ui.checkBoxLevel->isEnabled())
setComboBoxText(ui.comboBoxLevel, codecInfo->levelText);
else
ui.comboBoxLevel->setCurrentIndex(0);
ui.comboBoxSEI->setEnabled(ui.checkBoxLevel->isEnabled());
ui.checkBoxSPS->setEnabled(ui.checkBoxLevel->isEnabled());
ui.comboBoxAR->setEnabled(codecInfo->displayName == "MPEG-2");
ui.comboBoxAR->setEnabled(true);
ui.labelAR->setEnabled(ui.comboBoxAR->isEnabled());
ui.checkBoxSecondaryVideo->setEnabled(codecInfo->displayName != "MVC");
}
else
{
ui.tabWidgetTracks->addTab(ui.tabSheetAudio, TI_DEFAULT_TAB_NAME);
if (codecInfo->displayName == "LPCM")
ui.tabWidgetTracks->addTab(ui.demuxLpcmOptions, TI_DEMUX_TAB_NAME);
if (codecInfo->displayName == "DTS-HD")
ui.dtsDwnConvert->setText("Downconvert DTS-HD to DTS");
else if (codecInfo->displayName == "TRUE-HD")
ui.dtsDwnConvert->setText("Downconvert TRUE-HD to AC3");
else if (codecInfo->displayName == "E-AC3 (DD+)")
ui.dtsDwnConvert->setText("Downconvert E-AC3 to AC3");
else
ui.dtsDwnConvert->setText("Downconvert HD audio");
ui.dtsDwnConvert->setEnabled(codecInfo->displayName == "DTS-HD" || codecInfo->displayName == "TRUE-HD" || codecInfo->displayName == "E-AC3 (DD+)");
ui.secondaryCheckBox->setEnabled(codecInfo->descr.contains("(DTS Express)") || codecInfo->displayName == "E-AC3 (DD+)");
if (!ui.secondaryCheckBox->isEnabled())
ui.secondaryCheckBox->setChecked(false);
ui.langComboBox->setCurrentIndex(findLangByCode(codecInfo->lang));
ui.offsetsComboBox->setCurrentIndex(codecInfo->offsetId + 1);
ui.dtsDwnConvert->setVisible(codecInfo->displayName != "PGS" && codecInfo->displayName != "SRT");
ui.secondaryCheckBox->setVisible(ui.dtsDwnConvert->isVisible());
bool isPGS = codecInfo->displayName == "PGS";
ui.checkBoxKeepFps->setVisible(isPGS);
ui.offsetsLabel->setVisible(isPGS);
ui.offsetsComboBox->setVisible(isPGS);
ui.imageSubtitles->setVisible(!ui.dtsDwnConvert->isVisible());
ui.imageAudio->setVisible(ui.dtsDwnConvert->isVisible());
ui.editDelay->setValue(codecInfo->delay);
ui.dtsDwnConvert->setChecked(codecInfo->dtsDownconvert);
ui.secondaryCheckBox->setChecked(codecInfo->isSecondary);
ui.checkBoxKeepFps->setChecked(codecInfo->bindFps);
ui.editDelay->setEnabled(!ui.radioButtonDemux->isChecked());
}
}
disableUpdatesCnt--;
trackLVItemChanged(0);
}
void TsMuxerWindow::trackLVItemChanged(QTableWidgetItem* item)
{
if (disableUpdatesCnt > 0)
return;
Q_UNUSED(item);
updateMetaLines();
ui.moveupBtn->setEnabled(ui.trackLV->currentItem() != 0);
ui.movedownBtn->setEnabled(ui.trackLV->currentItem() != 0);
ui.removeTrackBtn->setEnabled(ui.trackLV->currentItem() != 0);
if (ui.trackLV->rowCount() == 0)
oldFileName.clear();
disableUpdatesCnt++;
bool checkedExist = false;
bool uncheckedExist = false;
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
if (ui.trackLV->item(i,0)->checkState() == Qt::Checked)
checkedExist = true;
else
uncheckedExist = true;
}
if (checkedExist && uncheckedExist)
m_header->setCheckState(Qt::PartiallyChecked);
else if (checkedExist)
m_header->setCheckState(Qt::Checked);
else
m_header->setCheckState(Qt::Unchecked);
update();
disableUpdatesCnt--;
}
void TsMuxerWindow::inputFilesLVChanged()
{
QListWidgetItem* itm = ui.inputFilesLV->currentItem();
if (!itm) {
ui.btnAppend->setEnabled(false);
ui.removeFile->setEnabled(false);
return;
}
ui.btnAppend->setEnabled(itm->data(MplsItemRole).toInt() != MPLS_M2TS && ui.buttonMux->isEnabled());
ui.removeFile->setEnabled(itm->data(MplsItemRole).toInt() != MPLS_M2TS);
}
void TsMuxerWindow::modifyOutFileName(const QString fileName)
{
QFileInfo fi(unquoteStr(fileName));
QString name = fi.completeBaseName();
QString existingName = QDir::toNativeSeparators(ui.outFileName->text());
QFileInfo fiDst(existingName);
if (fiDst.completeBaseName() == "default") {
QString dstPath;
if (existingName.contains(QDir::separator()))
dstPath = QDir::toNativeSeparators(fiDst.absolutePath());
else
dstPath = getOutputDir();
if (ui.radioButtonTS->isChecked())
ui.outFileName->setText(dstPath + QDir::separator() + name + ".ts");
else if (ui.radioButtonM2TS->isChecked())
ui.outFileName->setText(dstPath + QDir::separator() + name + ".m2ts");
else if (ui.radioButtonBluRayISO->isChecked())
ui.outFileName->setText(dstPath + QDir::separator() + name + ".iso");
else
ui.outFileName->setText(dstPath);
if (ui.outFileName->text() == fileName)
ui.outFileName->setText(dstPath + QDir::separator() + name + "_new." + fi.suffix());
}
}
void TsMuxerWindow::continueAddFile()
{
double fps;
double level;
disableUpdatesCnt++;
bool firstWarn = true;
int firstAddedIndex = -1;
for (int i = 0; i < codecList.size(); ++i)
{
if (codecList[i].displayName == "PGS (depended view)")
continue;
QtvCodecInfo info = codecList[i];
if (info.displayName.isEmpty())
{
QMessageBox msgBox(this);
msgBox.setWindowTitle("Unsupported format");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
if (codecList.size() == 0) {
msgBox.setText(QString("Unsupported format or all tracks are not recognized. File name: \"") + newFileName + "\"");
msgBox.exec();
disableUpdatesCnt--;
return;
}
else {
if (firstWarn) {
msgBox.setText(QString("Some tracks not recognized. This tracks was ignored. File name: \"") + newFileName + "\"");
msgBox.exec();
firstWarn = false;
}
continue;
}
}
if (mplsFileList.isEmpty())
info.fileList << newFileName;
else {
info.fileList << mplsFileList[0].name;
QFileInfo fileInfo(unquoteStr(newFileName));
//info.mplsFile = fileInfo.baseName();
info.mplsFiles << unquoteStr(newFileName);
}
if (info.descr.indexOf("not found") >= 0) {
fps = 23.976;
info.checkFPS = true;
}
else
fps = extractFloatFromDescr(info.descr, "Frame rate: ");
info.width = extractFloatFromDescr(info.descr, "Resolution: ") + 0.5;
info.height = extractFloatFromDescr(info.descr, QString::number(info.width) + ":") + 0.5;
if (qAbs(fps - 23.976) < EPS)
info.fpsText = "24000/1001";
else if (qAbs(fps - 29.97) < EPS)
info.fpsText = "30000/1001";
else
info.fpsText = myFloatToStr(fps);
info.fpsTextOrig = myFloatToStr(fps);
level = extractFloatFromDescr(info.descr, "@");
info.levelText = myFloatToStr(level);
if (info.descr.indexOf("pulldown") >= 0)
info.delPulldown = 0;
info.maxPgOffsets = extractFloatFromDescr(info.descr, "3d-pg-planes: ");
if (info.maxPgOffsets > 0)
m_3dMode = true;
if (info.descr.contains("3d-plane: zero"))
info.offsetId = -1;
else if (info.descr.contains("3d-plane: "))
info.offsetId = extractFloatFromDescr(info.descr, "3d-plane: ");
else if (m_3dMode)
info.offsetId = 0;
ui.trackLV->setRowCount(ui.trackLV->rowCount()+1);
ui.trackLV->setRowHeight(ui.trackLV->rowCount()-1, 18);
QTableWidgetItem *item = new QTableWidgetItem("");
item->setCheckState(info.enabledByDefault ? Qt::Checked : Qt::Unchecked);
QtvCodecInfo* __info = new QtvCodecInfo();
*__info = info;
item->setData(Qt::UserRole, (qlonglong) (void*) __info);
ui.trackLV->setCurrentItem(item);
ui.trackLV->setItem(ui.trackLV->rowCount()-1, 0, item);
item = new QTableWidgetItem(newFileName);
item ->setFlags(item ->flags() & (~Qt::ItemIsEditable));
ui.trackLV->setItem(ui.trackLV->rowCount()-1, 1, item);
item = new QTableWidgetItem(info.displayName);
item ->setFlags(item ->flags() & (~Qt::ItemIsEditable));
ui.trackLV->setItem(ui.trackLV->rowCount()-1, 2, item);
item = new QTableWidgetItem(info.lang);
item ->setFlags(item ->flags() & (~Qt::ItemIsEditable));
ui.trackLV->setItem(ui.trackLV->rowCount()-1, 3, item);
item = new QTableWidgetItem(info.descr);
item ->setFlags(item ->flags() & (~Qt::ItemIsEditable));
ui.trackLV->setItem(ui.trackLV->rowCount()-1, 4, item);
if (firstAddedIndex == -1)
firstAddedIndex = ui.trackLV->rowCount()-1;
colorizeCurrentRow(&info, ui.trackLV->rowCount()-1);
}
if (firstAddedIndex >= 0) {
ui.trackLV->setRangeSelected(QTableWidgetSelectionRange(firstAddedIndex, 0, ui.trackLV->rowCount()-1, 4), true);
ui.trackLV->setCurrentCell(firstAddedIndex, 0);
}
QString displayName = newFileName;
if (fileDuration > 0)
displayName += QString(" (%1)").arg(floatToTime(fileDuration));
ui.inputFilesLV->addItem(displayName);
int index = ui.inputFilesLV->count()-1;
QListWidgetItem* fileItem = ui.inputFilesLV->item(index);
if (!mplsFileList.empty())
fileItem->setData(MplsItemRole, MPLS_PRIMARY);
fileItem->setData(FileNameRole, newFileName);
QVariant v;
v.setValue<ChapterList>(chapters);
fileItem->setData(ChaptersRole, v);
fileItem->setData(FileDurationRole, fileDuration);
chapters.clear();
fileDuration = 0.0;
ui.inputFilesLV->setCurrentItem(fileItem);
if (mplsFileList.size() > 0)
doAppendInt(mplsFileList[0].name, newFileName, mplsFileList[0].duration, false, MPLS_M2TS);
for (int mplsCnt = 1; mplsCnt < mplsFileList.size(); ++mplsCnt)
doAppendInt(mplsFileList[mplsCnt].name, mplsFileList[mplsCnt-1].name, mplsFileList[mplsCnt].duration, false, MPLS_M2TS);
ui.inputFilesLV->setCurrentItem(ui.inputFilesLV->item(index));
updateMetaLines();
ui.moveupBtn->setEnabled(ui.trackLV->currentRow() >= 0);
ui.movedownBtn->setEnabled(ui.trackLV->currentRow() >= 0);
ui.removeTrackBtn->setEnabled(ui.trackLV->currentRow() >= 0);
if (!outFileNameModified)
{
modifyOutFileName(newFileName);
outFileNameModified = true;
}
updateMaxOffsets();
updateCustomChapters();
disableUpdatesCnt--;
trackLVItemSelectionChanged();
emit fileAdded();
}
void TsMuxerWindow::updateCustomChapters()
{
QSet<qint64> chaptersSet;
double prevDuration = 0.0;
double offset = 0.0;
for (int i = 0; i < ui.inputFilesLV->count(); ++i)
{
QListWidgetItem* item = ui.inputFilesLV->item(i);
if (item->data(MplsItemRole).toInt() == MPLS_M2TS)
continue;
if (item->text().left(4) == FILE_JOIN_PREFIX)
offset += prevDuration;
else
offset = 0;
ChapterList chapters = item->data(ChaptersRole).value<ChapterList>();
foreach(double chapter, chapters)
chaptersSet << qint64((chapter + offset) * 1000000);
prevDuration = item->data(FileDurationRole).toDouble();
}
ui.memoChapters->clear();
QList<qint64> mergedChapterList = chaptersSet.toList();
qSort(mergedChapterList);
foreach(qint64 chapter, mergedChapterList)
ui.memoChapters->insertPlainText(floatToTime(chapter/1000000.0) + QString('\n'));
}
void splitLines(const QString& str, QList<QString>& strList)
{
strList = str.split('\n');
for (int i = 0; i < strList.size(); ++i)
{
if (!strList[i].isEmpty() && strList[i].at(0) == '\r')
strList[i] = strList[i].mid(1);
else if (!strList[i].isEmpty() && strList[i].at(strList[i].length()-1) == '\r')
strList[i] = strList[i].mid(0, strList[i].length()-1);
}
}
void TsMuxerWindow::addLines(const QByteArray& arr, QList<QString>& outList, bool isError)
{
QString str = QString::fromLocal8Bit(arr);
QList<QString> strList;
splitLines(str, strList);
QString text;
for (int i = 0; i < strList.size(); ++i)
{
if (strList[i].trimmed().isEmpty())
continue;
int p = strList[i].indexOf("% complete");
if (p >= 0)
{
int numStartPos = 0;
for (int j = 0; j < strList[i].length(); ++j) {
if (strList[i].at(j) >= '0' && strList[i].at(j) <= '9') {
numStartPos = j;
break;
}
}
QString progress = strList[i].mid(numStartPos, p - numStartPos);
float tmpVal = progress.toFloat();
if (qAbs(tmpVal) > 0 && runInMuxMode)
muxForm.setProgress(int(double(tmpVal*10) + 0.5)); // todo: uncomment here
}
else {
if (runInMuxMode)
{
if (!text.isEmpty())
text += '\n';
text += strList[i];
}
else
outList << strList[i];
}
}
if (runInMuxMode && !text.isEmpty())
{
if (isError)
muxForm.addStdErrLine(text);
else
muxForm.addStdOutLine(text);
}
}
void TsMuxerWindow::readFromStdout()
{
addLines(proc.readAllStandardOutput(), procStdOutput, false);
}
void TsMuxerWindow::readFromStderr()
{
QByteArray data(proc.readAllStandardError());
addLines(data, procErrOutput, true);
}
void TsMuxerWindow::myPlaySound(const QString& fileName)
{
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return;
qint64 fSize = file.size();
char* data = new char[fSize];
qint64 readed = file.read(data, fSize);
QFileInfo fi(fileName);
QString outFileName = QDir::toNativeSeparators(QDir::tempPath()) + QDir::separator() + "tsMuxeR_" + fi.fileName();
delete sound;
delete tempSoundFile;
tempSoundFile = new QTemporaryFile(outFileName);
if (!tempSoundFile->open())
{
delete [] data;
delete tempSoundFile;
tempSoundFile = 0;
return;
}
tempSoundFile->write(data, readed);
QString tmpFileName = tempSoundFile->fileName();
tempSoundFile->close();
sound = new QSound(tmpFileName);
sound->play();
file.close();
delete [] data;
}
void TsMuxerWindow::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
processFinished = true;
if (!metaName.isEmpty())
{
QFile::remove(metaName);
metaName.clear();
if (ui.checkBoxSound->isChecked())
{
if (exitCode == 0)
myPlaySound(":/sounds/success.wav");
else
myPlaySound(":/sounds/fail.wav");
}
}
processExitCode = exitCode;
processExitStatus = exitStatus;
muxForm.muxFinished(processExitCode, ui.radioButtonDemux->isChecked() ? "Demux" : "Mux");
ui.buttonMux->setEnabled(true);
ui.addBtn->setEnabled(true);
inputFilesLVChanged();
if (processExitCode == 0)
emit tsMuxerSuccessFinished();
}
void TsMuxerWindow::onProcessError(QProcess::ProcessError error)
{
processExitCode = -1;
if (!metaName.isEmpty())
{
QFile::remove(metaName);
metaName.clear();
}
processFinished = true;
QMessageBox msgBox(this);
QString text;
msgBox. setWindowTitle("tsMuxeR error");
switch(error) {
case QProcess::FailedToStart:
msgBox.setText("tsMuxeR not found!");
ui.buttonMux->setEnabled(true);
ui.addBtn->setEnabled(true);
inputFilesLVChanged();
break;
case QProcess::Crashed:
// process killed
if (runInMuxMode)
return;
for (int i = 0; i < procErrOutput.size(); ++i) {
if (i > 0)
text += '\n';
text += procErrOutput[i];
}
msgBox.setText(text);
break;
default:
msgBox.setText("Can't execute tsMuxeR!");
break;
}
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
void TsMuxerWindow::shellExecute(const QString& process, const QStringList& args)
{
ui.buttonMux->setEnabled(false);
procStdOutput.clear();
procErrOutput.clear();
processFinished = false;
processExitCode = -1;
proc.start(process, args);
if (muxForm.isVisible())
muxForm.setProcess(&proc);
}
void TsMuxerWindow::doAppendInt(const QString& fileName, const QString& parentFileName, double duration, bool doublePrefix, MplsType mplsRole)
{
QString itemName = FILE_JOIN_PREFIX;
if (doublePrefix)
itemName += FILE_JOIN_PREFIX;
itemName += fileName;
if (duration > 0)
itemName += QString(" ( %1)").arg(floatToTime(duration));
QListWidgetItem* item = new QListWidgetItem(itemName);
ui.inputFilesLV->insertItem(ui.inputFilesLV->currentRow() + 1, item);
item->setData(MplsItemRole, mplsRole);
item->setData(FileNameRole, fileName);
if (duration > 0)
item->setData(FileDurationRole, duration);
QVariant v;
v.setValue<ChapterList>(chapters);
item->setData(ChaptersRole, v);
ui.inputFilesLV->setCurrentItem(item);
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
long data = ui.trackLV->item(i,0)->data(Qt::UserRole).toLongLong();
if (!data)
continue;
QtvCodecInfo* info = (QtvCodecInfo*) (void*) data;
if (mplsRole == MPLS_PRIMARY) {
for (int j = 0; j < info->mplsFiles.size(); ++j)
{
if (info->mplsFiles[j] == parentFileName) {
info->mplsFiles.insert(j+1, fileName);
break;
}
}
}
else {
for (int j = 0; j < info->fileList.size(); ++j)
{
if (info->fileList[j] == parentFileName) {
info->fileList.insert(j+1, fileName);
break;
}
}
}
}
}
bool TsMuxerWindow::isVideoCropped()
{
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
long iCodec = ui.trackLV->item(i,0)->data(Qt::UserRole).toLongLong();
if (!iCodec) continue;
QtvCodecInfo* codecInfo = (QtvCodecInfo*) (void*) iCodec;
if (isVideoCodec(codecInfo->displayName))
{
if (codecInfo->height < 1080 && codecInfo->height != 720 &&
codecInfo->height != 576 && codecInfo->height != 480)
return true;
}
}
return false;
}
bool TsMuxerWindow::isDiskOutput() const
{
return ui.radioButtonAVCHD->isChecked() || ui.radioButtonBluRay->isChecked() || ui.radioButtonBluRayISO->isChecked();
}
QString TsMuxerWindow::getMuxOpts()
{
QString rez = "MUXOPT --no-pcr-on-video-pid ";
if (ui.checkBoxNewAudioPes->isChecked())
rez += "--new-audio-pes ";
if (ui.radioButtonBluRay->isChecked())
rez += "--blu-ray ";
else if (ui.radioButtonBluRayISO->isChecked()) {
rez += "--blu-ray ";
if (!ui.DiskLabelEdit->text().isEmpty())
rez += QString("--label=\"%1\" ").arg(ui.DiskLabelEdit->text());
}
else if (ui.radioButtonAVCHD->isChecked())
rez += "--avchd ";
else if (ui.radioButtonDemux->isChecked())
rez += "--demux ";
if (ui.checkBoxCBR->isChecked())
rez += "--cbr --bitrate=" + toFixedDecPoint(QString::number(ui.editCBRBitrate->value(),'f',3));
else {
rez += "--vbr ";
if (ui.checkBoxRVBR->isChecked()) {
rez += QString("--minbitrate=") + toFixedDecPoint(QString::number(ui.editMinBitrate->value(),'f',3));
rez += QString(" --maxbitrate=") + toFixedDecPoint(QString::number(ui.editMaxBitrate->value(),'f',3));
}
}
//if (!ui.checkBoxuseAsynIO->isChecked())
// rez += " --no-asyncio";
if (isDiskOutput())
{
if (ui.checkBoxBlankPL->isChecked() && isVideoCropped()) {
rez += " --insertBlankPL";
if (ui.BlackplaylistCombo->value() != 1900)
rez += QString(" --blankOffset=") + QString::number(ui.BlackplaylistCombo->value());
}
if (ui.spinBoxMplsNum->value() > 0)
rez += " --mplsOffset=" + QString::number(ui.spinBoxMplsNum->value());
if (ui.spinBoxM2tsNum->value() > 0)
rez += " --m2tsOffset=" + QString::number(ui.spinBoxM2tsNum->value());
}
if (isDiskOutput())
{
if (ui.radioButtonAutoChapter->isChecked())
rez += " --auto-chapters=" + QString::number(ui.spinEditChapterLen->value());
if (ui.radioButtonCustomChapters->isChecked()) {
QString custChapStr;
QList<QString> lines;
splitLines(ui.memoChapters->toPlainText(), lines);
for (int i = 0; i < lines.size(); ++i)
{
QString tmpStr = lines[i].trimmed();
if (!tmpStr.isEmpty()) {
if (!custChapStr.isEmpty())
custChapStr += ';';
custChapStr += tmpStr;
}
}
rez += QString(" --custom-chapters=") + custChapStr;
}
}
if (ui.splitByDuration->isChecked())
rez += QString(" --split-duration=") + ui.spinEditSplitDuration->text();
if (ui.splitBySize->isChecked())
rez += QString(" --split-size=") + myFloatToStr(myStrToFloat(ui.editSplitSize->text())) + ui.comboBoxMeasure->currentText();
int startCut = qTimeToMsec(ui.cutStartTimeEdit->time());
int endCut = qTimeToMsec(ui.cutEndTimeEdit->time());
if (startCut > 0 && ui.checkBoxCut->isChecked())
rez += QString(" --cut-start=%1ms").arg(startCut);
if (endCut > 0 && ui.checkBoxCut->isChecked())
rez += QString(" --cut-end=%1ms").arg(endCut);
int vbvLen = ui.editVBVLen->value();
if (vbvLen > 0)
rez += " --vbv-len=" + QString::number(vbvLen);
if (isDiskOutput() && ui.rightEyeCheckBox->isChecked())
rez += " --right-eye";
/*
QString muxTimeStr = ui.muxTimeEdit->time().toString("hh:mm:ss.zzz");
if (muxTimeStr != "00:00:00.000")
rez += " --start-time=" + muxTimeStr;
*/
if (ui.muxTimeClock->value())
rez += QString(" --start-time=%1").arg(ui.muxTimeClock->value());
return rez;
}
int getCharsetCode(const QString& name)
{
Q_UNUSED(name);
return 0; // todo: refactor this function
}
double TsMuxerWindow::getRendererAnimationTime() const
{
switch (ui.comboBoxAnimation->currentIndex())
{
case 1:
return 0.1; // fast
break;
case 2:
return 0.25; // medium
break;
case 3:
return 0.5; // slow
break;
case 4:
return 1.0; // very slow
}
return 0.0;
}
void TsMuxerWindow::setRendererAnimationTime(double value)
{
int index = 0;
if (doubleCompare(value, 0.1))
index = 1;
else if (doubleCompare(value, 0.25))
index = 2;
else if (doubleCompare(value, 0.5))
index = 3;
else if (doubleCompare(value, 1.0))
index = 4;
ui.comboBoxAnimation->setCurrentIndex(index);
}
QString TsMuxerWindow::getSrtParams()
{
QString rez;
if (ui.listViewFont->rowCount() < 5)
return rez;
rez = QString(",font-name=\"") + ui.listViewFont->item(0,1)->text();
rez += QString("\",font-size=") + ui.listViewFont->item(1,1)->text();
rez += QString(",font-color=") + ui.listViewFont->item(2,1)->text();
int charsetCode = getCharsetCode(ui.listViewFont->item(3,1)->text());
if (charsetCode)
rez += QString(",font-charset=") + QString::number(charsetCode);
if (ui.lineSpacing->value() != 1.0)
rez += ",line-spacing=" + QString::number(ui.lineSpacing->value());
if (ui.listViewFont->item(4,1)->text().indexOf("Italic") >= 0)
rez += ",font-italic";
if (ui.listViewFont->item(4,1)->text().indexOf("Bold") >= 0)
rez += ",font-bold";
if (ui.listViewFont->item(4,1)->text().indexOf("Underline") >= 0)
rez += ",font-underline";
if (ui.listViewFont->item(4,1)->text().indexOf("Strikeout") >= 0)
rez += ",font-strikeout";
rez += QString(",bottom-offset=") + QString::number(ui.spinEditOffset->value()) + ",font-border=" + QString::number(ui.spinEditBorder->value());
if (ui.rbhLeft->isChecked())
rez += ",text-align=left";
else if (ui.rbhRight->isChecked())
rez += ",text-align=right";
else
rez += ",text-align=center";
double animationTime = getRendererAnimationTime();
if (animationTime > 0.0)
rez += QString(",fadein-time=%1,fadeout-time=%1").arg(animationTime);
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
if (ui.trackLV->item(i,0)->checkState() == Qt::Checked)
{
long iCodec = ui.trackLV->item(i,0)->data(Qt::UserRole).toLongLong();
if (!iCodec) continue;
QtvCodecInfo* codecInfo = (QtvCodecInfo*) (void*) iCodec;
if (isVideoCodec(codecInfo->displayName)) {
rez += QString(",video-width=") + QString::number(codecInfo->width);
rez += QString(",video-height=") + QString::number(codecInfo->height);
rez += QString(",fps=") + fpsTextToFpsStr(codecInfo->fpsText);
return rez;
}
}
}
rez += ",video-width=1920,video-height=1080,fps=23.976";
return rez;
}
QString TsMuxerWindow::getFileList(QtvCodecInfo* codecInfo)
{
QString rezStr;
if (codecInfo->mplsFiles.isEmpty()) {
for (int i = 0; i < codecInfo->fileList.size(); ++i)
{
if (i > 0)
rezStr += '+';
rezStr += QString("\"") + codecInfo->fileList[i] + "\"";
}
}
else {
for (int i = 0; i < codecInfo->mplsFiles.size(); ++i) {
if (i > 0)
rezStr += '+';
rezStr += QString("\"") + codecInfo->mplsFiles[i] + "\"";
}
}
return rezStr;
}
QString cornerToStr(int corner)
{
if (corner == 0)
return "topLeft";
else if (corner == 1)
return "topRight";
else if (corner == 2)
return "bottomRight";
else
return "bottomLeft";
}
QString toPipScaleStr(int scaleIdx)
{
if (scaleIdx == 0)
return "1";
if (scaleIdx == 1)
return "1/2";
else if (scaleIdx == 2)
return "1/4";
else if (scaleIdx == 3)
return "1.5";
else
return "fullScreen";
}
QString TsMuxerWindow::getVideoMetaInfo(QtvCodecInfo* codecInfo)
{
QString fpsStr;
QString levelStr;
QString rezStr = codecInfo->programName + ", ";
rezStr += getFileList(codecInfo);
if (codecInfo->checkFPS)
fpsStr = fpsTextToFpsStr(codecInfo->fpsText);
if (codecInfo->checkLevel)
levelStr = QString::number(codecInfo->levelText.toDouble(), 'f', 1);
if (ui.checkBoxCrop->isChecked() && ui.checkBoxCrop->isEnabled())
rezStr += QString(", ") + "restoreCrop";
if (!fpsStr.isEmpty())
rezStr += QString(", ") + "fps=" + fpsStr;
if (!levelStr.isEmpty())
rezStr += QString(", ") + "level=" + levelStr;
if (codecInfo->addSEIMethod == 1)
rezStr += QString(", ") + "insertSEI";
else if (codecInfo->addSEIMethod == 2)
rezStr += QString(", ") + "forceSEI";
if (codecInfo->addSPS)
rezStr += QString(", ") + "contSPS";
if (codecInfo->delPulldown == 1)
rezStr += QString(", ") + "delPulldown";
if (codecInfo->arText != "Not change" && !codecInfo->arText.isEmpty())
rezStr += QString(", ") + "ar=" + codecInfo->arText;
if (codecInfo->isSecondary) {
rezStr += QString(", secondary");
rezStr += QString(", pipCorner=%1").arg(cornerToStr(ui.comboBoxPipCorner->currentIndex()));
rezStr += QString(" ,pipHOffset=%1").arg(ui.spinBoxPipOffsetH->value());
rezStr += QString(" ,pipVOffset=%1").arg(ui.spinBoxPipOffsetV->value());
rezStr += QString(", pipScale=%1").arg(toPipScaleStr(ui.comboBoxPipSize->currentIndex()));
rezStr += QString(", pipLumma=3");
}
return rezStr;
}
QString TsMuxerWindow::getAudioMetaInfo(QtvCodecInfo* codecInfo)
{
QString rezStr = codecInfo->programName + ", ";
rezStr += getFileList(codecInfo);
if (codecInfo->delay != 0)
rezStr += QString(", timeshift=") + QString::number(codecInfo->delay) + "ms";
if (codecInfo->dtsDownconvert && codecInfo->programName == "A_DTS")
rezStr += ", down-to-dts";
else if (codecInfo->dtsDownconvert && codecInfo->programName == "A_AC3")
rezStr += ", down-to-ac3";
if (codecInfo->isSecondary)
rezStr += ", secondary";
return rezStr;
}
void TsMuxerWindow::updateMuxTime1()
{
QTime t = ui.muxTimeEdit->time();
int clock = (t.hour()*3600 + t.minute() * 60 + t.second()) * 45000ll + t.msec() * 45ll;
ui.muxTimeClock->blockSignals(true);
ui.muxTimeClock->setValue(clock);
ui.muxTimeClock->blockSignals(false);
updateMetaLines();
}
void TsMuxerWindow::updateMuxTime2()
{
double timeF = ui.muxTimeClock->value() / 45000.0;
ui.muxTimeEdit->blockSignals(true);
ui.muxTimeEdit->setTime(qTimeFromFloat(timeF));
ui.muxTimeEdit->blockSignals(false);
updateMetaLines();
}
void TsMuxerWindow::updateMetaLines()
{
if (!m_updateMeta || disableUpdatesCnt > 0)
return;
ui.memoMeta->clear();
ui.memoMeta->append(getMuxOpts());
QString tmpFps;
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
long iCodec = ui.trackLV->item(i,0)->data(Qt::UserRole).toLongLong();
if (!iCodec) continue;
QtvCodecInfo* codecInfo = (QtvCodecInfo*) (void*) iCodec;
if (isVideoCodec(codecInfo->displayName))
{
if (codecInfo->checkFPS)
tmpFps = fpsTextToFpsStr(codecInfo->fpsText);
else
tmpFps = codecInfo->fpsTextOrig;
break;
}
}
QString prefix;
QString postfix;
bool bluray3D = isDiskOutput() && m_3dMode;
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
if (ui.trackLV->item(i,0)->checkState() == Qt::Checked)
prefix = "";
else
prefix = "#";
long iCodec = ui.trackLV->item(i,0)->data(Qt::UserRole).toLongLong();
if (!iCodec) continue;
QtvCodecInfo* codecInfo = (QtvCodecInfo*) (void*) iCodec;
postfix.clear();
if (codecInfo->displayName == "PGS")
{
if (codecInfo->bindFps && !tmpFps.isEmpty())
postfix += QString(", fps=") + tmpFps;
if (bluray3D && codecInfo->offsetId >= 0)
postfix += QString(", 3d-plane=%1").arg(codecInfo->offsetId);
}
if (codecInfo->displayName == "SRT") {
postfix += getSrtParams();
if (bluray3D && codecInfo->offsetId >= 0)
postfix += QString(", offset=%1").arg(codecInfo->offsetId);
}
if (codecInfo->trackID != 0)
postfix += QString(", track=") + QString::number(codecInfo->trackID);
if (!codecInfo->lang.isEmpty())
postfix += QString(", lang=") + codecInfo->lang;
//if (!codecInfo->mplsFile.isEmpty())
// postfix += QString(", mplsFile=") + codecInfo->mplsFile;
if (codecInfo->subTrack != 0)
postfix += QString(", subTrack=") + QString::number(codecInfo->subTrack);
if (isVideoCodec(codecInfo->displayName))
ui.memoMeta->append(prefix + getVideoMetaInfo(codecInfo) + postfix);
else
ui.memoMeta->append(prefix + getAudioMetaInfo(codecInfo) + postfix);
}
}
void TsMuxerWindow::onFontBtnClicked()
{
bool ok;
QFont font;
font.setFamily(ui.listViewFont->item(0,1)->text());
font.setPointSize((ui.listViewFont->item(1,1)->text()).toInt());
font.setItalic(ui.listViewFont->item(4,1)->text().indexOf("Italic") >= 0);
font.setBold(ui.listViewFont->item(4,1)->text().indexOf("Bold") >= 0);
font.setUnderline(ui.listViewFont->item(4,1)->text().indexOf("Underline") >= 0);
font.setStrikeOut(ui.listViewFont->item(4,1)->text().indexOf("Strikeout") >= 0);
font = QFontDialog::getFont(&ok, font, this);
if (ok)
{
//FT_Face fontFace = fontfreetypeFace();
ui.listViewFont->item(0,1)->setText(font.family());
ui.listViewFont->item(1,1)->setText(QString::number(font.pointSize()));
QString optStr;
if (font.italic())
optStr += "Italic";
if (font.bold()) {
if (!optStr.isEmpty()) optStr += ',';
optStr += "Bold";
}
if (font.underline()) {
if (!optStr.isEmpty()) optStr += ',';
optStr += "Underline";
}
if (font.strikeOut()) {
if (!optStr.isEmpty()) optStr += ',';
optStr += "Strikeout";
}
ui.listViewFont->item(4,1)->setText(optStr);
writeSettings();
updateMetaLines();
}
}
quint32 bswap(quint32 val)
{
return val; //ntohl(val);
}
int colorLight(QColor color)
{
return (0.257 * color.red()) + (0.504 * color.green()) + (0.098 * color.blue()) + 16;
}
void TsMuxerWindow::setTextItemColor(QString str)
{
while (str.length() < 8)
str = '0' + str;
QColor color = bswap(str.toLongLong(0, 16));
QTableWidgetItem* item = ui.listViewFont->item(2,1);
item->setBackground(QBrush(color));
if (colorLight(color) < 128)
item->setForeground(QBrush(QColor(255,255,255,255)));
else
item->setForeground(QBrush(QColor(0,0,0,255)));
item->setText(QString("0x") + str);
}
void TsMuxerWindow::onColorBtnClicked()
{
QColor color = bswap(ui.listViewFont->item(2,1)->text().toLongLong(0, 16));
color = QColorDialog::getColor(color, this);
QString str = QString::number(bswap(color.rgba()), 16);
setTextItemColor(str);
writeSettings();
updateMetaLines();
}
void TsMuxerWindow::onGeneralCheckboxClicked()
{
ui.editMaxBitrate->setEnabled(ui.checkBoxRVBR->isChecked());
ui.editMinBitrate->setEnabled(ui.checkBoxRVBR->isChecked());
ui.editCBRBitrate->setEnabled(ui.checkBoxCBR->isChecked());
ui.BlackplaylistCombo->setEnabled(ui.checkBoxBlankPL->isChecked());
ui.BlackplaylistLabel->setEnabled(ui.checkBoxBlankPL->isChecked());
updateMetaLines();
}
void TsMuxerWindow::onGeneralSpinboxValueChanged()
{
updateMetaLines();
}
void TsMuxerWindow::onChapterParamsChanged()
{
ui.memoChapters->setEnabled(ui.radioButtonCustomChapters->isChecked());
ui.spinEditChapterLen->setEnabled(ui.radioButtonAutoChapter->isChecked());
updateMetaLines();
}
void TsMuxerWindow::onSplitCutParamsChanged()
{
ui.spinEditSplitDuration->setEnabled(ui.splitByDuration->isChecked());
ui.labelSplitByDur->setEnabled(ui.splitByDuration->isChecked());
ui.editSplitSize->setEnabled(ui.splitBySize->isChecked());
ui.comboBoxMeasure->setEnabled(ui.splitBySize->isChecked());
ui.cutStartTimeEdit->setEnabled(ui.checkBoxCut->isChecked());
ui.cutEndTimeEdit->setEnabled(ui.checkBoxCut->isChecked());
updateMetaLines();
}
void TsMuxerWindow::onSavedParamChanged()
{
writeSettings();
updateMetaLines();
}
void TsMuxerWindow::onFontParamsChanged()
{
updateMetaLines();
}
void TsMuxerWindow::onRemoveBtnClick()
{
if (!ui.inputFilesLV->currentItem())
return;
int idx = ui.inputFilesLV->currentRow();
bool delMplsM2ts = false;
if (idx < ui.inputFilesLV->count()-1)
{
if (ui.inputFilesLV->currentItem()->data(MplsItemRole).toInt() == MPLS_PRIMARY)
delMplsM2ts = true;
else if (ui.inputFilesLV->currentItem()->text().left(4) != FILE_JOIN_PREFIX)
{
QString text = ui.inputFilesLV->item(idx+1)->text();
if (text.length() >= 4 && text.left(4) == FILE_JOIN_PREFIX)
ui.inputFilesLV->item(idx+1)->setText(text.mid(4));
}
}
delTracksByFileName(myUnquoteStr(ui.inputFilesLV->currentItem()->data(FileNameRole).toString()));
ui.inputFilesLV->takeItem(idx);
if (idx >= ui.inputFilesLV->count())
idx--;
if (delMplsM2ts)
{
while (idx < ui.inputFilesLV->count())
{
QString text = ui.inputFilesLV->item(idx)->text();
if (text.length() >= 4 && text.left(4) == FILE_JOIN_PREFIX)
{
delTracksByFileName(myUnquoteStr(ui.inputFilesLV->item(idx)->data(FileNameRole).toString()));
ui.inputFilesLV->takeItem(idx);
}
else break;
}
}
if (ui.inputFilesLV->count() > 0)
ui.inputFilesLV->setCurrentRow(idx);
updateCustomChapters();
}
void TsMuxerWindow::delTracksByFileName(const QString& fileName)
{
for (int i = ui.trackLV->rowCount()-1; i >= 0; --i)
{
long iCodec = ui.trackLV->item(i, 0)->data(Qt::UserRole).toLongLong();
if (iCodec) {
QtvCodecInfo* info = (QtvCodecInfo*) (void*) iCodec;
for (int j = info->fileList.count()-1; j >= 0; --j)
{
if (info->fileList[j] == fileName) {
info->fileList.removeAt(j);
break;
}
}
for (int j = info->mplsFiles.count()-1; j >= 0; --j)
{
if (info->mplsFiles[j] == fileName) {
info->mplsFiles.removeAt(j);
break;
}
}
if (info->fileList.count() == 0)
deleteTrack(i);
}
}
updateMaxOffsets();
updateMetaLines();
}
void TsMuxerWindow::deleteTrack(int idx)
{
disableUpdatesCnt++;
long iCodec = ui.trackLV->item(idx, 0)->data(Qt::UserRole).toLongLong();
delete (QtvCodecInfo*) (void*) iCodec;
int lastItemIndex = idx; //trackLV.items[idx].index;
ui.trackLV->removeRow(idx);
if (ui.trackLV->rowCount() == 0)
{
lastSourceDir.clear();
while(ui.tabWidgetTracks->count())
ui.tabWidgetTracks->removeTab(0);
ui.tabWidgetTracks->addTab(ui.tabSheetFake, TI_DEFAULT_TAB_NAME);
ui.outFileName->setText(getDefaultOutputFileName());
outFileNameModified = false;
}
else {
if (lastItemIndex > ui.trackLV->rowCount()-1)
--lastItemIndex;
if (lastItemIndex >= 0) {
ui.trackLV->setCurrentCell(lastItemIndex,0);
ui.trackLV->setRangeSelected(QTableWidgetSelectionRange(lastItemIndex, 0, lastItemIndex, 4), true);
}
updateNum();
}
updateMaxOffsets();
updateMetaLines();
ui.moveupBtn->setEnabled(ui.trackLV->currentItem() != 0);
ui.movedownBtn->setEnabled(ui.trackLV->currentItem() != 0);
ui.removeTrackBtn->setEnabled(ui.trackLV->currentItem() != 0);
disableUpdatesCnt--;
trackLVItemSelectionChanged();
}
void TsMuxerWindow::updateNum()
{
//for (int i = 0; i < ui.trackLV->rowCount(); ++i)
// ui.trackLV->item(i,0)->setText(QString::number(i+1));
}
void TsMuxerWindow::onAppendButtonClick()
{
QList<QtvCodecInfo> codecList;
if (ui.inputFilesLV->currentItem() == 0)
{
QMessageBox msgBox(this);
msgBox. setWindowTitle("No track selected");
msgBox.setText("No track selected");
msgBox.setIcon(QMessageBox::Information);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return;
}
QString fileName = QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Append media file"), lastInputDir, tr(fileDialogFilter)));
if (fileName.isEmpty())
return;
lastInputDir = fileName;
addFileList.clear();
addFileList << QUrl::fromLocalFile(fileName);
appendFile();
}
void TsMuxerWindow::appendFile()
{
if (addFileList.isEmpty())
return;
newFileName = QDir::toNativeSeparators(addFileList[0].toLocalFile());
if (lastSourceDir.isEmpty())
lastSourceDir = QFileInfo(newFileName).absolutePath();
addFileList.removeAt(0);
if (!checkFileDuplicate(newFileName))
return;
//disconnect(this, SIGNAL(tsMuxerSuccessFinished()));
//disconnect(this, SIGNAL(codecListReady()));
disconnect();
connect(this, SIGNAL(tsMuxerSuccessFinished()), this, SLOT(getCodecInfo()));
connect(this, SIGNAL(codecListReady()), this, SLOT(continueAppendFile()));
connect(this, SIGNAL(fileAppended()), this, SLOT(appendFile()));
runInMuxMode = false;
shellExecute(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + QDir::separator() + "tsMuxeR", QStringList() << newFileName);
}
void TsMuxerWindow::continueAppendFile()
{
QString parentFileName = myUnquoteStr((ui.inputFilesLV->currentItem()->data(FileNameRole).toString()));
QFileInfo newFi(unquoteStr(newFileName));
QFileInfo oldFi(unquoteStr(parentFileName));
if (newFi.suffix().toUpper() != oldFi.suffix().toUpper())
{
QMessageBox msgBox(this);
msgBox. setWindowTitle("Invalid file extension");
msgBox.setText("Appended file must have same file extension.");
msgBox.setIcon(QMessageBox::Information);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return;
}
disableUpdatesCnt++;
int idx = ui.inputFilesLV->currentRow();
bool firstStep = true;
bool doublePrefix = false;
for (;idx < ui.inputFilesLV->count(); ++idx)
{
int mplsRole = ui.inputFilesLV->item(idx)->data(MplsItemRole).toInt();
if (mplsRole == MPLS_PRIMARY && firstStep || mplsRole == MPLS_M2TS) {
ui.inputFilesLV->setCurrentRow(idx);
doublePrefix = true;
}
else
break;
firstStep = false;
}
doAppendInt(newFileName, parentFileName, fileDuration, false, doublePrefix ? MPLS_PRIMARY : MPLS_NONE);
if (mplsFileList.size() > 0)
doAppendInt(mplsFileList[0].name, newFileName, mplsFileList[0].duration, doublePrefix, MPLS_M2TS);
for (int mplsCnt = 1; mplsCnt < mplsFileList.size(); ++mplsCnt)
doAppendInt(mplsFileList[mplsCnt].name, mplsFileList[mplsCnt-1].name, mplsFileList[mplsCnt].duration, doublePrefix, MPLS_M2TS);
updateMaxOffsets();
if (!outFileNameModified)
{
modifyOutFileName(newFileName);
outFileNameModified = true;
}
disableUpdatesCnt--;
updateMetaLines();
updateCustomChapters();
emit fileAppended();
}
void TsMuxerWindow::onRemoveTrackButtonClick()
{
if (ui.trackLV->currentItem())
deleteTrack(ui.trackLV->currentRow());
}
void TsMuxerWindow::onMoveUpButtonCLick()
{
if (ui.trackLV->currentItem() == 0 || ui.trackLV->currentRow() < 1)
return;
disableUpdatesCnt++;
moveRow(ui.trackLV->currentRow(), ui.trackLV->currentRow()-1);
updateMetaLines();
updateNum();
disableUpdatesCnt--;
}
void TsMuxerWindow::onMoveDownButtonCLick()
{
if (ui.trackLV->currentItem() == 0 || ui.trackLV->rowCount() == 0 || ui.trackLV->currentRow() == ui.trackLV->rowCount()-1)
return;
disableUpdatesCnt++;
moveRow(ui.trackLV->currentRow(), ui.trackLV->currentRow()+2);
updateMetaLines();
updateNum();
disableUpdatesCnt--;
}
void TsMuxerWindow::moveRow(int index, int index2)
{
ui.trackLV->insertRow(index2);
ui.trackLV->setRowHeight(index2, 18);
if (index2 < index)
index++;
for (int i = 0; i < ui.trackLV->columnCount(); ++i)
ui.trackLV->setItem(index2, i, ui.trackLV->item(index,i)->clone());
ui.trackLV->removeRow(index);
if (index2 > index)
index2--;
ui.trackLV->setRangeSelected(QTableWidgetSelectionRange(index2, 0, index2, 4), true);
ui.trackLV->setCurrentCell(index2, 0);
}
void TsMuxerWindow::RadioButtonMuxClick()
{
if (outFileNameDisableChange)
return;
if (ui.radioButtonDemux->isChecked())
ui.buttonMux->setText("Sta&rt demuxing");
else
ui.buttonMux->setText("Sta&rt muxing");
outFileNameDisableChange = true;
if (ui.radioButtonBluRay->isChecked() || ui.radioButtonDemux->isChecked() || ui.radioButtonAVCHD->isChecked())
{
QFileInfo fi(unquoteStr(ui.outFileName->text()));
if (!fi.suffix().isEmpty())
{
oldFileName = fi.fileName();
ui.outFileName->setText(QDir::toNativeSeparators(fi.absolutePath()) + QDir::separator());
}
ui.FilenameLabel->setText(tr("Folder"));
}
else {
ui.FilenameLabel->setText(tr("File name"));
if (!oldFileName.isEmpty())
{
ui.outFileName->setText(QDir::toNativeSeparators(ui.outFileName->text()));
if (!ui.outFileName->text().isEmpty() && ui.outFileName->text().right(1) != QDir::separator())
ui.outFileName->setText(ui.outFileName->text() + QDir::separator());
ui.outFileName->setText(ui.outFileName->text() + oldFileName);
oldFileName.clear();
}
if (ui.radioButtonTS->isChecked()) {
ui.outFileName->setText(changeFileExt(ui.outFileName->text(), "ts"));
saveDialogFilter = TS_SAVE_DIALOG_FILTER;
}
else if (ui.radioButtonBluRayISO->isChecked()) {
ui.outFileName->setText(changeFileExt(ui.outFileName->text(), "iso"));
saveDialogFilter = ISO_SAVE_DIALOG_FILTER;
}
else {
ui.outFileName->setText(changeFileExt(ui.outFileName->text(), "m2ts"));
saveDialogFilter = M2TS_SAVE_DIALOG_FILTER;
}
}
ui.DiskLabel->setVisible(ui.radioButtonBluRayISO->isChecked());
ui.DiskLabelEdit->setVisible(ui.radioButtonBluRayISO->isChecked());
ui.editDelay->setEnabled(!ui.radioButtonDemux->isChecked());
updateMetaLines();
outFileNameDisableChange = false;
}
void TsMuxerWindow::outFileNameChanged()
{
outFileNameModified = true;
if (outFileNameDisableChange)
return;
if (ui.radioButtonDemux->isChecked() || ui.radioButtonBluRay->isChecked() || ui.radioButtonAVCHD->isChecked())
return;
outFileNameDisableChange = true;
QFileInfo fi(unquoteStr(ui.outFileName->text().trimmed()));
QString ext = fi.suffix().toUpper();
bool isISOMode = ui.radioButtonBluRayISO->isChecked();
if (ext == "M2TS" || ext == "M2TS\"")
ui.radioButtonM2TS->setChecked(true);
else if (ext == "ISO" || ext == "ISO\"") {
ui.radioButtonBluRayISO->setChecked(true);
}
else
ui.radioButtonTS->setChecked(true);
bool isISOModeNew = ui.radioButtonBluRayISO->isChecked();
ui.DiskLabel->setVisible(ui.radioButtonBluRayISO->isChecked());
ui.DiskLabelEdit->setVisible(ui.radioButtonBluRayISO->isChecked());
if (isISOMode != isISOModeNew)
updateMetaLines();
outFileNameDisableChange = false;
}
void TsMuxerWindow::saveFileDialog()
{
if (ui.radioButtonDemux->isChecked() || ui.radioButtonBluRay->isChecked() || ui.radioButtonAVCHD->isChecked())
{
QString folder = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, getOutputDir()));
if (!folder.isEmpty()) {
ui.outFileName->setText(folder + QDir::separator());
outFileNameModified = true;
lastOutputDir = folder;
writeSettings();
}
}
else {
QString path = getOutputDir();
if (path.isEmpty()) {
QString fileName = unquoteStr(ui.outFileName->text());
path = QFileInfo(fileName).absolutePath();
}
QString fileName = QDir::toNativeSeparators(QFileDialog::getSaveFileName(this, "Select file for muxing", path, saveDialogFilter));
if (!fileName.isEmpty()) {
ui.outFileName->setText(fileName);
lastOutputDir = QFileInfo(fileName).absolutePath();
writeSettings();
}
}
}
void TsMuxerWindow::startMuxing()
{
QString outputName = unquoteStr(ui.outFileName->text().trimmed());
ui.outFileName->setText(outputName);
lastOutputDir = QFileInfo(outputName).absolutePath();
writeSettings();
if (ui.radioButtonM2TS->isChecked())
{
QFileInfo fi(ui.outFileName->text());
if (fi.suffix().toUpper() != "M2TS") {
QMessageBox msgBox(this);
msgBox. setWindowTitle("Invalid file name");
msgBox.setText(QString("The output file \"") + ui.outFileName->text() + "\" has invalid extension. Please, change file extension to \".m2ts\"");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return;
}
}
else if (ui.radioButtonBluRayISO->isChecked())
{
QFileInfo fi(ui.outFileName->text());
if (fi.suffix().toUpper() != "ISO") {
QMessageBox msgBox(this);
msgBox. setWindowTitle("Invalid file name");
msgBox.setText(QString("The output file \"") + ui.outFileName->text() + "\" has invalid extension. Please, change file extension to \".iso\"");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return;
}
}
bool isFile = ui.radioButtonM2TS->isChecked() || ui.radioButtonTS->isChecked() || ui.radioButtonBluRayISO->isChecked();
if (isFile && QFile::exists(ui.outFileName->text()))
{
QString fileOrDir = isFile ? "file" : "directory";
QMessageBox msgBox(this);
msgBox. setWindowTitle(QString("Overwrite existing ") + fileOrDir +"?");
msgBox.setText(QString("The output ") + fileOrDir + " \"" + ui.outFileName->text() + "\" already exists. Do you want to overwrite it?\"");
msgBox.setIcon(QMessageBox::Question);
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
if (msgBox.exec() != QMessageBox::Yes)
return;
}
QFileInfo fi(ui.outFileName->text());
metaName = QDir::toNativeSeparators(QDir::tempPath()) + QDir::separator() + "tsMuxeR_" + fi.completeBaseName () + ".meta";
metaName = metaName.replace(' ', '_');
if (!saveMetaFile(metaName)) {
metaName.clear();
return;
}
muxForm.prepare(!ui.radioButtonDemux->isChecked() ? "Muxing in progress" : "Demuxing in progress");
ui.buttonMux->setEnabled(false);
ui.addBtn->setEnabled(false);
ui.btnAppend->setEnabled(false);
muxForm.show();
disconnect();
//QCoreApplication::dir
runInMuxMode = true;
shellExecute(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + QDir::separator() + "tsMuxeR", QStringList() << metaName << quoteStr(ui.outFileName->text()));
}
void TsMuxerWindow::saveMetaFileBtnClick()
{
QString metaName = QFileDialog::getSaveFileName(this, "", changeFileExt(ui.outFileName->text(), "meta"), saveMetaFilter);
if (metaName.isEmpty())
return;
QFileInfo fi(metaName);
QDir dir;
dir.mkpath(fi.absolutePath());
saveMetaFile(metaName);
}
bool TsMuxerWindow::saveMetaFile(const QString& metaName)
{
QFile file(metaName);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox msgBox(this);
msgBox. setWindowTitle("Can't create temporary meta file");
msgBox.setText(QString("Can't create temporary meta file \"") + metaName + "\"");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return false;
}
QByteArray metaText = ui.memoMeta->toPlainText().toLocal8Bit();
file.write(metaText);
file.close();
return true;
}
void TsMuxerWindow::closeEvent(QCloseEvent * event )
{
if (!metaName.isEmpty()) {
QFile::remove(metaName);
metaName.clear();
}
muxForm.close();
event->accept();
}
void TsMuxerWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("text/plain") || event->mimeData()->hasFormat("text/uri-list"))
{
if (ui.addBtn->isEnabled())
{
opacityTimer.stop();
setWindowOpacity(0.9);
event->acceptProposedAction();
QWidget* w = childAt(event->pos());
updateBtns(w);
}
}
}
void TsMuxerWindow::dropEvent(QDropEvent* event)
{
setWindowOpacity(1.0);
updateBtns(0);
QWidget* w = childAt(event->pos());
QString wName;
if (w)
wName = w->objectName();
if (event->mimeData()->hasFormat("text/uri-list")) {
addFileList = event->mimeData()->urls();
event->acceptProposedAction();
}
else if (event->mimeData()->hasFormat("text/plain"))
{
QList<QString> strList;
addFileList.clear();
splitLines(event->mimeData()->text(), strList);
QList <QUrl> urls = event->mimeData()->urls();
for (int i = 0; i < strList.size(); ++i)
addFileList << QUrl::fromLocalFile(strList[i]);
event->acceptProposedAction();
}
if (addFileList.isEmpty())
return;
if (wName == "btnAppend" && ui.btnAppend->isEnabled())
appendFile();
else if (ui.addBtn->isEnabled())
addFile();
}
void TsMuxerWindow::dragMoveEvent(QDragMoveEvent *event)
{
event->acceptProposedAction();
QWidget* w = childAt(event->pos());
updateBtns(w);
}
void TsMuxerWindow::updateBtns(QWidget* w)
{
if (w) {
QString wName = w->objectName();
ui.btnAppend->setDefault(wName == "btnAppend" && ui.btnAppend->isEnabled());
ui.addBtn->setDefault(wName == "addBtn" && ui.addBtn->isEnabled());
}
else {
ui.btnAppend->setDefault(false);
ui.addBtn->setDefault(false);
}
QFont font = ui.removeFile->font();
QFont bFont(font);
bFont.setBold(true);
if (ui.btnAppend->isDefault())
ui.btnAppend->setFont(bFont);
else
ui.btnAppend->setFont(font);
if (ui.addBtn->isDefault())
ui.addBtn->setFont(bFont);
else
ui.addBtn->setFont(font);
}
void TsMuxerWindow::dragLeaveEvent ( QDragLeaveEvent * event)
{
opacityTimer.start(100);
}
void TsMuxerWindow::onOpacityTimer()
{
opacityTimer.stop();
setWindowOpacity(1.0);
updateBtns(0);
}
void TsMuxerWindow::updateMaxOffsets()
{
int maxPGOffsets = 0;
m_3dMode = false;
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
{
long iCodec = ui.trackLV->item(i,0)->data(Qt::UserRole).toLongLong();
if (!iCodec) continue;
QtvCodecInfo* codecInfo = (QtvCodecInfo*) (void*) iCodec;
if (codecInfo->displayName == "MVC")
{
m_3dMode = true;
maxPGOffsets = qMax(maxPGOffsets, codecInfo->maxPgOffsets);
}
}
disableUpdatesCnt++;
int oldIndex = ui.offsetsComboBox->currentIndex();
ui.offsetsComboBox->clear();
ui.offsetsComboBox->addItem(QString("zero"));
for (int i = 0; i < maxPGOffsets; ++i)
ui.offsetsComboBox->addItem(QString("plane #%1").arg(i));
if (oldIndex >= 0 && oldIndex < ui.offsetsComboBox->count())
ui.offsetsComboBox->setCurrentIndex(oldIndex);
disableUpdatesCnt--;
}
bool TsMuxerWindow::eventFilter(QObject *obj, QEvent* event)
{
if (obj == ui.label_Donate && event->type() == QEvent::MouseButtonPress) {
QDesktopServices::openUrl(QUrl("https://github.com/justdan96/tsMuxer"));
return true;
}
else {
return QWidget::eventFilter(obj, event);
}
}
void TsMuxerWindow::at_sectionCheckstateChanged(Qt::CheckState state)
{
if (disableUpdatesCnt > 0)
return;
disableUpdatesCnt++;
for (int i = 0; i < ui.trackLV->rowCount(); ++i)
ui.trackLV->item(i,0)->setCheckState(state);
disableUpdatesCnt--;
trackLVItemSelectionChanged();
}
void TsMuxerWindow::writeSettings()
{
if (disableUpdatesCnt > 0)
return;
disableUpdatesCnt++;
settings->beginGroup("general");
//settings->setValue("asyncIO", ui.checkBoxuseAsynIO->isChecked());
settings->setValue("soundEnabled", ui.checkBoxSound->isChecked());
settings->setValue("hdmvPES", ui.checkBoxNewAudioPes->isChecked());
if (ui.checkBoxCrop->isEnabled())
settings->setValue("restoreCropEnabled", ui.checkBoxCrop->isChecked());
settings->setValue("outputDir", lastOutputDir);
settings->setValue("useBlankPL", ui.checkBoxBlankPL->isChecked());
settings->setValue("blankPLNum", ui.BlackplaylistCombo->value());
settings->setValue("outputToInputFolder", ui.radioButtonOutoutInInput->isChecked());
settings->endGroup();
settings->beginGroup("subtitles");
settings->setValue("fontBorder", ui.spinEditBorder->value());
settings->setValue("fontLineSpacing", ui.lineSpacing->value());
settings->setValue("offset", ui.spinEditOffset->value());
settings->setValue("fadeTime", getRendererAnimationTime());
settings->setValue("famaly", ui.listViewFont->item(0,1)->text());
settings->setValue("size", ui.listViewFont->item(1,1)->text().toUInt());
settings->setValue("color", ui.listViewFont->item(2,1)->text().mid(2).toUInt(0, 16));
settings->setValue("options", ui.listViewFont->item(4,1)->text());
settings->endGroup();
settings->beginGroup("pip");
settings->setValue("corner", ui.comboBoxPipCorner->currentIndex());
settings->setValue("h_offset", ui.spinBoxPipOffsetH->value());
settings->setValue("v_offset", ui.spinBoxPipOffsetV->value());
settings->setValue("size", ui.comboBoxPipSize->currentIndex());
settings->endGroup();
disableUpdatesCnt--;
}
bool TsMuxerWindow::readSettings()
{
settings->beginGroup("general");
QString outputDir = settings->value("outputDir").toString();
if (!outputDir.isEmpty())
lastOutputDir = outputDir;
else {
settings->endGroup();
return false; // no settings still written
}
//ui.checkBoxuseAsynIO->setChecked(settings->value("asyncIO").toBool());
ui.checkBoxSound->setChecked(settings->value("soundEnabled").toBool());
ui.checkBoxNewAudioPes->setChecked(settings->value("hdmvPES").toBool());
ui.checkBoxCrop->setChecked(settings->value("restoreCropEnabled").toBool());
ui.checkBoxBlankPL->setChecked(settings->value("useBlankPL").toBool());
int plNum = settings->value("blankPLNum").toInt();
if (plNum)
ui.BlackplaylistCombo->setValue(plNum);
ui.radioButtonOutoutInInput->setChecked(settings->value("outputToInputFolder").toBool());
ui.radioButtonStoreOutput->setChecked(!ui.radioButtonOutoutInInput->isChecked());
settings->endGroup();
//checkBoxVBR checkBoxRVBR editMaxBitrate editMinBitrate checkBoxCBR editCBRBitrate editVBVLen
settings->beginGroup("subtitles");
ui.spinEditBorder->setValue(settings->value("fontBorder").toInt());
ui.lineSpacing->setValue(settings->value("fontLineSpacing").toDouble());
setRendererAnimationTime(settings->value("fadeTime").toDouble());
ui.spinEditOffset->setValue(settings->value("offset").toInt());
QString fontName = settings->value("famaly").toString();
if (!fontName.isEmpty())
ui.listViewFont->item(0,1)->setText(fontName);
int fontSize = settings->value("size").toInt();
if (fontSize > 0)
ui.listViewFont->item(1,1)->setText(QString::number(fontSize));
if (!settings->value("color").isNull()) {
quint32 color = settings->value("color").toUInt();
setTextItemColor(QString::number(color, 16));
}
ui.listViewFont->item(4,1)->setText(settings->value("options").toString());
settings->endGroup();
settings->beginGroup("pip");
ui.comboBoxPipCorner->setCurrentIndex(settings->value("corner").toInt());
ui.spinBoxPipOffsetH->setValue(settings->value("h_offset").toInt());
ui.spinBoxPipOffsetV->setValue(settings->value("v_offset").toInt());
ui.comboBoxPipSize->setCurrentIndex(settings->value("size").toInt());
settings->endGroup();
return true;
}