提交 c9ee436e 编写于 作者: P Palana

Add UI for remuxing recordings via FFmpeg

上级 4247a7b8
......@@ -97,6 +97,7 @@ set(obs_SOURCES
window-basic-preview.cpp
window-namedialog.cpp
window-log-reply.cpp
window-remux.cpp
properties-view.cpp
volume-control.cpp
qt-wrappers.cpp)
......@@ -116,6 +117,7 @@ set(obs_HEADERS
window-basic-preview.hpp
window-namedialog.hpp
window-log-reply.hpp
window-remux.hpp
properties-view.hpp
display-helpers.hpp
volume-control.hpp
......@@ -131,7 +133,8 @@ set(obs_UI
forms/OBSBasicSettings.ui
forms/OBSBasicSourceSelect.ui
forms/OBSBasicInteraction.ui
forms/OBSBasicProperties.ui)
forms/OBSBasicProperties.ui
forms/OBSRemux.ui)
set(obs_QRC
forms/obs.qrc)
......
......@@ -61,6 +61,21 @@ LicenseAgreement.ClickIAgreeToContinue="If you accept the terms of the agreement
LicenseAgreement.IAgree="I Agree"
LicenseAgreement.Exit="Exit"
# remux dialog
Remux.SourceFile="OBS Recording"
Remux.TargetFile="Target File"
Remux.Remux="Remux"
Remux.RecordingPattern="OBS Recording (*.flv)"
Remux.FinishedTitle="Remuxing finished"
Remux.Finished="Recording remuxed"
Remux.FinishedError="Recording remuxed, but the file may be incomplete"
Remux.SelectRecording="Select OBS Recording …"
Remux.SelectTarget="Select target file …"
Remux.FileExistsTitle="Target file exists"
Remux.FileExists="Target file exists, do you want to replace it?"
Remux.ExitUnfinishedTitle="Remuxing in progress"
Remux.ExitUnfinished="Remuxing is not finished, stopping now may render the target file unusable.\nAre you sure you want to stop remuxing?"
# update dialog
UpdateAvailable="New Update Available"
UpdateAvailable.Text="Version %1.%2.%3 is now available. <a href='%4'>Click here to download</a>"
......@@ -148,6 +163,7 @@ Basic.MainMenu.File="&File"
Basic.MainMenu.File.Export="&Export"
Basic.MainMenu.File.Import="&Import"
Basic.MainMenu.File.ShowRecordings="Show &Recordings"
Basic.MainMenu.File.Remux="&Remux Recordings"
Basic.MainMenu.File.Settings="&Settings"
Basic.MainMenu.File.Exit="E&xit"
......
......@@ -457,6 +457,7 @@
<addaction name="action_Open"/>
<addaction name="separator"/>
<addaction name="actionShow_Recordings"/>
<addaction name="actionRemux"/>
<addaction name="separator"/>
<addaction name="action_Settings"/>
<addaction name="separator"/>
......@@ -651,6 +652,11 @@
<string>Basic.MainMenu.File.ShowRecordings</string>
</property>
</action>
<action name="actionRemux">
<property name="text">
<string>Basic.MainMenu.File.Remux</string>
</property>
</action>
<action name="action_Settings">
<property name="text">
<string>Basic.MainMenu.File.Settings</string>
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OBSRemux</class>
<widget class="QDialog" name="OBSRemux">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>491</width>
<height>124</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>351</width>
<height>23</height>
</rect>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
<widget class="QWidget" name="formLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>471</width>
<height>71</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Remux.SourceFile</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Remux.TargetFile</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="sourceFile">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browseSource">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="targetFile">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browseTarget">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QPushButton" name="remux">
<property name="geometry">
<rect>
<x>370</x>
<y>90</y>
<width>111</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Remux.Remux</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
......@@ -38,6 +38,7 @@
#include "window-basic-main.hpp"
#include "window-basic-properties.hpp"
#include "window-log-reply.hpp"
#include "window-remux.hpp"
#include "qt-wrappers.hpp"
#include "display-helpers.hpp"
#include "volume-control.hpp"
......@@ -1474,6 +1475,14 @@ void OBSBasic::on_actionShow_Recordings_triggered()
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
void OBSBasic::on_actionRemux_triggered()
{
const char *path = config_get_string(basicConfig,
"SimpleOutput", "FilePath");
OBSRemux remux(path, this);
remux.exec();
}
void OBSBasic::on_action_Settings_triggered()
{
OBSBasicSettings settings(this);
......
......@@ -233,6 +233,7 @@ private slots:
void on_action_Open_triggered();
void on_action_Save_triggered();
void on_actionShow_Recordings_triggered();
void on_actionRemux_triggered();
void on_action_Settings_triggered();
void on_actionShowLogs_triggered();
void on_actionUploadCurrentLog_triggered();
......
/******************************************************************************
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "window-remux.hpp"
#include "obs-app.hpp"
#include <QCloseEvent>
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
#include "qt-wrappers.hpp"
#include <memory>
#include <cmath>
using namespace std;
OBSRemux::OBSRemux(const char *path, QWidget *parent)
: QDialog (parent),
worker (new RemuxWorker),
ui (new Ui::OBSRemux),
recPath (path)
{
ui->setupUi(this);
ui->progressBar->setVisible(false);
ui->remux->setEnabled(false);
ui->targetFile->setEnabled(false);
ui->browseTarget->setEnabled(false);
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(1000);
ui->progressBar->setValue(0);
connect(ui->browseSource, &QPushButton::clicked,
[&]() { BrowseInput(); });
connect(ui->browseTarget, &QPushButton::clicked,
[&]() { BrowseOutput(); });
connect(ui->remux, &QPushButton::clicked, [&]() { Remux(); });
connect(ui->sourceFile, &QLineEdit::textChanged,
this, &OBSRemux::inputChanged);
worker->moveToThread(&remuxer);
//gcc-4.8 can't use QPointer<RemuxWorker> below
RemuxWorker *worker_ = worker;
connect(worker_, &RemuxWorker::updateProgress,
this, &OBSRemux::updateProgress);
connect(&remuxer, &QThread::finished, worker_, &QObject::deleteLater);
connect(worker_, &RemuxWorker::remuxFinished,
this, &OBSRemux::remuxFinished);
connect(this, &OBSRemux::remux, worker_, &RemuxWorker::remux);
}
bool OBSRemux::Stop()
{
if (!worker->job)
return true;
if (QMessageBox::critical(nullptr,
QTStr("Remux.ExitUnfinishedTitle"),
QTStr("Remux.ExitUnfinished"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) ==
QMessageBox::Yes) {
os_event_signal(worker->stop);
return true;
}
return false;
}
OBSRemux::~OBSRemux()
{
Stop();
remuxer.quit();
remuxer.wait();
}
void OBSRemux::BrowseInput()
{
QString path = ui->sourceFile->text();
if (path.isEmpty())
path = recPath;
path = QFileDialog::getOpenFileName(this,
QTStr("Remux.SelectRecording"), path,
QTStr("Remux.RecordingPattern"));
inputChanged(path);
}
void OBSRemux::inputChanged(const QString &path)
{
if (!QFileInfo::exists(path)) {
ui->remux->setEnabled(false);
return;
}
ui->sourceFile->setText(path);
ui->remux->setEnabled(true);
QFileInfo fi(path);
QString mp4 = fi.path() + "/" + fi.baseName() + ".mp4";
ui->targetFile->setText(mp4);
ui->targetFile->setEnabled(true);
ui->browseTarget->setEnabled(true);
}
void OBSRemux::BrowseOutput()
{
QString path(ui->targetFile->text());
path = QFileDialog::getSaveFileName(this, QTStr("Remux.SelectTarget"),
path, "(*.mp4)");
if (path.isEmpty())
return;
ui->targetFile->setText(path);
}
void OBSRemux::Remux()
{
if (QFileInfo::exists(ui->targetFile->text()))
if (QMessageBox::question(this, QTStr("Remux.FileExistsTitle"),
QTStr("Remux.FileExists"),
QMessageBox::Yes | QMessageBox::No) !=
QMessageBox::Yes)
return;
media_remux_job_t mr_job = nullptr;
if (!media_remux_job_create(&mr_job, QT_TO_UTF8(ui->sourceFile->text()),
QT_TO_UTF8(ui->targetFile->text())))
return;
worker->job = job_t(mr_job, media_remux_job_destroy);
worker->lastProgress = 0.f;
ui->progressBar->setVisible(true);
ui->remux->setEnabled(false);
if (!remuxer.isRunning())
remuxer.start();
emit remux();
}
void OBSRemux::closeEvent(QCloseEvent *event)
{
if (!Stop())
event->ignore();
else
QDialog::closeEvent(event);
}
void OBSRemux::reject()
{
if (!Stop())
return;
QDialog::reject();
}
void OBSRemux::updateProgress(float percent)
{
ui->progressBar->setValue(percent * 10);
}
void OBSRemux::remuxFinished(bool success)
{
QMessageBox::information(this, QTStr("Remux.FinishedTitle"),
success ?
QTStr("Remux.Finished") : QTStr("Remux.FinishedError"));
worker->job.reset();
ui->progressBar->setVisible(false);
ui->remux->setEnabled(true);
}
RemuxWorker::RemuxWorker()
{
os_event_init(&stop, OS_EVENT_TYPE_MANUAL);
}
RemuxWorker::~RemuxWorker()
{
os_event_destroy(stop);
}
void RemuxWorker::UpdateProgress(float percent)
{
if (abs(lastProgress - percent) < 0.1f)
return;
emit updateProgress(percent);
lastProgress = percent;
}
void RemuxWorker::remux()
{
auto callback = [](void *data, float percent)
{
auto rw = static_cast<RemuxWorker*>(data);
rw->UpdateProgress(percent);
return !!os_event_try(rw->stop);
};
bool success = media_remux_job_process(job.get(), callback, this);
emit remuxFinished(os_event_try(stop) && success);
}
/******************************************************************************
Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include <QPointer>
#include <QThread>
#include <memory>
#include "ui_OBSRemux.h"
#include <media-io/media-remux.h>
#include <util/threading.h>
class RemuxWorker;
class OBSRemux : public QDialog {
Q_OBJECT
QThread remuxer;
QPointer<RemuxWorker> worker;
std::unique_ptr<Ui::OBSRemux> ui;
const char *recPath;
void BrowseInput();
void BrowseOutput();
void Remux();
bool Stop();
virtual void closeEvent(QCloseEvent *event) override;
virtual void reject() override;
public:
explicit OBSRemux(const char *recPath, QWidget *parent = nullptr);
virtual ~OBSRemux() override;
using job_t = std::shared_ptr<struct media_remux_job>;
private slots:
void inputChanged(const QString &str);
public slots:
void updateProgress(float percent);
void remuxFinished(bool success);
signals:
void remux();
};
class RemuxWorker : public QObject {
Q_OBJECT
OBSRemux::job_t job;
os_event_t *stop;
float lastProgress;
void UpdateProgress(float percent);
explicit RemuxWorker();
virtual ~RemuxWorker();
private slots:
void remux();
signals:
void updateProgress(float percent);
void remuxFinished(bool success);
friend class OBSRemux;
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册