window-remux.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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 "window-remux.hpp"
  15. #include "obs-app.hpp"
  16. #include <QCloseEvent>
  17. #include <QFileDialog>
  18. #include <QFileInfo>
  19. #include <QMessageBox>
  20. #include "qt-wrappers.hpp"
  21. #include <memory>
  22. #include <cmath>
  23. using namespace std;
  24. OBSRemux::OBSRemux(const char *path, QWidget *parent)
  25. : QDialog (parent),
  26. worker (new RemuxWorker),
  27. ui (new Ui::OBSRemux),
  28. recPath (path)
  29. {
  30. ui->setupUi(this);
  31. ui->progressBar->setVisible(false);
  32. ui->buttonBox->button(QDialogButtonBox::Ok)->
  33. setEnabled(false);
  34. ui->targetFile->setEnabled(false);
  35. ui->browseTarget->setEnabled(false);
  36. ui->progressBar->setMinimum(0);
  37. ui->progressBar->setMaximum(1000);
  38. ui->progressBar->setValue(0);
  39. installEventFilter(CreateShortcutFilter());
  40. connect(ui->browseSource, &QPushButton::clicked,
  41. [&]() { BrowseInput(); });
  42. connect(ui->browseTarget, &QPushButton::clicked,
  43. [&]() { BrowseOutput(); });
  44. connect(ui->sourceFile, &QLineEdit::textChanged,
  45. this, &OBSRemux::inputChanged);
  46. ui->buttonBox->button(QDialogButtonBox::Ok)->
  47. setText(QTStr("Remux.Remux"));
  48. connect(ui->buttonBox->button(QDialogButtonBox::Ok),
  49. SIGNAL(clicked()), this, SLOT(Remux()));
  50. connect(ui->buttonBox->button(QDialogButtonBox::Close),
  51. SIGNAL(clicked()), this, SLOT(close()));
  52. worker->moveToThread(&remuxer);
  53. remuxer.start();
  54. //gcc-4.8 can't use QPointer<RemuxWorker> below
  55. RemuxWorker *worker_ = worker;
  56. connect(worker_, &RemuxWorker::updateProgress,
  57. this, &OBSRemux::updateProgress);
  58. connect(&remuxer, &QThread::finished, worker_, &QObject::deleteLater);
  59. connect(worker_, &RemuxWorker::remuxFinished,
  60. this, &OBSRemux::remuxFinished);
  61. connect(this, &OBSRemux::remux, worker_, &RemuxWorker::remux);
  62. }
  63. bool OBSRemux::Stop()
  64. {
  65. if (!worker->job)
  66. return true;
  67. if (QMessageBox::critical(nullptr,
  68. QTStr("Remux.ExitUnfinishedTitle"),
  69. QTStr("Remux.ExitUnfinished"),
  70. QMessageBox::Yes | QMessageBox::No,
  71. QMessageBox::No) ==
  72. QMessageBox::Yes) {
  73. os_event_signal(worker->stop);
  74. return true;
  75. }
  76. return false;
  77. }
  78. OBSRemux::~OBSRemux()
  79. {
  80. Stop();
  81. remuxer.quit();
  82. remuxer.wait();
  83. }
  84. #define RECORDING_PATTERN "(*.flv *.mp4 *.mov *.mkv *.ts *.m3u8)"
  85. void OBSRemux::BrowseInput()
  86. {
  87. QString path = ui->sourceFile->text();
  88. if (path.isEmpty())
  89. path = recPath;
  90. path = QFileDialog::getOpenFileName(this,
  91. QTStr("Remux.SelectRecording"), path,
  92. QTStr("Remux.OBSRecording") + QString(" ") +
  93. RECORDING_PATTERN);
  94. inputChanged(path);
  95. }
  96. void OBSRemux::inputChanged(const QString &path)
  97. {
  98. if (!QFileInfo::exists(path)) {
  99. ui->buttonBox->button(QDialogButtonBox::Ok)->
  100. setEnabled(false);
  101. return;
  102. }
  103. ui->sourceFile->setText(path);
  104. ui->buttonBox->button(QDialogButtonBox::Ok)->
  105. setEnabled(true);
  106. QFileInfo fi(path);
  107. QString mp4 = fi.path() + "/" + fi.baseName() + ".mp4";
  108. ui->targetFile->setText(mp4);
  109. ui->targetFile->setEnabled(true);
  110. ui->browseTarget->setEnabled(true);
  111. }
  112. void OBSRemux::BrowseOutput()
  113. {
  114. QString path(ui->targetFile->text());
  115. path = QFileDialog::getSaveFileName(this, QTStr("Remux.SelectTarget"),
  116. path, RECORDING_PATTERN);
  117. if (path.isEmpty())
  118. return;
  119. ui->targetFile->setText(path);
  120. }
  121. void OBSRemux::Remux()
  122. {
  123. if (QFileInfo::exists(ui->targetFile->text()))
  124. if (QMessageBox::question(this, QTStr("Remux.FileExistsTitle"),
  125. QTStr("Remux.FileExists"),
  126. QMessageBox::Yes | QMessageBox::No) !=
  127. QMessageBox::Yes)
  128. return;
  129. media_remux_job_t mr_job = nullptr;
  130. if (!media_remux_job_create(&mr_job, QT_TO_UTF8(ui->sourceFile->text()),
  131. QT_TO_UTF8(ui->targetFile->text())))
  132. return;
  133. worker->job = job_t(mr_job, media_remux_job_destroy);
  134. worker->lastProgress = 0.f;
  135. ui->progressBar->setVisible(true);
  136. ui->buttonBox->button(QDialogButtonBox::Ok)->
  137. setEnabled(false);
  138. emit remux();
  139. }
  140. void OBSRemux::closeEvent(QCloseEvent *event)
  141. {
  142. if (!Stop())
  143. event->ignore();
  144. else
  145. QDialog::closeEvent(event);
  146. }
  147. void OBSRemux::reject()
  148. {
  149. if (!Stop())
  150. return;
  151. QDialog::reject();
  152. }
  153. void OBSRemux::updateProgress(float percent)
  154. {
  155. ui->progressBar->setValue(percent * 10);
  156. }
  157. void OBSRemux::remuxFinished(bool success)
  158. {
  159. QMessageBox::information(this, QTStr("Remux.FinishedTitle"),
  160. success ?
  161. QTStr("Remux.Finished") : QTStr("Remux.FinishedError"));
  162. worker->job.reset();
  163. ui->progressBar->setVisible(false);
  164. ui->buttonBox->button(QDialogButtonBox::Ok)->
  165. setEnabled(true);
  166. }
  167. RemuxWorker::RemuxWorker()
  168. {
  169. os_event_init(&stop, OS_EVENT_TYPE_MANUAL);
  170. }
  171. RemuxWorker::~RemuxWorker()
  172. {
  173. os_event_destroy(stop);
  174. }
  175. void RemuxWorker::UpdateProgress(float percent)
  176. {
  177. if (abs(lastProgress - percent) < 0.1f)
  178. return;
  179. emit updateProgress(percent);
  180. lastProgress = percent;
  181. }
  182. void RemuxWorker::remux()
  183. {
  184. auto callback = [](void *data, float percent)
  185. {
  186. auto rw = static_cast<RemuxWorker*>(data);
  187. rw->UpdateProgress(percent);
  188. return !!os_event_try(rw->stop);
  189. };
  190. bool success = media_remux_job_process(job.get(), callback, this);
  191. emit remuxFinished(os_event_try(stop) && success);
  192. }