Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
# Created by Filip Biedniak, SLSE, 2020-06-15
|
||||
#
|
||||
|
||||
SET(scr_gui_SRCS
|
||||
scr_gui_plugin.cpp
|
||||
scr_gui_plugin.h
|
||||
|
||||
screen_recording_dialog.cpp
|
||||
screen_recording_dialog.h
|
||||
|
||||
screen_recording_worker.cpp
|
||||
screen_recording_worker.h
|
||||
)
|
||||
|
||||
NGL_WRAP_UI(scr_gui_SRCS
|
||||
screen_recording_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY(WS ScrScreenRecordingGuiPlugin MODULE ${scr_gui_SRCS})
|
||||
NGL_TARGET_LINK_LIBRARIES(ScrScreenRecordingGuiPlugin
|
||||
NGLLogger
|
||||
LtsSession
|
||||
GuiPluginInterface
|
||||
situationMap
|
||||
SituationMapGuiPluginInterface
|
||||
Qt5::Widgets
|
||||
LtsWidgets
|
||||
svcScrDummy
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(ScrScreenRecordingGuiPlugin)
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Gui Plugin for ScreenRecorder - Party
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Filip Biedziak, NLE, 2020-06-15
|
||||
*/
|
||||
|
||||
#include "scr_gui_plugin.h"
|
||||
#include "screen_recording_dialog.h"
|
||||
#include "screen_recording_worker.h"
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <LtsWidgets/lts_tool_bar_area.h>
|
||||
#include <LtsWidgets/lts_drag_icon.h>
|
||||
|
||||
#include <Services/ScreenRecording/scr_operator_rights.h>
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QToolButton>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
namespace ScreenRecording
|
||||
{
|
||||
|
||||
#define SCREEN_RECORDING_IDENTIFIER QLatin1String("scrScreenRecorder")
|
||||
#define TAKE_SCREENSHOT_IDENTIFIER QLatin1String("scrTakeScreenshot")
|
||||
|
||||
|
||||
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
||||
{
|
||||
return std::make_shared<InterfaceImpl>(session);
|
||||
}
|
||||
|
||||
|
||||
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
|
||||
: GuiPluginBase::Interface(session, NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("ScreenRecording")) // TODO: log name
|
||||
, m_worker(new ScreenRecordingWorker(this))
|
||||
{
|
||||
}
|
||||
|
||||
bool InterfaceImpl::fetchInterfaces()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void InterfaceImpl::clearInterfaces()
|
||||
{
|
||||
}
|
||||
|
||||
QString InterfaceImpl::menuGroup() const
|
||||
{
|
||||
return tr("Screen Recording");
|
||||
}
|
||||
|
||||
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
||||
{
|
||||
QVector<LtsWidgets::PluginData> ret;
|
||||
|
||||
LtsWidgets::PluginData plDataScreenRec;
|
||||
plDataScreenRec.menuGroup = menuGroup();
|
||||
plDataScreenRec.menuText = tr("Record screen");
|
||||
plDataScreenRec.identifier = SCREEN_RECORDING_IDENTIFIER;
|
||||
plDataScreenRec.menuIcon = QIcon(QLatin1String("icon:rec_screen.svg"));
|
||||
plDataScreenRec.operatorRight = OperatorRights::ScreenRecording::RecordScreen;
|
||||
plDataScreenRec.exercisePart = LtsWidgets::ExercisePart::eSpecial;
|
||||
plDataScreenRec.directFunc = [this](const LtsWidgets::PluginData &) { onStartScreenRecorder(); };
|
||||
|
||||
LtsWidgets::PluginData plDataTakeScreen;
|
||||
plDataTakeScreen.menuGroup = menuGroup();
|
||||
plDataTakeScreen.menuText = tr("Take screenshot");
|
||||
plDataTakeScreen.identifier = TAKE_SCREENSHOT_IDENTIFIER;
|
||||
plDataTakeScreen.menuIcon = QIcon(QLatin1String("icon:screenshot.svg"));
|
||||
plDataTakeScreen.operatorRight = OperatorRights::ScreenRecording::TakeSnapshot;
|
||||
plDataTakeScreen.exercisePart = LtsWidgets::ExercisePart::eSpecial;
|
||||
plDataTakeScreen.directFunc = [this](const LtsWidgets::PluginData &) { onTakeScreenshot(); };
|
||||
|
||||
ret.append(plDataScreenRec);
|
||||
ret.append(plDataTakeScreen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
QWidget *InterfaceImpl::onStartScreenRecorder()
|
||||
{
|
||||
if (m_worker->isBusy())
|
||||
{
|
||||
if (m_worker->stopRecording())
|
||||
onUpdateGui();
|
||||
return nullptr;
|
||||
} // if
|
||||
|
||||
m_dialog = new ScreenRecordingDialog(menuGroup());
|
||||
if (!m_dialog->init(m_session))
|
||||
return nullptr;
|
||||
|
||||
connect(m_dialog, &ScreenRecordingDialog::startRecording,
|
||||
m_worker, &ScreenRecordingWorker::onStartRecording);
|
||||
connect(m_dialog, &ScreenRecordingDialog::startRecording,
|
||||
this, &InterfaceImpl::onUpdateGui);
|
||||
|
||||
m_dialog->show();
|
||||
return m_dialog;
|
||||
}
|
||||
|
||||
QWidget *InterfaceImpl::onTakeScreenshot()
|
||||
{
|
||||
QString recFile = QDir(ScreenRecordingWorker::s_screenshotTmpDir).filePath(QLatin1String("screenshot_") +
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).replace(QLatin1Char(':'), QLatin1Char('-')) +
|
||||
QLatin1String(".png"));
|
||||
recFile = QFileDialog::getSaveFileName(nullptr,
|
||||
tr("Save Screenshot"),
|
||||
recFile,
|
||||
tr("Images (*.jpg *.jpeg *.png);;All files (*.*)"));
|
||||
|
||||
if (recFile.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
QFileInfo fileInfo{recFile};
|
||||
m_worker->takeScreenshot(fileInfo, m_session->baseLayout());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE SLOTS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void InterfaceImpl::onUpdateGui()
|
||||
{
|
||||
// add tool button to show red blinking light during recording
|
||||
if (!m_toolBtn)
|
||||
{
|
||||
m_toolBtn = new QToolButton();
|
||||
m_timer = new QTimer(m_toolBtn);
|
||||
m_toolBtn->setIcon(m_recIcon);
|
||||
m_toolBtn->setIconSize(QSize{24, 24});
|
||||
m_toolBtn->setStyleSheet(QLatin1String("QToolButton{border: none} QToolButton:hover{background: none; border: none}"));
|
||||
connect(m_timer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
m_toolBtn->setIcon((m_counter++ % 2) ? m_recIcon : QIcon());
|
||||
m_toolBtn->setToolTip(QDateTime::fromSecsSinceEpoch(m_counter, Qt::UTC).toString(QLatin1String("hh:mm:ss")));
|
||||
});
|
||||
}
|
||||
|
||||
QPointer<LtsWidgets::LtsDragIcon> menuBtn = m_session->baseLayout()->findChild<LtsWidgets::LtsDragIcon *>(SCREEN_RECORDING_IDENTIFIER);
|
||||
QIcon icon = QIcon(QLatin1String("icon:rec_screen.svg"));
|
||||
|
||||
if (m_worker->isBusy())
|
||||
{
|
||||
icon = QIcon(QLatin1String("icon:avm_stop.svg"));
|
||||
menuBtn->setText(QLatin1String("Stop recording"));
|
||||
m_session->baseLayout()->addNotification(m_toolBtn);
|
||||
m_toolBtn->show();
|
||||
m_counter = 0;
|
||||
m_timer->start(1000);
|
||||
} // if
|
||||
else
|
||||
{
|
||||
menuBtn->setText(QLatin1String("Record screen"));
|
||||
m_timer->stop();
|
||||
m_toolBtn->hide();
|
||||
} // else
|
||||
menuBtn->setIcon(icon);
|
||||
|
||||
// update toolbar icon if it exists
|
||||
for (auto &it : m_session->baseLayout()->getToolbar()->findChildren<LtsWidgets::LtsDragIcon *>()) // findChild + identifier does not work for some reason
|
||||
{
|
||||
if (it->identifier() == SCREEN_RECORDING_IDENTIFIER)
|
||||
{
|
||||
it->setIcon(icon);
|
||||
break;
|
||||
} // if
|
||||
} // for
|
||||
}
|
||||
|
||||
|
||||
#undef TAKE_SCREENSHOT_IDENTIFIER
|
||||
#undef SCREEN_RECORDING_IDENTIFIER
|
||||
|
||||
} // namespace ScreenRecording
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Gui Plugin for ScreenRecorder - Party
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Filip Biedziak, NLE, 2020-06-15
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
|
||||
|
||||
class QToolButton;
|
||||
class QIcon;
|
||||
|
||||
|
||||
namespace ScreenRecording
|
||||
{
|
||||
|
||||
#define RECORDING_TEMP_DIR QDir::tempPath() + QLatin1String("/screenrecording/")
|
||||
#define SCREENSHOT_TEMP_DIR QDir::tempPath() + QLatin1String("/screenshot/")
|
||||
|
||||
|
||||
class ScreenRecordingDialog;
|
||||
class ScreenRecordingWorker;
|
||||
|
||||
|
||||
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
|
||||
{
|
||||
/**
|
||||
* \brief GuiPlugin provides an interface for recording the screen.
|
||||
*
|
||||
* FFmpeg is used to record the screen with the user being able to adjust the settings, such as:
|
||||
* - What will be recorded
|
||||
* - Where it will be saved
|
||||
* - How the video will be encoded
|
||||
* To start FFmpeg, a QProcess is used to start it as a command line application via a worker
|
||||
* class ScreenRecordingWorker.
|
||||
*/
|
||||
Q_PLUGIN_METADATA(IID "gui:3:scr.screenrecorder")
|
||||
Q_INTERFACES(GuiPluginBase::Plugin)
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
GuiPlugin() = default;
|
||||
~GuiPlugin() = default;
|
||||
|
||||
QString pluginName() override { return QStringLiteral("ScreenRecording.GuiPlugin.ScreenRecorder"); }
|
||||
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override
|
||||
{
|
||||
return (partition == IDL::PartitionType::Global ||
|
||||
partition == IDL::PartitionType::Exercise ||
|
||||
partition == IDL::PartitionType::Replay);
|
||||
}
|
||||
};
|
||||
|
||||
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InterfaceImpl(LtsSession::LtsSession *session);
|
||||
|
||||
bool fetchInterfaces() override;
|
||||
void clearInterfaces() override;
|
||||
QString menuGroup() const override;
|
||||
QVector<LtsWidgets::PluginData> getActions() override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override
|
||||
{
|
||||
return partition == IDL::PartitionType::Global ||
|
||||
partition == IDL::PartitionType::Exercise ||
|
||||
partition == IDL::PartitionType::Replay;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
private:
|
||||
/**
|
||||
* @brief onStartScreenRecorder opens the screen recording dialog.
|
||||
*
|
||||
* If there is recording already in process, this will not open a dialog, but stop the recording
|
||||
* using the worker class' member function ScreenRecordingWorker::stopProcess() instead. It will
|
||||
* also paint the button in the menu to its default style.
|
||||
* @return screen recording dialog
|
||||
*/
|
||||
QWidget *onStartScreenRecorder();
|
||||
/**
|
||||
* @brief onTakeScreenshot takes a snapshot of the application.
|
||||
*
|
||||
* This will prompt a save file dialog to determine where to save the screenshot. The worker
|
||||
* class is then called to make the snapshot using ScreenRecordingWorker::takeScreenshot(). No
|
||||
* screenshot is taken if the file path is empty.
|
||||
* @return nullptr
|
||||
*/
|
||||
QWidget *onTakeScreenshot();
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE SLOTS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
private slots:
|
||||
/**
|
||||
* @brief onUpdateGui paints the menu button to reflect the recording state.
|
||||
*
|
||||
* This will update the Gui to emphasize the recording state. The icon in the menu will be
|
||||
* replaced by a different icon and a recording notification is shown or hidden. Uses the worker
|
||||
* class' member function ScreenRecordingWorker::isBusy() to determine whether recording is
|
||||
* taking place.
|
||||
*/
|
||||
void onUpdateGui();
|
||||
|
||||
private:
|
||||
ScreenRecordingWorker *m_worker{nullptr};
|
||||
ScreenRecordingDialog *m_dialog{nullptr};
|
||||
QToolButton *m_toolBtn{nullptr};
|
||||
QTimer *m_timer{nullptr};
|
||||
QIcon m_recIcon{ QLatin1String("icon:rec_on.svg") };
|
||||
|
||||
quint32 m_counter{ 0 };
|
||||
};
|
||||
|
||||
} // namespace ScreenRecording
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* ScreenRecording - Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Filip Biedziak, NLE, 2020-06-23
|
||||
*/
|
||||
|
||||
#include "screen_recording_dialog.h"
|
||||
#include "ui_screen_recording_dialog.h"
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <LtsWidgets/lts_tab_widget.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
|
||||
|
||||
namespace ScreenRecording
|
||||
{
|
||||
|
||||
ScreenRecordingDialog::ScreenRecordingDialog(const QString &menuGroup, QWidget *parent, const LtsWidgets::Behaviour &behaviour)
|
||||
: LtsFramelessDialog(parent, behaviour)
|
||||
, m_ui(new Ui::ScreenRecordingDialog)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("ScreenRecording"))
|
||||
{
|
||||
Q_UNUSED(menuGroup);
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_recAreaWidget = new QWidget();
|
||||
|
||||
connect(m_ui->pbBrowseRecordingFile, &QPushButton::clicked, this, &ScreenRecordingDialog::onBrowseRecordingFile);
|
||||
connect(m_ui->cobRecordingArea, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ScreenRecordingDialog::onChangeRecordingArea);
|
||||
connect(m_ui->chbShowArea, &QCheckBox::clicked, m_recAreaWidget, &QWidget::setVisible);
|
||||
connect(m_ui->chbRecordTitleBar, &QCheckBox::clicked, this, &ScreenRecordingDialog::onChangeRecordingArea);
|
||||
|
||||
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked, this, &ScreenRecordingDialog::onRecord);
|
||||
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &ScreenRecordingDialog::onCancel);
|
||||
}
|
||||
|
||||
ScreenRecordingDialog::~ScreenRecordingDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
bool ScreenRecordingDialog::init(LtsSession::LtsSession *session)
|
||||
{
|
||||
m_baseLayoutPtr = session->baseLayout();
|
||||
|
||||
if (m_baseLayoutPtr == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "base layout is nullptr";
|
||||
return false;
|
||||
} // if
|
||||
|
||||
m_recAreaWidget->setWindowOpacity(0.5);
|
||||
m_recAreaWidget->setStyleSheet(QLatin1String("border: 5px solid red"));
|
||||
m_recAreaWidget->setWindowFlags(Qt::SubWindow | Qt::FramelessWindowHint);
|
||||
|
||||
m_ui->leRecordingFile->setText(QDir(ScreenRecordingWorker::s_recordingTmpDir).filePath(QLatin1String("screenrecording_") +
|
||||
QDateTime::currentDateTime().toString(Qt::ISODate).replace(QLatin1Char(':'), QLatin1Char('-')) +
|
||||
QLatin1String(".") + ScreenRecordingWorker::s_videoFormat));
|
||||
|
||||
for (const auto &frameRate : allFrameRates)
|
||||
m_ui->cobFrameRate->addItem(ScreenRecordingWorker::frameRateFromToString(frameRate), QVariant::fromValue(frameRate));
|
||||
m_ui->cobFrameRate->setCurrentIndex(m_ui->cobFrameRate->findData(QVariant::fromValue(ScreenRecordingWorker::FrameRate::Medium)));
|
||||
|
||||
for (const auto &videoCodec : allVideoCodecs)
|
||||
m_ui->cobVideoCodec->addItem(ScreenRecordingWorker::videoCodecToString(videoCodec), QVariant::fromValue(videoCodec));
|
||||
m_ui->cobVideoCodec->setCurrentIndex(m_ui->cobVideoCodec->findData(QVariant::fromValue(ScreenRecordingWorker::VideoCodec::H264)));
|
||||
|
||||
for (const auto &recArea : allRecordingAreas)
|
||||
m_ui->cobRecordingArea->addItem(ScreenRecordingWorker::recordingAreaToString(recArea), QVariant::fromValue(recArea));
|
||||
m_ui->cobRecordingArea->setCurrentIndex(m_ui->cobRecordingArea->findData(QVariant::fromValue(ScreenRecordingWorker::RecordingArea::MainArea)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE SLOTS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void ScreenRecordingDialog::onBrowseRecordingFile()
|
||||
{
|
||||
QString recFile = QFileDialog::getSaveFileName(this,
|
||||
tr("Save Screen Recording"),
|
||||
m_ui->leRecordingFile->text(),
|
||||
tr("Videos (*.mp4);;All files (*.*)"));
|
||||
if (!recFile.isEmpty())
|
||||
m_ui->leRecordingFile->setText(recFile);
|
||||
}
|
||||
|
||||
void ScreenRecordingDialog::onChangeRecordingArea()
|
||||
{
|
||||
QWidget *mainWidgetPtr = m_baseLayoutPtr->getTabFromLayoutArea(LtsWidgets::LayoutArea::eMainTab);
|
||||
QPoint point = m_ui->chbRecordTitleBar->isChecked() ? m_baseLayoutPtr->mapToGlobal({})
|
||||
: m_baseLayoutPtr->getTabFromLayoutArea(LtsWidgets::LayoutArea::eInfoTab)->mapToGlobal({});
|
||||
QSize size = {m_baseLayoutPtr->width(),
|
||||
m_ui->chbRecordTitleBar->isChecked() ? m_baseLayoutPtr->height() : m_baseLayoutPtr->getMenu()->height()};
|
||||
|
||||
switch (m_ui->cobRecordingArea->currentData().value<ScreenRecordingWorker::RecordingArea>())
|
||||
{
|
||||
case ScreenRecordingWorker::RecordingArea::MainArea:
|
||||
point = {mainWidgetPtr->mapToGlobal({}).x(),
|
||||
m_ui->chbRecordTitleBar->isChecked() ? m_baseLayoutPtr->mapToGlobal({}).y()
|
||||
: mainWidgetPtr->mapToGlobal({}).y()};
|
||||
size = {mainWidgetPtr->width(),
|
||||
m_ui->chbRecordTitleBar->isChecked() ? mainWidgetPtr->mapToGlobal(mainWidgetPtr->geometry().bottomLeft()).y() - m_baseLayoutPtr->mapToGlobal({}).y() + 1
|
||||
: mainWidgetPtr->height()};
|
||||
break;
|
||||
|
||||
case ScreenRecordingWorker::RecordingArea::LeftOfMenu:
|
||||
size.setWidth(mainWidgetPtr->mapToGlobal(mainWidgetPtr->geometry().bottomRight()).x() - m_baseLayoutPtr->mapToGlobal({}).x() + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_recAreaWidget->setGeometry({point, size});
|
||||
}
|
||||
|
||||
void ScreenRecordingDialog::onRecord()
|
||||
{
|
||||
ScreenRecordingWorker::RecordingData recData;
|
||||
recData.filePath.setFile(m_ui->leRecordingFile->text().endsWith(QLatin1String(".mp4")) ? m_ui->leRecordingFile->text()
|
||||
: (m_ui->leRecordingFile->text() + QLatin1String(".mp4")));
|
||||
recData.frameRate = m_ui->cobFrameRate->currentData().value<ScreenRecordingWorker::FrameRate>();
|
||||
recData.recArea = m_recAreaWidget->geometry();
|
||||
recData.videoCodec = m_ui->cobVideoCodec->currentData().value<ScreenRecordingWorker::VideoCodec>();
|
||||
|
||||
m_recAreaWidget->close();
|
||||
m_recAreaWidget->deleteLater();
|
||||
m_recAreaWidget = nullptr;
|
||||
|
||||
emit startRecording(recData);
|
||||
accept();
|
||||
}
|
||||
|
||||
void ScreenRecordingDialog::onCancel()
|
||||
{
|
||||
m_recAreaWidget->close();
|
||||
reject();
|
||||
}
|
||||
|
||||
} // namespace ScreenRecording
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* ScreenRecording - Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Filip Biedziak, NLE, 2020-06-23
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "screen_recording_worker.h"
|
||||
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ScreenRecordingDialog;
|
||||
} // namespace Ui
|
||||
|
||||
namespace LtsWidgets
|
||||
{
|
||||
class LtsBaseLayoutWidget;
|
||||
}
|
||||
|
||||
namespace LtsSession
|
||||
{
|
||||
class LtsSession;
|
||||
}
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
|
||||
namespace ScreenRecording
|
||||
{
|
||||
|
||||
class ScreenRecordingDialog final
|
||||
: public LtsWidgets::LtsFramelessDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ScreenRecordingDialog(const QString &menuGroup,
|
||||
QWidget *parent = nullptr,
|
||||
const LtsWidgets::Behaviour &behaviour = LtsWidgets::Behaviour::Modal);
|
||||
~ScreenRecordingDialog();
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
public:
|
||||
/**
|
||||
* @brief init initializes member variables with default values.
|
||||
* @param session the current session
|
||||
* @return true if the initialization was successfull, false otherwise
|
||||
*/
|
||||
bool init(LtsSession::LtsSession *session);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// SIGNALS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
signals:
|
||||
void startRecording(const ScreenRecordingWorker::RecordingData&);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE SLOTS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
private slots:
|
||||
/**
|
||||
* @brief onBrowseRecordingFile opens a file dialog to choose the destination file.
|
||||
*/
|
||||
void onBrowseRecordingFile();
|
||||
/**
|
||||
* @brief onChangeRecordingArea updates the widget showing the area to record.
|
||||
*/
|
||||
void onChangeRecordingArea();
|
||||
/**
|
||||
* @brief onRecord collects the recording data from the GUI and emits the startRecording signal.
|
||||
*/
|
||||
void onRecord();
|
||||
/**
|
||||
* @brief onCancel closes the dialog.
|
||||
*/
|
||||
void onCancel();
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE MEMBER VARIABLES
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
private:
|
||||
Ui::ScreenRecordingDialog *m_ui{nullptr};
|
||||
NGL::INGLLogger &m_logger;
|
||||
LtsWidgets::LtsBaseLayoutWidget *m_baseLayoutPtr{nullptr};
|
||||
QWidget *m_recAreaWidget{nullptr};
|
||||
|
||||
}; // ScreenRecordingDialog
|
||||
|
||||
} // namespace ScreenRecording
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScreenRecordingDialog</class>
|
||||
<widget class="QWidget" name="ScreenRecordingDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>429</width>
|
||||
<height>271</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Screen Recording</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="gbRecordingOptions">
|
||||
<property name="title">
|
||||
<string>Recording Options</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cobFrameRate"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbxFrameRate">
|
||||
<property name="text">
|
||||
<string>Frame Rate:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cobVideoCodec"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbxVideoEncoder">
|
||||
<property name="text">
|
||||
<string>Video Encoder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="gbRecordingFile">
|
||||
<property name="title">
|
||||
<string>Recording File</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbxPath">
|
||||
<property name="text">
|
||||
<string>Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="leRecordingFile"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="pbBrowseRecordingFile">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="LtsWidgets::LtsDialogButtonBox" name="ltsDialogButtonBox"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="gbRecordingScreen">
|
||||
<property name="title">
|
||||
<string>Recording Screen</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbxRecordingArea">
|
||||
<property name="text">
|
||||
<string>Recording Area:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cobRecordingArea"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="chbShowArea">
|
||||
<property name="text">
|
||||
<string>Show Recording Area</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="chbRecordTitleBar">
|
||||
<property name="text">
|
||||
<string>Record Title Bar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LtsWidgets::LtsDialogButtonBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsWidgets/lts_dialog_button_box.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Screen Recording worker class for handling processes - Party
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Filip Biedziak, NLE, 2020-06-23
|
||||
*/
|
||||
|
||||
#include "screen_recording_worker.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QThread>
|
||||
#include <QProcess>
|
||||
|
||||
|
||||
namespace ScreenRecording
|
||||
{
|
||||
|
||||
#define EXECUTABLE QLatin1String("ffmpeg")
|
||||
#define DISPLAY QLatin1String(std::getenv("DISPLAY")) + QLatin1String(".0+")
|
||||
|
||||
|
||||
QStringList ScreenRecordingWorker::RecordingData::toStringList() const
|
||||
{
|
||||
QRect fixedRec = recArea;
|
||||
if (videoCodec == VideoCodec::H264)
|
||||
{
|
||||
if (recArea.width() % 2 > 0)
|
||||
fixedRec.setWidth(fixedRec.width() - 1);
|
||||
if (recArea.height() % 2 > 0)
|
||||
fixedRec.setHeight(fixedRec.height() - 1);
|
||||
}
|
||||
QStringList args = {QLatin1String("-y")};
|
||||
args << QLatin1String("-f") << QLatin1String("x11grab");
|
||||
args << QLatin1String("-s") << (QString::number(fixedRec.width()) + QLatin1String("x") + QString::number(fixedRec.height()));
|
||||
args << QLatin1String("-r") << QString::number(static_cast<quint32>(frameRate));
|
||||
args << QLatin1String("-i") << (DISPLAY + QString::number(fixedRec.x()) + QLatin1String(",") + QString::number(fixedRec.y()));
|
||||
args << QLatin1String("-c:v") << videoCodecToArg(videoCodec);
|
||||
// args << QLatin1String("-pix_fmt yuv420p");
|
||||
if (videoCodec == VideoCodec::H264)
|
||||
{
|
||||
args << QLatin1String("-vf");
|
||||
args << QLatin1String("format=yuv420p");
|
||||
args << QLatin1String("-colorspace");
|
||||
args << QLatin1String("1");
|
||||
args << QLatin1String("-color_trc");
|
||||
args << QLatin1String("1");
|
||||
args << QLatin1String("-color_primaries");
|
||||
args << QLatin1String("1");
|
||||
}
|
||||
//args << QLatin1String("-q:v") << QString::number(1);
|
||||
args << filePath.filePath();
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
const QString ScreenRecordingWorker::frameRateFromToString(ScreenRecordingWorker::FrameRate frameRate)
|
||||
{
|
||||
switch (frameRate)
|
||||
{
|
||||
case ScreenRecordingWorker::FrameRate::VeryLow:
|
||||
return tr("Very Low (%1 fps)").arg(static_cast<int>(ScreenRecordingWorker::FrameRate::VeryLow));
|
||||
|
||||
case ScreenRecordingWorker::FrameRate::Low:
|
||||
return tr("Low (%1 fps)").arg(static_cast<int>(ScreenRecordingWorker::FrameRate::Low));
|
||||
|
||||
case ScreenRecordingWorker::FrameRate::Medium:
|
||||
return tr("Medium (%1 fps)").arg(static_cast<int>(ScreenRecordingWorker::FrameRate::Medium));
|
||||
|
||||
case ScreenRecordingWorker::FrameRate::High:
|
||||
return tr("High (%1 fps)").arg(static_cast<int>(ScreenRecordingWorker::FrameRate::High));
|
||||
|
||||
case ScreenRecordingWorker::FrameRate::VeryHigh:
|
||||
return tr("Very High (%1 fps)").arg(static_cast<int>(ScreenRecordingWorker::FrameRate::VeryHigh));
|
||||
|
||||
default:
|
||||
return tr("Error");
|
||||
} // switch
|
||||
}
|
||||
|
||||
const QString ScreenRecordingWorker::videoCodecToString(ScreenRecordingWorker::VideoCodec videoCodec)
|
||||
{
|
||||
switch (videoCodec)
|
||||
{
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::H264:
|
||||
return QLatin1String("H264");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::FFv1:
|
||||
return QLatin1String("FFmpeg video codec #1");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::FFvHuff:
|
||||
return QLatin1String("HuffYUV FFmpeg variant");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::Huffyuv:
|
||||
return QLatin1String("HuffYUV (Huffington YUV)");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::Targa:
|
||||
return QLatin1String("Truevision Targa image");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::V210:
|
||||
return QLatin1String("Uncompressed 4:2:2 10-bit");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::ZLib:
|
||||
return QLatin1String("LCL (Lossless Codec Library) ZLIB");
|
||||
|
||||
default:
|
||||
return tr("Error");
|
||||
} // switch
|
||||
}
|
||||
|
||||
const QString ScreenRecordingWorker::videoCodecToArg(ScreenRecordingWorker::VideoCodec videoCodec)
|
||||
{
|
||||
switch (videoCodec)
|
||||
{
|
||||
case ScreenRecordingWorker::VideoCodec::H264:
|
||||
return QLatin1String("libx264");
|
||||
case ScreenRecordingWorker::VideoCodec::FFv1:
|
||||
return QLatin1String("ffv1");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::FFvHuff:
|
||||
return QLatin1String("ffvhuff");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::Huffyuv:
|
||||
return QLatin1String("huffyuv");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::Targa:
|
||||
return QLatin1String("targa");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::V210:
|
||||
return QLatin1String("v210");
|
||||
|
||||
case ScreenRecordingWorker::VideoCodec::ZLib:
|
||||
return QLatin1String("zlib");
|
||||
|
||||
default:
|
||||
return QLatin1String("");
|
||||
} // switch
|
||||
}
|
||||
|
||||
const QString ScreenRecordingWorker::recordingAreaToString(ScreenRecordingWorker::RecordingArea recordingArea)
|
||||
{
|
||||
switch (recordingArea)
|
||||
{
|
||||
case ScreenRecordingWorker::RecordingArea::FullScreen:
|
||||
return tr("Full Screen");
|
||||
|
||||
case ScreenRecordingWorker::RecordingArea::MainArea:
|
||||
return tr("Main Area");
|
||||
|
||||
case ScreenRecordingWorker::RecordingArea::LeftOfMenu:
|
||||
return tr("Left of Menu");
|
||||
|
||||
default:
|
||||
return tr("Error");
|
||||
} // switch
|
||||
}
|
||||
|
||||
const QString ScreenRecordingWorker::s_recordingTmpDir = QDir::tempPath() + QLatin1String("/screenrecording");
|
||||
|
||||
const QString ScreenRecordingWorker::s_screenshotTmpDir = QDir::tempPath() + QLatin1String("/screenshot");
|
||||
|
||||
const QString ScreenRecordingWorker::s_videoFormat = QLatin1String("mp4");
|
||||
|
||||
|
||||
const QStringList ScreenRecordingWorker::s_imageFormats = { QLatin1String("jpg"), QLatin1String("jepg"), QLatin1String("png") };
|
||||
|
||||
|
||||
ScreenRecordingWorker::ScreenRecordingWorker(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("ScreenRecording"))
|
||||
{
|
||||
QDir tmpRecordingDir(s_recordingTmpDir);
|
||||
if (!tmpRecordingDir.exists())
|
||||
tmpRecordingDir.mkpath(QLatin1String("."));
|
||||
|
||||
QDir tmpScreenshotDir(s_screenshotTmpDir);
|
||||
if (!tmpScreenshotDir.exists())
|
||||
tmpScreenshotDir.mkpath(QLatin1String("."));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
bool ScreenRecordingWorker::takeScreenshot(const QFileInfo &fileInfo, LtsWidgets::LtsBaseLayoutWidget *baseLayout)
|
||||
{
|
||||
if (!s_imageFormats.contains(fileInfo.suffix()))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "file format not supported (must be .jpg, .jpeg or .png)";
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList args = {QLatin1String("-y")}; // overwrite files
|
||||
args << QLatin1String("-f") << QLatin1String("x11grab");
|
||||
args << QLatin1String("-s") << (QString::number(baseLayout->geometry().width()) + QLatin1String("x") + QString::number(baseLayout->geometry().height()));
|
||||
args << QLatin1String("-r") << QString::number(1);
|
||||
args << QLatin1String("-i") << (DISPLAY + QString::number(baseLayout->mapToGlobal({}).x()) + QLatin1String(",") + QString::number(baseLayout->mapToGlobal({}).y()));
|
||||
args << QLatin1String("-vframes") << QString::number(1);
|
||||
args << fileInfo.absoluteFilePath();
|
||||
|
||||
QProcess *proc = new QProcess();
|
||||
proc->start(EXECUTABLE, args);
|
||||
|
||||
if (!proc->waitForStarted())
|
||||
{
|
||||
ERROR_LOG(m_logger) << QLatin1String("Could not start process '") + EXECUTABLE
|
||||
+ QLatin1String("' with arguments '") + args.join(QLatin1String("', '")) + QLatin1String("'\n");
|
||||
proc->deleteLater();
|
||||
return false;
|
||||
} // if
|
||||
|
||||
if (!proc->waitForFinished())
|
||||
{
|
||||
ERROR_LOG(m_logger) << QLatin1String("Could not finish process '") + EXECUTABLE
|
||||
+ QLatin1String("' with arguments '") + args.join(QLatin1String("', '")) + QLatin1String("'\n");
|
||||
proc->deleteLater();
|
||||
return false;
|
||||
} // if
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScreenRecordingWorker::stopRecording()
|
||||
{
|
||||
if (!m_isBusy)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "no running process";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_recProc->terminate();
|
||||
if (!m_recProc->waitForFinished(5000))
|
||||
m_recProc->kill();
|
||||
m_recProc->close();
|
||||
|
||||
m_isBusy = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC SLOTS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
bool ScreenRecordingWorker::onStartRecording(const RecordingData &data)
|
||||
{
|
||||
if (m_isBusy)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "process is already running";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.filePath.suffix() != s_videoFormat)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "file format not supported (must be .mp4)";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_isBusy = true;
|
||||
m_recProc = new QProcess();
|
||||
m_recProc->start(EXECUTABLE, data.toStringList());
|
||||
|
||||
if (!m_recProc->waitForStarted())
|
||||
{
|
||||
ERROR_LOG(m_logger) << QLatin1String("Could not start process '") + EXECUTABLE
|
||||
+ QLatin1String("' with arguments '") + data.toStringList().join(QLatin1String("', '")) + QLatin1String("'\n");
|
||||
m_recProc->deleteLater();
|
||||
m_isBusy = false;
|
||||
return false;
|
||||
} // if
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#undef EXECUTABLE
|
||||
#undef DISPLAY
|
||||
|
||||
} // namespace ScreenRecording
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Screen Recording worker class for handling processes - Party
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Filip Biedziak, NLE, 2020-06-23
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QRect>
|
||||
|
||||
|
||||
class QProcess;
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace LtsWidgets
|
||||
{
|
||||
class LtsBaseLayoutWidget;
|
||||
}
|
||||
|
||||
namespace LtsSession
|
||||
{
|
||||
class LtsSession;
|
||||
}
|
||||
|
||||
|
||||
namespace ScreenRecording
|
||||
{
|
||||
|
||||
class ScreenRecordingWorker final
|
||||
: public QObject
|
||||
{
|
||||
/** @brief ScreenRecordingWorker manages the screen capturing for both screen recording and
|
||||
* taking a snapshot.
|
||||
*
|
||||
* FFmpeg is used via command line by executing it in a QProcess. Settings like available frame
|
||||
* rates, possible video codecs or screen recording areas are specified.
|
||||
*/
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScreenRecordingWorker(QObject *parent = nullptr);
|
||||
|
||||
enum class FrameRate { VeryLow = 1, Low = 5, Medium = 15, High = 25, VeryHigh = 30};
|
||||
enum class RecordingArea { FullScreen, MainArea, LeftOfMenu };
|
||||
enum class VideoCodec { FFv1, FFvHuff, Huffyuv, Targa, V210, ZLib, H264 };
|
||||
|
||||
static const QString s_recordingTmpDir;
|
||||
static const QString s_screenshotTmpDir;
|
||||
static const QString s_videoFormat;
|
||||
static const QStringList s_imageFormats;
|
||||
|
||||
struct RecordingData
|
||||
{
|
||||
QFileInfo filePath{};
|
||||
VideoCodec videoCodec{VideoCodec::FFv1};
|
||||
QRect recArea{};
|
||||
FrameRate frameRate{FrameRate::Medium};
|
||||
|
||||
QStringList toStringList() const;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC STATIC MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
public:
|
||||
/**
|
||||
* @brief frameRateFromIndexToString generates a string to be displayed e.g. in a QComboBox.
|
||||
* @param idx index of the frame rate in the enum
|
||||
* @return a string to display
|
||||
*/
|
||||
static const QString frameRateFromToString(FrameRate frameRate);
|
||||
/**
|
||||
* @brief videoCodecToString generates a string to be displayed e.g. in a QComboBox
|
||||
* @param videoCodec a video codec
|
||||
* @return a string to display
|
||||
*/
|
||||
static const QString videoCodecToString(VideoCodec videoCodec);
|
||||
/**
|
||||
* @brief recordingAreaToString generates a string to be displayed e.g. in a QComboBox
|
||||
* @param recordingArea a recording area
|
||||
* @return a string to display
|
||||
*/
|
||||
static const QString recordingAreaToString(RecordingArea recordingArea);
|
||||
/**
|
||||
* @brief videoCodecToArg turns the video codec into a valid argument for FFmpeg.
|
||||
* @param videoCodec a video codec
|
||||
* @return the optional argument for FFmpeg associated with the codec
|
||||
*/
|
||||
static const QString videoCodecToArg(VideoCodec videoCodec);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC MEMBER FUNCTIONS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
public:
|
||||
/**
|
||||
* @brief takeScreenshot creates a screenshot of the application.
|
||||
*
|
||||
* Both a file path and a pointer to the base layout widget must be provided. The latter is used
|
||||
* to obtain the geometry of the application. Uses FFmpeg with a frame rate of 1 to take a
|
||||
* screenshot.
|
||||
* @param fileInfo file info with destination file path of the screenshot
|
||||
* @param baseLayout pointer to the base layout
|
||||
* @return true of the screenshot was taken, false otherwise
|
||||
*/
|
||||
bool takeScreenshot(const QFileInfo &fileInfo, LtsWidgets::LtsBaseLayoutWidget *baseLayout);
|
||||
/**
|
||||
* @brief onStopRecording terminates the running recording process.
|
||||
* @return true if process has been stopped, false otherwise
|
||||
*/
|
||||
bool stopRecording();
|
||||
|
||||
inline bool isBusy() const { return m_isBusy; }
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PUBLIC SLOTS
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
public slots:
|
||||
/**
|
||||
* @brief onStartRecording initializes the screen recording using FFmpeg.
|
||||
*
|
||||
* The recording is started in a separate thread to ensure that it is running in the background.
|
||||
* Optional arguments for FFmpeg are taken from the data struct containing the file path, the
|
||||
* video codec, the frame rate and a QRect with the coordinates and dimensions of the recording
|
||||
* area.
|
||||
* @param data struct containing necessary recording information
|
||||
* @return true if the screen is being recorded, false otherwise
|
||||
*/
|
||||
bool onStartRecording(const RecordingData &data);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// PRIVATE MEMBER VARIABLES
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
private:
|
||||
NGL::INGLLogger &m_logger;
|
||||
QProcess *m_recProc{nullptr};
|
||||
bool m_isBusy{false};
|
||||
};
|
||||
|
||||
|
||||
static constexpr std::initializer_list<ScreenRecordingWorker::FrameRate> allFrameRates =
|
||||
{
|
||||
ScreenRecordingWorker::FrameRate::VeryLow,
|
||||
ScreenRecordingWorker::FrameRate::Low,
|
||||
ScreenRecordingWorker::FrameRate::Medium,
|
||||
ScreenRecordingWorker::FrameRate::High,
|
||||
ScreenRecordingWorker::FrameRate::VeryHigh
|
||||
};
|
||||
|
||||
static constexpr std::initializer_list<ScreenRecordingWorker::RecordingArea> allRecordingAreas =
|
||||
{
|
||||
ScreenRecordingWorker::RecordingArea::FullScreen,
|
||||
ScreenRecordingWorker::RecordingArea::MainArea,
|
||||
ScreenRecordingWorker::RecordingArea::LeftOfMenu
|
||||
};
|
||||
|
||||
static constexpr std::initializer_list<ScreenRecordingWorker::VideoCodec> allVideoCodecs =
|
||||
{
|
||||
ScreenRecordingWorker::VideoCodec::H264,
|
||||
// ScreenRecordingWorker::VideoCodec::FFv1,
|
||||
// ScreenRecordingWorker::VideoCodec::FFvHuff,
|
||||
// ScreenRecordingWorker::VideoCodec::Huffyuv,
|
||||
// ScreenRecordingWorker::VideoCodec::Targa,
|
||||
// ScreenRecordingWorker::VideoCodec::V210,
|
||||
// ScreenRecordingWorker::VideoCodec::ZLib
|
||||
};
|
||||
|
||||
} // namespace ScreenRecording
|
||||
|
||||
|
||||
Q_DECLARE_METATYPE(ScreenRecording::ScreenRecordingWorker::RecordingData)
|
||||
Q_DECLARE_METATYPE(ScreenRecording::ScreenRecordingWorker::VideoCodec)
|
||||
Q_DECLARE_METATYPE(ScreenRecording::ScreenRecordingWorker::FrameRate)
|
||||
Q_DECLARE_METATYPE(ScreenRecording::ScreenRecordingWorker::RecordingArea)
|
||||
Reference in new issue
Block a user