1
0

OBSRemux.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /******************************************************************************
  2. Copyright (C) 2014 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "OBSRemux.hpp"
  15. #include <utility/RemuxEntryPathItemDelegate.hpp>
  16. #include <utility/RemuxQueueModel.hpp>
  17. #include <utility/RemuxWorker.hpp>
  18. #include <widgets/OBSBasic.hpp>
  19. #include <qt-wrappers.hpp>
  20. #include <QDirIterator>
  21. #include <QDropEvent>
  22. #include <QMimeData>
  23. #include <QPushButton>
  24. #include "moc_OBSRemux.cpp"
  25. OBSRemux::OBSRemux(const char *path, QWidget *parent, bool autoRemux_)
  26. : QDialog(parent),
  27. queueModel(new RemuxQueueModel),
  28. worker(new RemuxWorker()),
  29. ui(new Ui::OBSRemux),
  30. recPath(path),
  31. autoRemux(autoRemux_)
  32. {
  33. setAcceptDrops(true);
  34. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  35. ui->setupUi(this);
  36. ui->progressBar->setVisible(false);
  37. ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
  38. ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
  39. if (autoRemux) {
  40. resize(280, 40);
  41. ui->tableView->hide();
  42. ui->buttonBox->hide();
  43. ui->label->hide();
  44. }
  45. ui->progressBar->setMinimum(0);
  46. ui->progressBar->setMaximum(1000);
  47. ui->progressBar->setValue(0);
  48. ui->tableView->setModel(queueModel);
  49. ui->tableView->setItemDelegateForColumn(RemuxEntryColumn::InputPath,
  50. new RemuxEntryPathItemDelegate(false, recPath));
  51. ui->tableView->setItemDelegateForColumn(RemuxEntryColumn::OutputPath,
  52. new RemuxEntryPathItemDelegate(true, recPath));
  53. ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch);
  54. ui->tableView->horizontalHeader()->setSectionResizeMode(RemuxEntryColumn::State,
  55. QHeaderView::ResizeMode::Fixed);
  56. ui->tableView->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged);
  57. ui->tableView->setTextElideMode(Qt::ElideMiddle);
  58. ui->tableView->setWordWrap(false);
  59. installEventFilter(CreateShortcutFilter());
  60. ui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr("Remux.Remux"));
  61. ui->buttonBox->button(QDialogButtonBox::Reset)->setText(QTStr("Remux.ClearFinished"));
  62. ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(QTStr("Remux.ClearAll"));
  63. ui->buttonBox->button(QDialogButtonBox::Reset)->setDisabled(true);
  64. connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &OBSRemux::beginRemux);
  65. connect(ui->buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, this, &OBSRemux::clearFinished);
  66. connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this,
  67. &OBSRemux::clearAll);
  68. connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &OBSRemux::close);
  69. worker->moveToThread(&remuxer);
  70. remuxer.start();
  71. connect(worker.data(), &RemuxWorker::updateProgress, this, &OBSRemux::updateProgress);
  72. connect(&remuxer, &QThread::finished, worker.data(), &QObject::deleteLater);
  73. connect(worker.data(), &RemuxWorker::remuxFinished, this, &OBSRemux::remuxFinished);
  74. connect(this, &OBSRemux::remux, worker.data(), &RemuxWorker::remux);
  75. connect(queueModel.data(), &RemuxQueueModel::rowsInserted, this, &OBSRemux::rowCountChanged);
  76. connect(queueModel.data(), &RemuxQueueModel::rowsRemoved, this, &OBSRemux::rowCountChanged);
  77. QModelIndex index = queueModel->createIndex(0, 1);
  78. QMetaObject::invokeMethod(ui->tableView, "setCurrentIndex", Qt::QueuedConnection,
  79. Q_ARG(const QModelIndex &, index));
  80. }
  81. bool OBSRemux::stopRemux()
  82. {
  83. if (!worker->isWorking)
  84. return true;
  85. // By locking the worker thread's mutex, we ensure that its
  86. // update poll will be blocked as long as we're in here with
  87. // the popup open.
  88. QMutexLocker lock(&worker->updateMutex);
  89. bool exit = false;
  90. if (QMessageBox::critical(nullptr, QTStr("Remux.ExitUnfinishedTitle"), QTStr("Remux.ExitUnfinished"),
  91. QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
  92. exit = true;
  93. }
  94. if (exit) {
  95. // Inform the worker it should no longer be
  96. // working. It will interrupt accordingly in
  97. // its next update callback.
  98. worker->isWorking = false;
  99. }
  100. return exit;
  101. }
  102. OBSRemux::~OBSRemux()
  103. {
  104. stopRemux();
  105. remuxer.quit();
  106. remuxer.wait();
  107. }
  108. void OBSRemux::rowCountChanged(const QModelIndex &, int, int)
  109. {
  110. // See if there are still any rows ready to remux. Change
  111. // the state of the "go" button accordingly.
  112. // There must be more than one row, since there will always be
  113. // at least one row for the empty insertion point.
  114. if (queueModel->rowCount() > 1) {
  115. ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
  116. ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);
  117. ui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(queueModel->canClearFinished());
  118. } else {
  119. ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
  120. ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
  121. ui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false);
  122. }
  123. }
  124. void OBSRemux::dropEvent(QDropEvent *ev)
  125. {
  126. QStringList urlList;
  127. for (QUrl url : ev->mimeData()->urls()) {
  128. QFileInfo fileInfo(url.toLocalFile());
  129. if (fileInfo.isDir()) {
  130. QStringList directoryFilter;
  131. directoryFilter << "*.flv"
  132. << "*.mp4"
  133. << "*.mov"
  134. << "*.mkv"
  135. << "*.ts"
  136. << "*.m3u8";
  137. QDirIterator dirIter(fileInfo.absoluteFilePath(), directoryFilter, QDir::Files,
  138. QDirIterator::Subdirectories);
  139. while (dirIter.hasNext()) {
  140. urlList.append(dirIter.next());
  141. }
  142. } else {
  143. urlList.append(fileInfo.canonicalFilePath());
  144. }
  145. }
  146. if (urlList.empty()) {
  147. QMessageBox::information(nullptr, QTStr("Remux.NoFilesAddedTitle"), QTStr("Remux.NoFilesAdded"),
  148. QMessageBox::Ok);
  149. } else if (!autoRemux) {
  150. QModelIndex insertIndex = queueModel->index(queueModel->rowCount() - 1, RemuxEntryColumn::InputPath);
  151. queueModel->setData(insertIndex, urlList, RemuxEntryRole::NewPathsToProcessRole);
  152. }
  153. }
  154. void OBSRemux::dragEnterEvent(QDragEnterEvent *ev)
  155. {
  156. if (ev->mimeData()->hasUrls() && !worker->isWorking)
  157. ev->accept();
  158. }
  159. void OBSRemux::beginRemux()
  160. {
  161. if (worker->isWorking) {
  162. stopRemux();
  163. return;
  164. }
  165. bool proceedWithRemux = true;
  166. QFileInfoList overwriteFiles = queueModel->checkForOverwrites();
  167. if (!overwriteFiles.empty()) {
  168. QString message = QTStr("Remux.FileExists");
  169. message += "\n\n";
  170. for (QFileInfo fileInfo : overwriteFiles)
  171. message += fileInfo.canonicalFilePath() + "\n";
  172. if (OBSMessageBox::question(this, QTStr("Remux.FileExistsTitle"), message) != QMessageBox::Yes)
  173. proceedWithRemux = false;
  174. }
  175. if (!proceedWithRemux)
  176. return;
  177. // Set all jobs to "pending" first.
  178. queueModel->beginProcessing();
  179. ui->progressBar->setVisible(true);
  180. ui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr("Remux.Stop"));
  181. setAcceptDrops(false);
  182. remuxNextEntry();
  183. }
  184. void OBSRemux::AutoRemux(QString inFile, QString outFile)
  185. {
  186. if (inFile != "" && outFile != "" && autoRemux) {
  187. ui->progressBar->setVisible(true);
  188. emit remux(inFile, outFile);
  189. autoRemuxFile = outFile;
  190. }
  191. }
  192. void OBSRemux::remuxNextEntry()
  193. {
  194. worker->lastProgress = 0.f;
  195. QString inputPath, outputPath;
  196. if (queueModel->beginNextEntry(inputPath, outputPath)) {
  197. emit remux(inputPath, outputPath);
  198. } else {
  199. queueModel->autoRemux = autoRemux;
  200. queueModel->endProcessing();
  201. if (!autoRemux) {
  202. OBSMessageBox::information(this, QTStr("Remux.FinishedTitle"),
  203. queueModel->checkForErrors() ? QTStr("Remux.FinishedError")
  204. : QTStr("Remux.Finished"));
  205. }
  206. ui->progressBar->setVisible(autoRemux);
  207. ui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr("Remux.Remux"));
  208. ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);
  209. ui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(queueModel->canClearFinished());
  210. setAcceptDrops(true);
  211. }
  212. }
  213. void OBSRemux::closeEvent(QCloseEvent *event)
  214. {
  215. if (!stopRemux())
  216. event->ignore();
  217. else
  218. QDialog::closeEvent(event);
  219. }
  220. void OBSRemux::reject()
  221. {
  222. if (!stopRemux())
  223. return;
  224. QDialog::reject();
  225. }
  226. void OBSRemux::updateProgress(float percent)
  227. {
  228. ui->progressBar->setValue(percent * 10);
  229. }
  230. void OBSRemux::remuxFinished(bool success)
  231. {
  232. ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
  233. queueModel->finishEntry(success);
  234. if (autoRemux && autoRemuxFile != "") {
  235. QTimer::singleShot(3000, this, &OBSRemux::close);
  236. OBSBasic *main = OBSBasic::Get();
  237. main->ShowStatusBarMessage(QTStr("Basic.StatusBar.AutoRemuxedTo").arg(autoRemuxFile));
  238. }
  239. remuxNextEntry();
  240. }
  241. void OBSRemux::clearFinished()
  242. {
  243. queueModel->clearFinished();
  244. }
  245. void OBSRemux::clearAll()
  246. {
  247. queueModel->clearAll();
  248. }