diff --git a/.travis.yml b/.travis.yml index d49f311900fc456d7a55f7cb3699f87862fa0372..dcac7c7693a6b2c08b81332a9d9a0f6000cef286 100644 --- a/.travis.yml +++ b/.travis.yml @@ -116,11 +116,23 @@ install: # osx dependencies: qt, gstreamer, gstreamer-devel - if [ "${SPEC}" = "macx-clang" ]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.1-mac-clang-min.tar.bz2 && - tar jxf Qt5.9.1-mac-clang-min.tar.bz2 -C /tmp && + tar jxf Qt5.9.1-mac-clang-min.tar.bz2 -C /tmp + ; + fi + + - if [ "${SPEC}" = "macx-clang" ]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-1.5.2-x86_64.pkg && - sudo installer -verboseR -pkg gstreamer-1.0-1.5.2-x86_64.pkg -target / && + sudo installer -verboseR -pkg gstreamer-1.0-1.5.2-x86_64.pkg -target / + ; + fi + + - if [ "${SPEC}" = "macx-clang" ]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-devel-1.5.2-x86_64.pkg && - sudo installer -verboseR -pkg gstreamer-1.0-devel-1.5.2-x86_64.pkg -target / && + sudo installer -verboseR -pkg gstreamer-1.0-devel-1.5.2-x86_64.pkg -target / + ; + fi + + - if [ "${SPEC}" = "macx-clang" ]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/osx-gstreamer.tar.bz2 && sudo tar jxf osx-gstreamer.tar.bz2 -C /Library/Frameworks && export QT_DIR=Qt5.9-mac-clang/5.9.1/clang_64 && diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 5a743baf9caed70fc9b10fd98611dd23a4dd7f83..8fccc74e16e2f4a7e24dbc7d3ed0c096ec67beef 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -92,6 +92,7 @@ src/QmlControls/QGCPipable.qml src/QmlControls/QGCRadioButton.qml src/QmlControls/QGCSlider.qml + src/QmlControls/QGCSwitch.qml src/QmlControls/QGCTextField.qml src/QmlControls/QGCToolBarButton.qml src/QmlControls/QGCView.qml @@ -179,6 +180,7 @@ src/VehicleSetup/VehicleSummary.qml src/FlightDisplay/VirtualJoystick.qml src/FlightMap/Widgets/CameraPageWidget.qml + src/FlightMap/Widgets/VideoPageWidget.qml src/FlightMap/Widgets/ValuePageWidget.qml src/FlightMap/Widgets/HealthPageWidget.qml src/FlightMap/Widgets/VibrationPageWidget.qml diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 3c87dc13aa576040dca443faee06d60d178dd8a3..e2ff0b2348db9aac725d564c915d67363258ca0c 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -43,8 +43,6 @@ QGCView { property var _geoFenceController: _planMasterController.geoFenceController property var _rallyPointController: _planMasterController.rallyPointController property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property var _videoReceiver: QGroundControl.videoManager.videoReceiver - property bool _recordingVideo: _videoReceiver && _videoReceiver.recording property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, true) : true property bool _isPipVisible: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_PIPVisibleKey, true) : false property real _savedZoomLevel: 0 @@ -346,63 +344,6 @@ QGCView { property var qgcView: root } - // Button to start/stop video recording - Item { - z: _flightVideoPipControl.z + 1 - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.bottom: _flightVideo.bottom - anchors.right: _flightVideo.right - height: ScreenTools.defaultFontPixelHeight * 2 - width: height - visible: _videoReceiver && _videoReceiver.videoRunning && QGroundControl.settingsManager.videoSettings.showRecControl.rawValue && _flightVideo.visible && !_isCamera && !QGroundControl.videoManager.fullScreen - opacity: 0.75 - - readonly property string recordBtnBackground: "BackgroundName" - - Rectangle { - id: recordBtnBackground - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - radius: _recordingVideo ? 0 : height - color: "red" - - SequentialAnimation on visible { - running: _recordingVideo - loops: Animation.Infinite - PropertyAnimation { to: false; duration: 1000 } - PropertyAnimation { to: true; duration: 1000 } - } - } - - QGCColoredImage { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - width: height * 0.625 - sourceSize.width: width - source: "/qmlimages/CameraIcon.svg" - visible: recordBtnBackground.visible - fillMode: Image.PreserveAspectFit - color: "white" - } - - MouseArea { - anchors.fill: parent - onClicked: { - if (_videoReceiver) { - if (_recordingVideo) { - _videoReceiver.stopRecording() - // reset blinking animation - recordBtnBackground.visible = true - } else { - _videoReceiver.startRecording() - } - } - } - } - } - MultiVehicleList { anchors.margins: _margins anchors.top: singleMultiSelector.bottom diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index a3d39fd822ec9bea0fb79623ba108d0956c28810..0ed7eed06c52d2d51be399a794aa5108e788aa5d 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -34,7 +34,7 @@ Item { color: Qt.rgba(0,0,0,0.75) visible: !(_videoReceiver && _videoReceiver.videoRunning) QGCLabel { - text: qsTr("WAITING FOR VIDEO") + text: QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue ? qsTr("WAITING FOR VIDEO") : qsTr("VIDEO DISABLED") font.family: ScreenTools.demiboldFontFamily color: "white" font.pointSize: _mainIsMap ? ScreenTools.smallFontPointSize : ScreenTools.largeFontPointSize diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 4caab475fca7838b6de1b5eaea18c8212f650e5b..7f9751123eda24fa070d565eaa54d6fd398dcc47 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -40,7 +40,7 @@ Item { function getPreferredInstrumentWidth() { if(ScreenTools.isMobile) { - return ScreenTools.isTinyScreen ? mainWindow.width * 0.2 : mainWindow.width * 0.15 + return mainWindow.width * 0.25 } return ScreenTools.defaultFontPixelWidth * 30 } diff --git a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml index 4e2fe1dc7d3711e3b52a5ee00e4c89cd221691e4..290fec81fec381820564febe485d2dfd4644c683 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml @@ -20,7 +20,7 @@ import QGroundControl.Palette 1.0 /// Instrument panel shown when virtual thumbsticks are visible Rectangle { id: root - width: ScreenTools.isTinyScreen ? getPreferredInstrumentWidth() * 1.5 : getPreferredInstrumentWidth() + width: getPreferredInstrumentWidth() height: _outerRadius * 2 radius: _outerRadius color: qgcPal.window diff --git a/src/FlightMap/Widgets/VideoPageWidget.qml b/src/FlightMap/Widgets/VideoPageWidget.qml new file mode 100644 index 0000000000000000000000000000000000000000..10d0e6503e9577ee7784a0fcef6bae1efc2a5258 --- /dev/null +++ b/src/FlightMap/Widgets/VideoPageWidget.qml @@ -0,0 +1,150 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtPositioning 5.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 +import QtGraphicalEffects 1.0 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 + +/// Video streaming page for Instrument Panel PageView +Item { + width: pageWidth + height: videoGrid.height + (ScreenTools.defaultFontPixelHeight * 2) + anchors.margins: ScreenTools.defaultFontPixelWidth * 2 + anchors.centerIn: parent + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _communicationLost: _activeVehicle ? _activeVehicle.connectionLost : false + property var _videoReceiver: QGroundControl.videoManager.videoReceiver + property bool _recordingVideo: _videoReceiver && _videoReceiver.recording + property bool _videoRunning: _videoReceiver && _videoReceiver.videoRunning + property bool _streamingEnabled: QGroundControl.settingsManager.videoSettings.streamConfigured + + QGCPalette { id:qgcPal; colorGroupEnabled: true } + + GridLayout { + id: videoGrid + columns: 2 + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + rowSpacing: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + Connections { + // For some reason, the normal signal is not reflected in the control below + target: QGroundControl.settingsManager.videoSettings.streamEnabled + onRawValueChanged: { + enableSwitch.checked = QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue + } + } + // Enable/Disable Video Streaming + QGCLabel { + text: qsTr("Enable Stream") + font.pointSize: ScreenTools.smallFontPointSize + } + QGCSwitch { + id: enableSwitch + enabled: _streamingEnabled + checked: QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue + onClicked: { + if(checked) { + QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue = 1 + _videoReceiver.start() + } else { + QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue = 0 + _videoReceiver.stop() + } + } + } + // Grid Lines + QGCLabel { + text: qsTr("Grid Lines") + font.pointSize: ScreenTools.smallFontPointSize + visible: QGroundControl.videoManager.isGStreamer && QGroundControl.settingsManager.videoSettings.gridLines.visible + } + QGCSwitch { + enabled: _streamingEnabled && _activeVehicle + checked: QGroundControl.settingsManager.videoSettings.gridLines.rawValue + visible: QGroundControl.videoManager.isGStreamer && QGroundControl.settingsManager.videoSettings.gridLines.visible + onClicked: { + if(checked) { + QGroundControl.settingsManager.videoSettings.gridLines.rawValue = 1 + } else { + QGroundControl.settingsManager.videoSettings.gridLines.rawValue = 0 + } + } + } + QGCLabel { + text: _recordingVideo ? qsTr("Stop Recording") : qsTr("Record Stream") + font.pointSize: ScreenTools.smallFontPointSize + visible: QGroundControl.settingsManager.videoSettings.showRecControl.rawValue + } + // Button to start/stop video recording + Item { + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + height: ScreenTools.defaultFontPixelHeight * 2 + width: height + Layout.alignment: Qt.AlignHCenter + visible: QGroundControl.settingsManager.videoSettings.showRecControl.rawValue + Rectangle { + id: recordBtnBackground + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + radius: _recordingVideo ? 0 : height + color: (_videoRunning && _streamingEnabled) ? "red" : "gray" + SequentialAnimation on opacity { + running: _recordingVideo + loops: Animation.Infinite + PropertyAnimation { to: 0.5; duration: 500 } + PropertyAnimation { to: 1.0; duration: 500 } + } + } + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: height * 0.625 + sourceSize.width: width + source: "/qmlimages/CameraIcon.svg" + visible: recordBtnBackground.visible + fillMode: Image.PreserveAspectFit + color: "white" + } + MouseArea { + anchors.fill: parent + enabled: _videoRunning && _streamingEnabled + onClicked: { + if (_recordingVideo) { + _videoReceiver.stopRecording() + // reset blinking animation + recordBtnBackground.opacity = 1 + } else { + _videoReceiver.startRecording() + } + } + } + } + QGCLabel { + text: qsTr("Video Streaming Not Configured") + font.pointSize: ScreenTools.smallFontPointSize + visible: !_streamingEnabled + Layout.columnSpan: 2 + } + } +} diff --git a/src/QmlControls/PageView.qml b/src/QmlControls/PageView.qml index 80196383c57ca0623df00631fa437cdf9047fc53..6dfdc6cb4c2944bc81a19130e471c8b5a50ea278 100644 --- a/src/QmlControls/PageView.qml +++ b/src/QmlControls/PageView.qml @@ -38,7 +38,7 @@ Rectangle { width: parent.height -(_margins * 2) sourceSize.width: width fillMode: Image.PreserveAspectFit - visible: pageWidgetLoader.item.showSettingsIcon + visible: pageWidgetLoader.item ? (pageWidgetLoader.item.showSettingsIcon ? pageWidgetLoader.item.showSettingsIcon : false) : false QGCMouseArea { fillItem: parent diff --git a/src/QmlControls/QGCSwitch.qml b/src/QmlControls/QGCSwitch.qml new file mode 100644 index 0000000000000000000000000000000000000000..74da42b1306b40dca49bf8d1354fb5a39b589d23 --- /dev/null +++ b/src/QmlControls/QGCSwitch.qml @@ -0,0 +1,30 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.4 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Switch { + id: _root + QGCPalette { id:qgcPal; colorGroupEnabled: true } + style: SwitchStyle { + groove: Rectangle { + implicitWidth: ScreenTools.defaultFontPixelWidth * 6 + implicitHeight: ScreenTools.defaultFontPixelHeight + color: (control.checked && control.enabled) ? qgcPal.colorGreen : qgcPal.colorGrey + radius: 3 + border.color: qgcPal.button + border.width: 1 + } + } +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index ad49c42db7e48ea25142897cd184e6ab7a540404..514aeb14a92752e1b45b73e6da27990a24106432 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -52,6 +52,7 @@ QGCMovableItem 1.0 QGCMovableItem.qml QGCPipable 1.0 QGCPipable.qml QGCRadioButton 1.0 QGCRadioButton.qml QGCSlider 1.0 QGCSlider.qml +QGCSwitch 1.0 QGCSwitch.qml QGCTextField 1.0 QGCTextField.qml QGCToolBarButton 1.0 QGCToolBarButton.qml QGCView 1.0 QGCView.qml diff --git a/src/Settings/Video.SettingsGroup.json b/src/Settings/Video.SettingsGroup.json index 68088d4e68c7fe160f5beed4678f8b7ed10cf0bb..20ee46b381866722d25f8381900954497fad390c 100644 --- a/src/Settings/Video.SettingsGroup.json +++ b/src/Settings/Video.SettingsGroup.json @@ -94,5 +94,19 @@ "min": 1, "units": "s", "defaultValue": 2 +}, +{ + "name": "StreamEnabled", + "shortDescription": "Video Stream Enabled", + "longDescription": "Start/Stop Video Stream.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "DisableWhenDisarmed", + "shortDescription": "Video Stream Disnabled When Armed", + "longDescription": "Disable Video Stream when disarmed.", + "type": "bool", + "defaultValue": false } ] diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index 631a7d14a202390924513fe4984a4e38a6df3759..cb6ab514f1eeca4f5250f42d4d82017caf65d53e 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -30,6 +30,8 @@ const char* VideoSettings::recordingFormatName = "RecordingFormat"; const char* VideoSettings::maxVideoSizeName = "MaxVideoSize"; const char* VideoSettings::enableStorageLimitName = "EnableStorageLimit"; const char* VideoSettings::rtspTimeoutName = "RtspTimeout"; +const char* VideoSettings::streamEnabledName = "StreamEnabled"; +const char* VideoSettings::disableWhenDisarmedName ="DisableWhenDisarmed"; const char* VideoSettings::videoSourceNoVideo = "No Video Available"; const char* VideoSettings::videoDisabled = "Video Stream Disabled"; @@ -50,6 +52,8 @@ VideoSettings::VideoSettings(QObject* parent) , _maxVideoSizeFact(NULL) , _enableStorageLimitFact(NULL) , _rtspTimeoutFact(NULL) + , _streamEnabledFact(NULL) + , _disableWhenDisarmedFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); @@ -94,8 +98,8 @@ Fact* VideoSettings::videoSource(void) { if (!_videoSourceFact) { _videoSourceFact = _createSettingsFact(videoSourceName); + connect(_videoSourceFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _videoSourceFact; } @@ -103,8 +107,8 @@ Fact* VideoSettings::udpPort(void) { if (!_udpPortFact) { _udpPortFact = _createSettingsFact(udpPortName); + connect(_udpPortFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _udpPortFact; } @@ -112,8 +116,8 @@ Fact* VideoSettings::rtspUrl(void) { if (!_rtspUrlFact) { _rtspUrlFact = _createSettingsFact(rtspUrlName); + connect(_rtspUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _rtspUrlFact; } @@ -121,8 +125,8 @@ Fact* VideoSettings::tcpUrl(void) { if (!_tcpUrlFact) { _tcpUrlFact = _createSettingsFact(tcpUrlName); + connect(_tcpUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _tcpUrlFact; } @@ -131,7 +135,6 @@ Fact* VideoSettings::aspectRatio(void) if (!_videoAspectRatioFact) { _videoAspectRatioFact = _createSettingsFact(videoAspectRatioName); } - return _videoAspectRatioFact; } @@ -140,7 +143,6 @@ Fact* VideoSettings::gridLines(void) if (!_gridLinesFact) { _gridLinesFact = _createSettingsFact(videoGridLinesName); } - return _gridLinesFact; } @@ -149,7 +151,6 @@ Fact* VideoSettings::showRecControl(void) if (!_showRecControlFact) { _showRecControlFact = _createSettingsFact(showRecControlName); } - return _showRecControlFact; } @@ -158,7 +159,6 @@ Fact* VideoSettings::recordingFormat(void) if (!_recordingFormatFact) { _recordingFormatFact = _createSettingsFact(recordingFormatName); } - return _recordingFormatFact; } @@ -167,7 +167,6 @@ Fact* VideoSettings::maxVideoSize(void) if (!_maxVideoSizeFact) { _maxVideoSizeFact = _createSettingsFact(maxVideoSizeName); } - return _maxVideoSizeFact; } @@ -176,7 +175,6 @@ Fact* VideoSettings::enableStorageLimit(void) if (!_enableStorageLimitFact) { _enableStorageLimitFact = _createSettingsFact(enableStorageLimitName); } - return _enableStorageLimitFact; } @@ -185,6 +183,50 @@ Fact* VideoSettings::rtspTimeout(void) if (!_rtspTimeoutFact) { _rtspTimeoutFact = _createSettingsFact(rtspTimeoutName); } - return _rtspTimeoutFact; } + +Fact* VideoSettings::streamEnabled(void) +{ + if (!_streamEnabledFact) { + _streamEnabledFact = _createSettingsFact(streamEnabledName); + } + return _streamEnabledFact; +} + +Fact* VideoSettings::disableWhenDisarmed(void) +{ + if (!_disableWhenDisarmedFact) { + _disableWhenDisarmedFact = _createSettingsFact(disableWhenDisarmedName); + } + return _disableWhenDisarmedFact; +} + +bool VideoSettings::streamConfigured(void) +{ +#if !defined(QGC_GST_STREAMING) + return false; +#endif + QString vSource = videoSource()->rawValue().toString(); + if(vSource == videoSourceNoVideo || vSource == videoDisabled) { + return false; + } + //-- If UDP, check if port is set + if(vSource == videoSourceUDP) { + return udpPort()->rawValue().toInt() != 0; + } + //-- If RTSP, check for URL + if(vSource == videoSourceRTSP) { + return !rtspUrl()->rawValue().toString().isEmpty(); + } + //-- If TCP, check for URL + if(vSource == videoSourceTCP) { + return !tcpUrl()->rawValue().toString().isEmpty(); + } + return false; +} + +void VideoSettings::_configChanged(QVariant) +{ + emit streamConfiguredChanged(); +} diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index e05816a1cc561cbd4b05c029cf8b3c1e36ddb062..63b6840d165d5d110b478f0e34608c53f70dec5b 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -19,29 +19,35 @@ class VideoSettings : public SettingsGroup public: VideoSettings(QObject* parent = NULL); - Q_PROPERTY(Fact* videoSource READ videoSource CONSTANT) - Q_PROPERTY(Fact* udpPort READ udpPort CONSTANT) - Q_PROPERTY(Fact* tcpUrl READ tcpUrl CONSTANT) - Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) - Q_PROPERTY(Fact* aspectRatio READ aspectRatio CONSTANT) - Q_PROPERTY(Fact* gridLines READ gridLines CONSTANT) - Q_PROPERTY(Fact* showRecControl READ showRecControl CONSTANT) - Q_PROPERTY(Fact* recordingFormat READ recordingFormat CONSTANT) - Q_PROPERTY(Fact* maxVideoSize READ maxVideoSize CONSTANT) - Q_PROPERTY(Fact* enableStorageLimit READ enableStorageLimit CONSTANT) - Q_PROPERTY(Fact* rtspTimeout READ rtspTimeout CONSTANT) + Q_PROPERTY(Fact* videoSource READ videoSource CONSTANT) + Q_PROPERTY(Fact* udpPort READ udpPort CONSTANT) + Q_PROPERTY(Fact* tcpUrl READ tcpUrl CONSTANT) + Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) + Q_PROPERTY(Fact* aspectRatio READ aspectRatio CONSTANT) + Q_PROPERTY(Fact* gridLines READ gridLines CONSTANT) + Q_PROPERTY(Fact* showRecControl READ showRecControl CONSTANT) + Q_PROPERTY(Fact* recordingFormat READ recordingFormat CONSTANT) + Q_PROPERTY(Fact* maxVideoSize READ maxVideoSize CONSTANT) + Q_PROPERTY(Fact* enableStorageLimit READ enableStorageLimit CONSTANT) + Q_PROPERTY(Fact* rtspTimeout READ rtspTimeout CONSTANT) + Q_PROPERTY(Fact* streamEnabled READ streamEnabled CONSTANT) + Q_PROPERTY(Fact* disableWhenDisarmed READ disableWhenDisarmed CONSTANT) + Q_PROPERTY(bool streamConfigured READ streamConfigured NOTIFY streamConfiguredChanged) - Fact* videoSource (void); - Fact* udpPort (void); - Fact* rtspUrl (void); - Fact* tcpUrl (void); - Fact* aspectRatio (void); - Fact* gridLines (void); - Fact* showRecControl (void); - Fact* recordingFormat (void); - Fact* maxVideoSize (void); - Fact* enableStorageLimit(void); - Fact* rtspTimeout (void); + Fact* videoSource (void); + Fact* udpPort (void); + Fact* rtspUrl (void); + Fact* tcpUrl (void); + Fact* aspectRatio (void); + Fact* gridLines (void); + Fact* showRecControl (void); + Fact* recordingFormat (void); + Fact* maxVideoSize (void); + Fact* enableStorageLimit (void); + Fact* rtspTimeout (void); + Fact* streamEnabled (void); + Fact* disableWhenDisarmed (void); + bool streamConfigured (void); static const char* videoSettingsGroupName; @@ -56,6 +62,8 @@ public: static const char* maxVideoSizeName; static const char* enableStorageLimitName; static const char* rtspTimeoutName; + static const char* streamEnabledName; + static const char* disableWhenDisarmedName; static const char* videoSourceNoVideo; static const char* videoDisabled; @@ -63,6 +71,12 @@ public: static const char* videoSourceRTSP; static const char* videoSourceTCP; +signals: + void streamConfiguredChanged (); + +private slots: + void _configChanged (QVariant value); + private: SettingsFact* _videoSourceFact; SettingsFact* _udpPortFact; @@ -75,6 +89,8 @@ private: SettingsFact* _maxVideoSizeFact; SettingsFact* _enableStorageLimitFact; SettingsFact* _rtspTimeoutFact; + SettingsFact* _streamEnabledFact; + SettingsFact* _disableWhenDisarmedFact; }; #endif diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 422637e5ab223974eaa81e8f4e09802195ee2dff..99b8ba000166ca52cc3cd746410f57b8f64476f2 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -33,6 +33,8 @@ #include "QGCCorePlugin.h" #include "ADSBVehicle.h" #include "QGCCameraManager.h" +#include "VideoReceiver.h" +#include "VideoManager.h" QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog") @@ -1121,6 +1123,11 @@ void Vehicle::_handleHeartbeat(mavlink_message_t& message) _clearCameraTriggerPoints(); } else { _mapTrajectoryStop(); + // Also handle Video Streaming + if(_settingsManager->videoSettings()->disableWhenDisarmed()->rawValue().toBool()) { + _settingsManager->videoSettings()->streamEnabled()->setRawValue(false); + qgcApp()->toolbox()->videoManager()->videoReceiver()->stop(); + } } } diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 5091f215a1bc134c1f65ea02c50dcced9ae1143a..341558bd1f57fb58ead5c6ff7de9e51ad2b88354 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -67,8 +67,10 @@ VideoReceiver::VideoReceiver(QObject* parent) , _videoSurface(NULL) , _videoRunning(false) , _showFullScreen(false) + , _videoSettings(NULL) { - _videoSurface = new VideoSurface; + _videoSurface = new VideoSurface; + _videoSettings = qgcApp()->toolbox()->settingsManager()->videoSettings(); #if defined(QGC_GST_STREAMING) _setVideoSink(_videoSurface->videoSink()); _timer.setSingleShot(true); @@ -147,8 +149,10 @@ VideoReceiver::_connected() _timer.stop(); _socket->deleteLater(); _socket = NULL; - _serverPresent = true; - start(); + if(_videoSettings->streamEnabled()->rawValue().toBool()) { + _serverPresent = true; + start(); + } } #endif @@ -161,7 +165,9 @@ VideoReceiver::_socketError(QAbstractSocket::SocketError socketError) _socket->deleteLater(); _socket = NULL; //-- Try again in 5 seconds - _timer.start(5000); + if(_videoSettings->streamEnabled()->rawValue().toBool()) { + _timer.start(5000); + } } #endif @@ -175,18 +181,19 @@ VideoReceiver::_timeout() delete _socket; _socket = NULL; } - //-- RTSP will try to connect to the server. If it cannot connect, - // it will simply give up and never try again. Instead, we keep - // attempting a connection on this timer. Once a connection is - // found to be working, only then we actually start the stream. - QUrl url(_uri); - _socket = new QTcpSocket; - _socket->setProxy(QNetworkProxy::NoProxy); - connect(_socket, static_cast(&QTcpSocket::error), this, &VideoReceiver::_socketError); - connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected); - //qCDebug(VideoReceiverLog) << "Trying to connect to:" << url.host() << url.port(); - _socket->connectToHost(url.host(), url.port()); - _timer.start(5000); + if(_videoSettings->streamEnabled()->rawValue().toBool()) { + //-- RTSP will try to connect to the server. If it cannot connect, + // it will simply give up and never try again. Instead, we keep + // attempting a connection on this timer. Once a connection is + // found to be working, only then we actually start the stream. + QUrl url(_uri); + _socket = new QTcpSocket; + _socket->setProxy(QNetworkProxy::NoProxy); + connect(_socket, static_cast(&QTcpSocket::error), this, &VideoReceiver::_socketError); + connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected); + _socket->connectToHost(url.host(), url.port()); + _timer.start(5000); + } } #endif @@ -203,6 +210,11 @@ VideoReceiver::_timeout() void VideoReceiver::start() { + if(!_videoSettings->streamEnabled()->rawValue().toBool() || + !_videoSettings->streamConfigured()) { + qCDebug(VideoReceiverLog) << "start() but not enabled/configured"; + return; + } #if defined(QGC_GST_STREAMING) qCDebug(VideoReceiverLog) << "start()"; @@ -550,7 +562,7 @@ void VideoReceiver::_cleanupOldVideos() { //-- Only perform cleanup if storage limit is enabled - if(qgcApp()->toolbox()->settingsManager()->videoSettings()->enableStorageLimit()->rawValue().toBool()) { + 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); @@ -566,7 +578,7 @@ VideoReceiver::_cleanupOldVideos() if(!vidList.isEmpty()) { uint64_t total = 0; //-- Settings are stored using MB - uint64_t maxSize = (qgcApp()->toolbox()->settingsManager()->videoSettings()->maxVideoSize()->rawValue().toUInt() * 1024 * 1024); + 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(); @@ -614,7 +626,7 @@ VideoReceiver::startRecording(void) return; } - uint32_t muxIdx = qgcApp()->toolbox()->settingsManager()->videoSettings()->recordingFormat()->rawValue().toUInt(); + uint32_t muxIdx = _videoSettings->recordingFormat()->rawValue().toUInt(); if(muxIdx >= NUM_MUXES) { qgcApp()->showMessage(tr("Invalid video format defined.")); return; @@ -803,7 +815,7 @@ VideoReceiver::_updateTimer() if(_videoRunning) { uint32_t timeout = 1; if(qgcApp()->toolbox() && qgcApp()->toolbox()->settingsManager()) { - timeout = qgcApp()->toolbox()->settingsManager()->videoSettings()->rtspTimeout()->rawValue().toUInt(); + timeout = _videoSettings->rtspTimeout()->rawValue().toUInt(); } time_t elapsed = 0; time_t lastFrame = _videoSurface->lastFrame(); @@ -814,7 +826,7 @@ VideoReceiver::_updateTimer() stop(); } } else { - if(!running() && !_uri.isEmpty()) { + if(!running() && !_uri.isEmpty() && _videoSettings->streamEnabled()->rawValue().toBool()) { start(); } } diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 7e7cfaf5601d61500e06ac8fc1aaf1b137c24404..f21380a78ec4925894f65d01f6b8b5ec7993858c 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -30,6 +30,8 @@ Q_DECLARE_LOGGING_CATEGORY(VideoReceiverLog) +class VideoSettings; + class VideoReceiver : public QObject { Q_OBJECT @@ -38,9 +40,9 @@ public: Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) #endif Q_PROPERTY(VideoSurface* videoSurface READ videoSurface CONSTANT) - Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) - Q_PROPERTY(QString imageFile READ imageFile NOTIFY imageFileChanged) - Q_PROPERTY(bool showFullScreen READ showFullScreen WRITE setShowFullScreen NOTIFY showFullScreenChanged) + Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) + Q_PROPERTY(QString imageFile READ imageFile NOTIFY imageFileChanged) + Q_PROPERTY(bool showFullScreen READ showFullScreen WRITE setShowFullScreen NOTIFY showFullScreenChanged) explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); @@ -57,6 +59,7 @@ public: bool videoRunning () { return _videoRunning; } QString imageFile () { return _imageFile; } bool showFullScreen () { return _showFullScreen; } + void grabImage (QString imageFile); void setShowFullScreen (bool show) { _showFullScreen = show; emit showFullScreenChanged(); } @@ -136,7 +139,7 @@ private: VideoSurface* _videoSurface; bool _videoRunning; bool _showFullScreen; - + VideoSettings* _videoSettings; }; #endif // VIDEORECEIVER_H diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index de2d76d00ee6a22dde0e3298ee74223b646c46a2..58b06834e82948323797611d4de59f72d147f161 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -39,6 +39,7 @@ public: , defaultOptions (NULL) , valuesPageWidgetInfo (NULL) , cameraPageWidgetInfo (NULL) + , videoPageWidgetInfo (NULL) , healthPageWidgetInfo (NULL) , vibrationPageWidgetInfo (NULL) { @@ -80,6 +81,7 @@ public: QmlComponentInfo* valuesPageWidgetInfo; QmlComponentInfo* cameraPageWidgetInfo; + QmlComponentInfo* videoPageWidgetInfo; QmlComponentInfo* healthPageWidgetInfo; QmlComponentInfo* vibrationPageWidgetInfo; QVariantList instrumentPageWidgetList; @@ -148,13 +150,19 @@ QVariantList &QGCCorePlugin::settingsPages() QVariantList& QGCCorePlugin::instrumentPages(void) { if (!_p->valuesPageWidgetInfo) { - _p->valuesPageWidgetInfo = new QmlComponentInfo(tr("Values"), QUrl::fromUserInput("qrc:/qml/ValuePageWidget.qml")); - _p->cameraPageWidgetInfo = new QmlComponentInfo(tr("Camera"), QUrl::fromUserInput("qrc:/qml/CameraPageWidget.qml")); - _p->healthPageWidgetInfo = new QmlComponentInfo(tr("Health"), QUrl::fromUserInput("qrc:/qml/HealthPageWidget.qml")); + _p->valuesPageWidgetInfo = new QmlComponentInfo(tr("Values"), QUrl::fromUserInput("qrc:/qml/ValuePageWidget.qml")); + _p->cameraPageWidgetInfo = new QmlComponentInfo(tr("Camera"), QUrl::fromUserInput("qrc:/qml/CameraPageWidget.qml")); +#if defined(QGC_GST_STREAMING) + _p->videoPageWidgetInfo = new QmlComponentInfo(tr("Video Stream"), QUrl::fromUserInput("qrc:/qml/VideoPageWidget.qml")); +#endif + _p->healthPageWidgetInfo = new QmlComponentInfo(tr("Health"), QUrl::fromUserInput("qrc:/qml/HealthPageWidget.qml")); _p->vibrationPageWidgetInfo = new QmlComponentInfo(tr("Vibration"), QUrl::fromUserInput("qrc:/qml/VibrationPageWidget.qml")); _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->valuesPageWidgetInfo)); _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->cameraPageWidgetInfo)); +#if defined(QGC_GST_STREAMING) + _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->videoPageWidgetInfo)); +#endif _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->healthPageWidgetInfo)); _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->vibrationPageWidgetInfo)); } diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 4d6c4ae3df6d95ccec2b1dc8c66c955938fe8597..cc853858727291c25c13194bfa825ca344399fcd 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -37,7 +37,7 @@ QGCView { property Fact _appFontPointSize: QGroundControl.settingsManager.appSettings.appFontPointSize property Fact _userBrandImageIndoor: QGroundControl.settingsManager.brandImageSettings.userBrandImageIndoor property Fact _userBrandImageOutdoor: QGroundControl.settingsManager.brandImageSettings.userBrandImageOutdoor - property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 + property real _labelWidth: ScreenTools.defaultFontPixelWidth * 20 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 property Fact _mapProvider: QGroundControl.settingsManager.flightMapSettings.mapProvider property Fact _mapType: QGroundControl.settingsManager.flightMapSettings.mapType @@ -590,13 +590,13 @@ QGCView { spacing: ScreenTools.defaultFontPixelWidth visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex && videoSource.currentIndex < 3 && QGroundControl.settingsManager.videoSettings.gridLines.visible QGCLabel { - text: qsTr("Grid Lines:") + text: qsTr("Disable When Disarmed:") width: _labelWidth anchors.verticalCenter: parent.verticalCenter } - FactComboBox { - width: _editFieldWidth - fact: QGroundControl.settingsManager.videoSettings.gridLines + FactCheckBox { + text: "" + fact: QGroundControl.settingsManager.videoSettings.disableWhenDisarmed anchors.verticalCenter: parent.verticalCenter } }