Unverified Commit 53226024 authored by Gus Grubba's avatar Gus Grubba Committed by GitHub

Merge pull request #8455 from tcanabrava/remove_settings_receiver

Remove QGCApplication and VideoSettings from the VideoReceiver
parents 7cb08b79 63623b0f
......@@ -71,7 +71,7 @@ public:
void reportMissingParameter(int componentId, const QString& name);
/// Show a non-modal message to the user
void showMessage(const QString& message);
Q_SLOT void showMessage(const QString& message);
/// @return true: Fake ui into showing mobile interface
bool fakeMobile(void) const { return _fakeMobile; }
......
......@@ -167,5 +167,5 @@ bool VideoSettings::streamConfigured(void)
void VideoSettings::_configChanged(QVariant)
{
emit streamConfiguredChanged();
emit streamConfiguredChanged(streamConfigured());
}
......@@ -60,7 +60,7 @@ public:
static const char* videoSourceMPEGTS;
signals:
void streamConfiguredChanged ();
void streamConfiguredChanged (bool configured);
private slots:
void _configChanged (QVariant value);
......
......@@ -53,6 +53,8 @@ VideoManager::setToolbox(QGCToolbox *toolbox)
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
qmlRegisterUncreatableType<VideoManager> ("QGroundControl.VideoManager", 1, 0, "VideoManager", "Reference only");
qmlRegisterUncreatableType<VideoReceiver>("QGroundControl", 1, 0, "VideoReceiver","Reference only");
// TODO: Those connections should be Per Video, not per VideoManager.
_videoSettings = toolbox->settingsManager()->videoSettings();
QString videoSource = _videoSettings->videoSource()->rawValue().toString();
connect(_videoSettings->videoSource(), &Fact::rawValueChanged, this, &VideoManager::_videoSourceChanged);
......@@ -72,7 +74,56 @@ VideoManager::setToolbox(QGCToolbox *toolbox)
emit isGStreamerChanged();
qCDebug(VideoManagerLog) << "New Video Source:" << videoSource;
_videoReceiver = toolbox->corePlugin()->createVideoReceiver(this);
_videoReceiver->setUnittestMode(qgcApp()->runningUnitTests());
_thermalVideoReceiver = toolbox->corePlugin()->createVideoReceiver(this);
_thermalVideoReceiver->setUnittestMode(qgcApp()->runningUnitTests());
_videoReceiver->moveToThread(qgcApp()->thread());
_thermalVideoReceiver->moveToThread(qgcApp()->thread());
// Those connects are temporary: In a perfect world those connections are going to be done on the Qml
// but because currently the videoReceiver is created in the C++ world, this is easier.
// The fact returning a QVariant is a quite annoying to use proper signal / slot connection.
_updateSettings();
auto appSettings = toolbox->settingsManager()->appSettings();
for (auto *videoReceiver : { _videoReceiver, _thermalVideoReceiver}) {
// First, Setup the current values from the settings.
videoReceiver->setRtspTimeout(_videoSettings->rtspTimeout()->rawValue().toInt());
videoReceiver->setStreamEnabled(_videoSettings->streamEnabled()->rawValue().toBool());
videoReceiver->setRecordingFormatId(_videoSettings->recordingFormat()->rawValue().toInt());
videoReceiver->setStreamConfigured(_videoSettings->streamConfigured());
connect(_videoSettings->rtspTimeout(), &Fact::rawValueChanged,
videoReceiver, [videoReceiver](const QVariant &value) {
videoReceiver->setRtspTimeout(value.toInt());
}
);
connect(_videoSettings->streamEnabled(), &Fact::rawValueChanged,
videoReceiver, [videoReceiver](const QVariant &value) {
videoReceiver->setStreamEnabled(value.toBool());
}
);
connect(_videoSettings->recordingFormat(), &Fact::rawValueChanged,
videoReceiver, [videoReceiver](const QVariant &value) {
videoReceiver->setRecordingFormatId(value.toInt());
}
);
// Why some options are facts while others aren't?
connect(_videoSettings, &VideoSettings::streamConfiguredChanged, videoReceiver, &VideoReceiver::setStreamConfigured);
// Fix those.
// connect(appSettings, &Fact::rawValueChanged, videoReceiver, &VideoReceiver::setVideoPath);
// connect(appSettings->videoSavePath(), &Fact::rawValueChanged, videoReceiver, &VideoReceiver::setImagePath);
// Connect the video receiver with the rest of the app.
connect(videoReceiver, &VideoReceiver::restartTimeout, this, &VideoManager::restartVideo);
connect(videoReceiver, &VideoReceiver::sendMessage, qgcApp(), &QGCApplication::showMessage);
connect(videoReceiver, &VideoReceiver::beforeRecording, this, &VideoManager::cleanupOldVideos);
}
_updateSettings();
if(isGStreamer()) {
startVideo();
......@@ -84,6 +135,55 @@ VideoManager::setToolbox(QGCToolbox *toolbox)
#endif
}
QStringList VideoManager::videoMuxes()
{
return {"matroskamux", "qtmux", "mp4mux"};
}
QStringList VideoManager::videoExtensions()
{
return {"mkv", "mov", "mp4"};
}
void VideoManager::cleanupOldVideos()
{
#if defined(QGC_GST_STREAMING)
//-- Only perform cleanup if storage limit is enabled
if(!_videoSettings->enableStorageLimit()->rawValue().toBool()) {
return;
}
QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath();
QDir videoDir = QDir(savePath);
videoDir.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable);
videoDir.setSorting(QDir::Time);
QStringList nameFilters;
for(const QString& extension : videoExtensions()) {
nameFilters << QString("*.") + extension;
}
videoDir.setNameFilters(nameFilters);
//-- get the list of videos stored
QFileInfoList vidList = videoDir.entryInfoList();
if(!vidList.isEmpty()) {
uint64_t total = 0;
//-- Settings are stored using MB
uint64_t maxSize = _videoSettings->maxVideoSize()->rawValue().toUInt() * 1024 * 1024;
//-- Compute total used storage
for(int i = 0; i < vidList.size(); i++) {
total += vidList[i].size();
}
//-- Remove old movies until max size is satisfied.
while(total >= maxSize && !vidList.isEmpty()) {
total -= vidList.last().size();
qCDebug(VideoReceiverLog) << "Removing old video file:" << vidList.last().filePath();
QFile file (vidList.last().filePath());
file.remove();
vidList.removeLast();
}
}
#endif
}
//-----------------------------------------------------------------------------
void
VideoManager::startVideo()
......
......@@ -67,6 +67,9 @@ public:
virtual VideoReceiver* videoReceiver () { return _videoReceiver; }
virtual VideoReceiver* thermalVideoReceiver () { return _thermalVideoReceiver; }
QStringList videoExtensions();
QStringList videoMuxes();
#if defined(QGC_DISABLE_UVC)
virtual bool uvcEnabled () { return false; }
#else
......@@ -82,6 +85,8 @@ public:
Q_INVOKABLE void startVideo ();
Q_INVOKABLE void stopVideo ();
void cleanupOldVideos();
signals:
void hasVideoChanged ();
void isGStreamerChanged ();
......
......@@ -15,8 +15,6 @@
*/
#include "VideoReceiver.h"
#include "SettingsManager.h"
#include "QGCApplication.h"
#include "VideoManager.h"
#ifdef QGC_GST_TAISYNC_ENABLED
#include "TaisyncHandler.h"
......@@ -70,14 +68,15 @@ VideoReceiver::VideoReceiver(QObject* parent)
#endif
, _videoRunning(false)
, _showFullScreen(false)
, _videoSettings(nullptr)
, _streamEnabled(false)
, _streamConfigured(false)
, _unittTestMode(false)
, _isTaisync(false)
{
// FIXME: AV: temporal workaround to allow for Qt::QueuedConnection for gstreamer signals. Need to evaluate proper solution - perhaps QtGst will be helpful
moveToThread(qgcApp()->thread());
_videoSettings = qgcApp()->toolbox()->settingsManager()->videoSettings();
#if defined(QGC_GST_STREAMING)
_restart_timer.setSingleShot(true);
connect(&_restart_timer, &QTimer::timeout, this, &VideoReceiver::_restart_timeout);
connect(&_restart_timer, &QTimer::timeout, this, &VideoReceiver::restartTimeout);
connect(this, &VideoReceiver::msgErrorReceived, this, &VideoReceiver::_handleError, Qt::QueuedConnection);
connect(this, &VideoReceiver::msgEOSReceived, this, &VideoReceiver::_handleEOS, Qt::QueuedConnection);
connect(this, &VideoReceiver::msgStateChangedReceived, this, &VideoReceiver::_handleStateChanged, Qt::QueuedConnection);
......@@ -433,12 +432,102 @@ VideoReceiver::_makeSource(const QString& uri)
return srcbin;
}
//-----------------------------------------------------------------------------
void
VideoReceiver::_restart_timeout()
bool VideoReceiver::streamEnabled() const
{
return _streamEnabled;
}
void VideoReceiver::setStreamEnabled(bool enabled)
{
if (_streamEnabled != enabled) {
_streamEnabled = enabled;
emit streamEnabledChanged();
}
}
bool VideoReceiver::streamConfigured() const
{
return _streamConfigured;
}
void VideoReceiver::setStreamConfigured(bool enabled)
{
if (_streamConfigured != enabled) {
_streamConfigured = enabled;
emit streamEnabledChanged();
}
}
bool VideoReceiver::isTaisync() const
{
return _isTaisync;
}
void VideoReceiver::setIsTaysinc(bool enabled)
{
if (_isTaisync != enabled) {
_isTaisync = enabled;
emit isTaisyncChanged();
}
}
QString VideoReceiver::videoPath() const
{
return _videoPath;
}
void VideoReceiver::setVideoPath(const QString& value)
{
qgcApp()->toolbox()->videoManager()->restartVideo();
if (_videoPath != value) {
_videoPath = value;
emit videoPathChanged();
}
}
QString VideoReceiver::imagePath() const
{
return _imagePath;
}
void VideoReceiver::setImagePath(const QString& value)
{
if (_imagePath != value) {
_imagePath = value;
emit imagePathChanged();
}
}
int VideoReceiver::recordingFormatId() const
{
return _recordingFormatId;
}
void VideoReceiver::setRecordingFormatId(int value)
{
if (_recordingFormatId != value && value < (int) NUM_MUXES) {
_recordingFormatId = value;
emit recordingFormatIdChanged();
}
}
int VideoReceiver::rtspTimeout() const
{
return _rtspTimeout;
}
void VideoReceiver::setRtspTimeout(int value)
{
if (_rtspTimeout != value) {
_rtspTimeout = value;
emit rtspTimeoutChanged();
}
}
void VideoReceiver::setUnittestMode(bool runUnitTests)
{
_unittTestMode = runUnitTests;
}
#endif
//-----------------------------------------------------------------------------
......@@ -454,11 +543,10 @@ void
VideoReceiver::start()
{
qCDebug(VideoReceiverLog) << "Starting " << _uri;
if(qgcApp()->runningUnitTests()) {
if(_unittTestMode) {
return;
}
if(!_videoSettings->streamEnabled()->rawValue().toBool() ||
!_videoSettings->streamConfigured()) {
if(!_streamEnabled || !_streamConfigured) {
qCDebug(VideoReceiverLog) << "Stream not enabled/configured";
return;
}
......@@ -470,7 +558,7 @@ VideoReceiver::start()
#if defined(QGC_GST_TAISYNC_ENABLED) && (defined(__android__) || defined(__ios__))
//-- Taisync on iOS or Android sends a raw h.264 stream
if (qgcApp()->toolbox()->videoManager()->isTaisync()) {
if (_isTaisync) {
uri = QString("tsusb://0.0.0.0:%1").arg(TAISYNC_VIDEO_UDP_PORT);
}
#endif
......@@ -609,7 +697,7 @@ VideoReceiver::start()
void
VideoReceiver::stop()
{
if(qgcApp() && qgcApp()->runningUnitTests()) {
if(_unittTestMode) {
return;
}
#if defined(QGC_GST_STREAMING)
......@@ -762,46 +850,6 @@ VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer data)
}
#endif
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
void
VideoReceiver::_cleanupOldVideos()
{
//-- Only perform cleanup if storage limit is enabled
if(_videoSettings->enableStorageLimit()->rawValue().toBool()) {
QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath();
QDir videoDir = QDir(savePath);
videoDir.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable);
videoDir.setSorting(QDir::Time);
//-- All the movie extensions we support
QStringList nameFilters;
for(uint32_t i = 0; i < NUM_MUXES; i++) {
nameFilters << QString("*.") + QString(kVideoExtensions[i]);
}
videoDir.setNameFilters(nameFilters);
//-- get the list of videos stored
QFileInfoList vidList = videoDir.entryInfoList();
if(!vidList.isEmpty()) {
uint64_t total = 0;
//-- Settings are stored using MB
uint64_t maxSize = (_videoSettings->maxVideoSize()->rawValue().toUInt() * 1024 * 1024);
//-- Compute total used storage
for(int i = 0; i < vidList.size(); i++) {
total += vidList[i].size();
}
//-- Remove old movies until max size is satisfied.
while(total >= maxSize && !vidList.isEmpty()) {
total -= vidList.last().size();
qCDebug(VideoReceiverLog) << "Removing old video file:" << vidList.last().filePath();
QFile file (vidList.last().filePath());
file.remove();
vidList.removeLast();
}
}
}
}
#endif
//-----------------------------------------------------------------------------
#if defined(QGC_GST_STREAMING)
void
......@@ -934,6 +982,7 @@ void
VideoReceiver::startRecording(const QString &videoFile)
{
#if defined(QGC_GST_STREAMING)
emit beforeRecording();
qCDebug(VideoReceiverLog) << "Starting recording";
// exit immediately if we are already recording
......@@ -942,18 +991,15 @@ VideoReceiver::startRecording(const QString &videoFile)
return;
}
uint32_t muxIdx = _videoSettings->recordingFormat()->rawValue().toUInt();
uint32_t muxIdx = _recordingFormatId;
if(muxIdx >= NUM_MUXES) {
qgcApp()->showMessage(tr("Invalid video format defined."));
emit sendMessage(tr("Invalid video format defined."));
return;
}
//-- Disk usage maintenance
_cleanupOldVideos();
QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath();
QString savePath = _videoPath;
if(savePath.isEmpty()) {
qgcApp()->showMessage(tr("Unabled to record video. Video save path must be specified in Settings."));
emit sendMessage(tr("Unabled to record video. Video save path must be specified in Settings."));
return;
}
......@@ -1175,11 +1221,7 @@ VideoReceiver::_updateTimer()
}
if(_videoRunning) {
uint32_t timeout = 1;
if(qgcApp()->toolbox() && qgcApp()->toolbox()->settingsManager()) {
timeout = _videoSettings->rtspTimeout()->rawValue().toUInt();
}
uint32_t timeout = _rtspTimeout;
const qint64 now = QDateTime::currentSecsSinceEpoch();
if(now - _lastFrameTime > timeout) {
......@@ -1189,7 +1231,7 @@ VideoReceiver::_updateTimer()
}
} else {
// FIXME: AV: if pipeline is _running but not _streaming for some time then we need to restart
if(!_stop && !_running && !_uri.isEmpty() && _videoSettings->streamEnabled()->rawValue().toBool()) {
if(!_stop && !_running && !_uri.isEmpty() && _streamEnabled) {
start();
}
}
......
......@@ -39,11 +39,54 @@ public:
Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged)
Q_PROPERTY(QString imageFile READ imageFile NOTIFY imageFileChanged)
Q_PROPERTY(QString videoFile READ videoFile NOTIFY videoFileChanged)
Q_PROPERTY(QString imagePath READ imagePath NOTIFY imagePathChanged)
Q_PROPERTY(QString videoPath READ videoPath NOTIFY videoPathChanged)
Q_PROPERTY(bool showFullScreen READ showFullScreen WRITE setShowFullScreen NOTIFY showFullScreenChanged)
Q_PROPERTY(bool streamEnabled READ streamEnabled WRITE setStreamEnabled NOTIFY streamEnabledChanged)
Q_PROPERTY(bool streamConfigured READ streamConfigured WRITE setStreamConfigured NOTIFY streamConfiguredChanged)
Q_PROPERTY(bool isTaisync READ isTaisync WRITE setIsTaysinc NOTIFY isTaisyncChanged)
Q_PROPERTY(int recordingFormatId READ recordingFormatId WRITE setRecordingFormatId NOTIFY recordingFormatIdChanged)
Q_PROPERTY(int rtspTimeout READ rtspTimeout WRITE setRtspTimeout NOTIFY rtspTimeoutChanged)
explicit VideoReceiver(QObject* parent = nullptr);
~VideoReceiver();
bool streamEnabled() const;
Q_SLOT void setStreamEnabled(bool enabled);
Q_SIGNAL void streamEnabledChanged();
bool streamConfigured() const;
Q_SLOT void setStreamConfigured(bool enabled);
Q_SIGNAL void streamConfiguredChanged();
bool isTaisync() const;
Q_SLOT void setIsTaysinc(bool value);
Q_SIGNAL void isTaisyncChanged();
QString videoPath() const;
Q_SLOT void setVideoPath(const QString& path);
Q_SIGNAL void videoPathChanged();
QString imagePath() const;
Q_SLOT void setImagePath(const QString& path);
Q_SIGNAL void imagePathChanged();
int recordingFormatId() const;
Q_SLOT void setRecordingFormatId(int value);
Q_SIGNAL void recordingFormatIdChanged();
int rtspTimeout() const;
Q_SLOT void setRtspTimeout(int value);
Q_SIGNAL void rtspTimeoutChanged();
Q_SIGNAL void restartTimeout();
Q_SIGNAL void sendMessage(const QString& message);
// Emitted before recording starts.
Q_SIGNAL void beforeRecording();
void setUnittestMode(bool runUnitTests);
#if defined(QGC_GST_STREAMING)
virtual bool recording () { return _recording; }
#endif
......@@ -86,7 +129,6 @@ protected slots:
#if defined(QGC_GST_STREAMING)
GstElement* _makeSource (const QString& uri);
GstElement* _makeFileSink (const QString& videoFile, unsigned format);
virtual void _restart_timeout ();
virtual void _handleError ();
virtual void _handleEOS ();
virtual void _handleStateChanged ();
......@@ -122,7 +164,6 @@ protected:
virtual void _unlinkRecordingBranch (GstPadProbeInfo* info);
virtual void _shutdownRecordingBranch();
virtual void _shutdownPipeline ();
virtual void _cleanupOldVideos ();
GstElement* _pipeline;
GstElement* _videoSink;
......@@ -141,8 +182,18 @@ protected:
QString _uri;
QString _imageFile;
QString _videoFile;
QString _videoPath;
QString _imagePath;
bool _videoRunning;
bool _showFullScreen;
VideoSettings* _videoSettings;
bool _streamEnabled;
bool _streamConfigured;
bool _storageLimit;
bool _unittTestMode;
bool _isTaisync;
int _recordingFormatId; // 0 - 2, defined in VideoReceiver.cc / kVideoExtensions. TODO: use a better representation.
int _rtspTimeout;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment