2634 lines
93 KiB
C++
2634 lines
93 KiB
C++
#include "tsmuxerwindow.h"
|
|
#include "lang_codes.h"
|
|
#include <QApplication>
|
|
#include <QColorDialog>
|
|
#include <QFontDialog>
|
|
#include <QMessageBox>
|
|
#include <QSettings>
|
|
#include <QUrl>
|
|
|
|
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, &QnCheckBoxedHeaderView::sectionClicked, this,
|
|
&QnCheckBoxedHeaderView::at_sectionClicked);
|
|
}
|
|
|
|
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, &QnCheckBoxedHeaderView::checkStateChanged, this,
|
|
&TsMuxerWindow::at_sectionCheckstateChanged);
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
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));
|
|
}
|
|
const auto comboBoxIndexChanged = QOverload<int>::of(&QComboBox::currentIndexChanged);
|
|
const auto spinBoxValueChanged = QOverload<int>::of(&QSpinBox::valueChanged);
|
|
const auto doubleSpinBoxValueChanged = QOverload<double>::of(&QDoubleSpinBox::valueChanged);
|
|
connect(&opacityTimer, &QTimer::timeout, this, &TsMuxerWindow::onOpacityTimer);
|
|
connect(ui.trackLV, &QTableWidget::itemSelectionChanged, this,
|
|
&TsMuxerWindow::trackLVItemSelectionChanged);
|
|
connect(ui.trackLV, &QTableWidget::itemChanged, this,
|
|
&TsMuxerWindow::trackLVItemChanged);
|
|
connect(ui.inputFilesLV, &QListWidget::currentRowChanged, this,
|
|
&TsMuxerWindow::inputFilesLVChanged);
|
|
connect(ui.addBtn, &QPushButton::clicked, this, &TsMuxerWindow::onAddBtnClick);
|
|
connect(ui.btnAppend, &QPushButton::clicked, this, &TsMuxerWindow::onAppendButtonClick);
|
|
connect(ui.removeFile, &QPushButton::clicked, this, &TsMuxerWindow::onRemoveBtnClick);
|
|
connect(ui.removeTrackBtn, &QPushButton::clicked, this,
|
|
&TsMuxerWindow::onRemoveTrackButtonClick);
|
|
connect(ui.moveupBtn, &QPushButton::clicked, this, &TsMuxerWindow::onMoveUpButtonCLick);
|
|
connect(ui.movedownBtn, &QPushButton::clicked, this,
|
|
&TsMuxerWindow::onMoveDownButtonCLick);
|
|
connect(ui.checkFPS, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onVideoCheckBoxChanged);
|
|
connect(ui.checkBoxLevel, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onVideoCheckBoxChanged);
|
|
connect(ui.comboBoxSEI, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onVideoCheckBoxChanged);
|
|
connect(ui.checkBoxSecondaryVideo, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onVideoCheckBoxChanged);
|
|
connect(ui.checkBoxSPS, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onVideoCheckBoxChanged);
|
|
connect(ui.checkBoxRemovePulldown, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onPulldownCheckBoxChanged);
|
|
connect(ui.comboBoxFPS, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onVideoComboBoxChanged);
|
|
connect(ui.comboBoxLevel, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onVideoComboBoxChanged);
|
|
connect(ui.comboBoxAR, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onVideoComboBoxChanged);
|
|
connect(ui.checkBoxKeepFps, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onAudioSubtitlesParamsChanged);
|
|
connect(ui.dtsDwnConvert, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onAudioSubtitlesParamsChanged);
|
|
connect(ui.secondaryCheckBox, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onAudioSubtitlesParamsChanged);
|
|
connect(ui.langComboBox, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onAudioSubtitlesParamsChanged);
|
|
connect(ui.offsetsComboBox, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onAudioSubtitlesParamsChanged);
|
|
connect(ui.comboBoxPipCorner, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.comboBoxPipSize, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.spinBoxPipOffsetH, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.spinBoxPipOffsetV, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.checkBoxSound, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.editDelay, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onEditDelayChanged);
|
|
connect(ui.muxTimeEdit, &QTimeEdit::timeChanged, this,
|
|
&TsMuxerWindow::updateMuxTime1);
|
|
connect(ui.muxTimeClock, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::updateMuxTime2);
|
|
connect(ui.fontButton, &QPushButton::clicked, this, &TsMuxerWindow::onFontBtnClicked);
|
|
connect(ui.colorButton, &QPushButton::clicked, this, &TsMuxerWindow::onColorBtnClicked);
|
|
connect(ui.checkBoxVBR, &QPushButton::clicked, this,
|
|
&TsMuxerWindow::onGeneralCheckboxClicked);
|
|
connect(ui.spinBoxMplsNum, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onGeneralCheckboxClicked);
|
|
connect(ui.spinBoxM2tsNum, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onGeneralCheckboxClicked);
|
|
connect(ui.checkBoxBlankPL, &QPushButton::clicked, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.checkBoxV3, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::updateMetaLines);
|
|
connect(ui.BlackplaylistCombo, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.checkBoxNewAudioPes, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.checkBoxCrop, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.checkBoxRVBR, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onGeneralCheckboxClicked);
|
|
connect(ui.checkBoxCBR, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onGeneralCheckboxClicked);
|
|
// connect(ui.checkBoxuseAsynIO, SIGNAL(stateChanged(int)), this,
|
|
// SLOT(onSavedParamChanged()));
|
|
connect(ui.radioButtonStoreOutput, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.radioButtonOutoutInInput, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.editVBVLen, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onGeneralSpinboxValueChanged);
|
|
connect(ui.editMaxBitrate, doubleSpinBoxValueChanged, this,
|
|
&TsMuxerWindow::onGeneralSpinboxValueChanged);
|
|
connect(ui.editMinBitrate, doubleSpinBoxValueChanged, this,
|
|
&TsMuxerWindow::onGeneralSpinboxValueChanged);
|
|
connect(ui.editCBRBitrate, doubleSpinBoxValueChanged, this,
|
|
&TsMuxerWindow::onGeneralSpinboxValueChanged);
|
|
connect(ui.rightEyeCheckBox, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::updateMetaLines);
|
|
connect(ui.radioButtonAutoChapter, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onChapterParamsChanged);
|
|
connect(ui.radioButtonNoChapters, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onChapterParamsChanged);
|
|
connect(ui.radioButtonCustomChapters, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onChapterParamsChanged);
|
|
connect(ui.spinEditChapterLen, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onChapterParamsChanged);
|
|
connect(ui.memoChapters, &QTextEdit::textChanged, this,
|
|
&TsMuxerWindow::onChapterParamsChanged);
|
|
connect(ui.noSplit, &QAbstractButton::clicked, this, &TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.splitByDuration, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.splitBySize, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.spinEditSplitDuration, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.editSplitSize, doubleSpinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.comboBoxMeasure, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.checkBoxCut, &QCheckBox::stateChanged, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.cutStartTimeEdit, &QTimeEdit::timeChanged, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.cutEndTimeEdit, &QTimeEdit::timeChanged, this,
|
|
&TsMuxerWindow::onSplitCutParamsChanged);
|
|
connect(ui.spinEditBorder, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.comboBoxAnimation, comboBoxIndexChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.lineSpacing, doubleSpinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.spinEditOffset, spinBoxValueChanged, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
connect(ui.radioButtonTS, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::RadioButtonMuxClick);
|
|
connect(ui.radioButtonM2TS, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::RadioButtonMuxClick);
|
|
connect(ui.radioButtonBluRay, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::RadioButtonMuxClick);
|
|
connect(ui.radioButtonBluRayISO, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::RadioButtonMuxClick);
|
|
connect(ui.radioButtonAVCHD, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::RadioButtonMuxClick);
|
|
connect(ui.radioButtonDemux, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::RadioButtonMuxClick);
|
|
connect(ui.outFileName, &QLineEdit::textChanged, this,
|
|
&TsMuxerWindow::outFileNameChanged);
|
|
connect(ui.DiskLabelEdit, &QLineEdit::textChanged, this,
|
|
&TsMuxerWindow::onGeneralCheckboxClicked);
|
|
connect(ui.btnBrowse, &QAbstractButton::clicked, this, &TsMuxerWindow::saveFileDialog);
|
|
connect(ui.buttonMux, &QAbstractButton::clicked, this, &TsMuxerWindow::startMuxing);
|
|
connect(ui.buttonSaveMeta, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::saveMetaFileBtnClick);
|
|
connect(ui.radioButtonOutoutInInput, &QAbstractButton::clicked, this,
|
|
&TsMuxerWindow::onSavedParamChanged);
|
|
|
|
connect(&proc, &QProcess::readyReadStandardOutput, this,
|
|
&TsMuxerWindow::readFromStdout);
|
|
connect(&proc, &QProcess::readyReadStandardError, this,
|
|
&TsMuxerWindow::readFromStderr);
|
|
connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
|
|
&TsMuxerWindow::onProcessFinished);
|
|
connect(&proc, QOverload<QProcess::ProcessError>::of(&QProcess::error), this,
|
|
&TsMuxerWindow::onProcessError);
|
|
|
|
ui.DiskLabel->setVisible(false);
|
|
ui.DiskLabelEdit->setVisible(false);
|
|
|
|
ui.label_Donate->installEventFilter(this);
|
|
|
|
ui.langComboBox->addItem("und (Undetermined)");
|
|
ui.langComboBox->addItem("--------- common ---------");
|
|
for (auto&& lang : shortLangList) {
|
|
ui.langComboBox->addItem(QString("%1 (%2)").arg(lang.code).arg(lang.lang),
|
|
QString::fromUtf8(lang.code));
|
|
}
|
|
ui.langComboBox->addItem("---------- all ----------");
|
|
for (auto&& lang : fullLangList) {
|
|
ui.langComboBox->addItem(QString("%1 (%2)").arg(lang.code).arg(lang.lang),
|
|
QString::fromUtf8(lang.code));
|
|
}
|
|
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, &TsMuxerWindow::tsMuxerSuccessFinished, this, &TsMuxerWindow::getCodecInfo);
|
|
connect(this, &TsMuxerWindow::codecListReady, this, &TsMuxerWindow::continueAddFile);
|
|
connect(this, &TsMuxerWindow::fileAdded, this, &TsMuxerWindow::addFile);
|
|
runInMuxMode = false;
|
|
tsMuxerExecute(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();
|
|
}
|
|
|
|
static QString getTsMuxerBinaryPath() {
|
|
const auto applicationDirPath =
|
|
QDir::toNativeSeparators(QCoreApplication::applicationDirPath()) + QDir::separator();
|
|
for(auto binaryName : {"tsmuxer", "tsMuxeR"}) {
|
|
auto binaryPath = QStandardPaths::findExecutable(binaryName, {applicationDirPath});
|
|
if (!binaryPath.isEmpty()) {
|
|
return binaryPath;
|
|
}
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void TsMuxerWindow::tsMuxerExecute(const QStringList &args) {
|
|
const auto exePath = getTsMuxerBinaryPath();
|
|
ui.buttonMux->setEnabled(false);
|
|
procStdOutput.clear();
|
|
procErrOutput.clear();
|
|
processFinished = false;
|
|
processExitCode = -1;
|
|
proc.start(exePath, 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 += (ui.checkBoxV3->isChecked() ? "--blu-ray-v3 " : "--blu-ray ");
|
|
else if (ui.radioButtonBluRayISO->isChecked()) {
|
|
rez += (ui.checkBoxV3->isChecked() ? "--blu-ray-v3 " : "--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, &TsMuxerWindow::tsMuxerSuccessFinished, this, &TsMuxerWindow::getCodecInfo);
|
|
connect(this, &TsMuxerWindow::codecListReady, this, &TsMuxerWindow::continueAppendFile);
|
|
connect(this, &TsMuxerWindow::fileAppended, this, &TsMuxerWindow::appendFile);
|
|
runInMuxMode = false;
|
|
tsMuxerExecute(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";
|
|
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;
|
|
tsMuxerExecute(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;
|
|
}
|