QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
VideoSettings.cc
Go to the documentation of this file.
1#include "VideoSettings.h"
2#include "VideoManager.h"
3
5#include "QGCNetworkHelper.h"
6#include <QtCore/QSettings>
7#include <QtCore/QVariantList>
8
9QGC_LOGGING_CATEGORY(VideoSettingsLog, "Settings.VideoSettings")
10
11#ifdef QGC_GST_STREAMING
12#include "GStreamer.h"
13static constexpr bool kGstEnabled = true;
14#else
15static constexpr bool kGstEnabled = false;
16#endif
17#include "UVCReceiver.h"
18
19DECLARE_SETTINGGROUP(Video, "Video")
20{
21 // Setup enum values for videoSource settings into meta data
22 QVariantList videoSourceList;
23 videoSourceList.append(videoSourceRTSP);
24 videoSourceList.append(videoSourceUDPH264);
25 videoSourceList.append(videoSourceUDPH265);
26 videoSourceList.append(videoSourceTCP);
27 videoSourceList.append(videoSourceMPEGTS);
28 videoSourceList.append(videoSource3DRSolo);
29 videoSourceList.append(videoSourceParrotDiscovery);
30 videoSourceList.append(videoSourceYuneecMantisG);
31
32#ifdef QGC_HERELINK_AIRUNIT_VIDEO
33 videoSourceList.append(videoSourceHerelinkAirUnit);
34#else
35 videoSourceList.append(videoSourceHerelinkHotspot);
36#endif
37 QStringList uvcDevices = UVCReceiver::getDeviceNameList();
38 for (const QString& device : uvcDevices) {
39 videoSourceList.append(device);
40 }
41 if (videoSourceList.count() == 0) {
42 _noVideo = true;
43 videoSourceList.append(videoSourceNoVideo);
44 setUserVisible(false);
45 } else {
46 videoSourceList.insert(0, videoDisabled);
47 }
48
49 // make translated strings
50 QStringList videoSourceCookedList;
51 for (const QVariant& videoSource: videoSourceList) {
52 videoSourceCookedList.append( VideoSettings::tr(videoSource.toString().toStdString().c_str()) );
53 }
54
55 _nameToMetaDataMap[videoSourceName]->setEnumInfo(videoSourceCookedList, videoSourceList);
56
57 _setForceVideoDecodeList();
58
59 // Migrate legacy gpuZeroCopyEnabled (pre-rename) into the new force-CPU semantics.
60 {
61 QSettings settings;
62 settings.beginGroup(settingsGroup);
63 const bool hasLegacy = settings.contains(QStringLiteral("gpuZeroCopyEnabled"));
64 const bool hasNew = settings.contains(forceCpuVideoPathName);
65 if (hasLegacy) {
66 if (!hasNew) {
67 const bool gpuZeroCopy = settings.value(QStringLiteral("gpuZeroCopyEnabled")).toBool();
68 forceCpuVideoPath()->setRawValue(!gpuZeroCopy);
69 }
70 settings.remove(QStringLiteral("gpuZeroCopyEnabled"));
71 }
72 settings.endGroup();
73 }
74
75 // Set default value for videoSource
76 _setDefaults();
77}
78
79void VideoSettings::_setDefaults()
80{
81 if (_noVideo) {
82 _nameToMetaDataMap[videoSourceName]->setRawDefaultValue(videoSourceNoVideo);
83 } else {
84 _nameToMetaDataMap[videoSourceName]->setRawDefaultValue(videoDisabled);
85 }
86}
87
92DECLARE_SETTINGSFACT(VideoSettings, recordingFormat)
94DECLARE_SETTINGSFACT(VideoSettings, enableStorageLimit)
96DECLARE_SETTINGSFACT(VideoSettings, disableWhenDisarmed)
97
99{
100 if (!_videoSourceFact) {
101 _videoSourceFact = _createSettingsFact(videoSourceName);
102 //-- Check for sources no longer available
103 if(!_videoSourceFact->enumValues().contains(_videoSourceFact->rawValue().toString())) {
104 if (_noVideo) {
105 _videoSourceFact->setRawValue(videoSourceNoVideo);
106 } else {
107 _videoSourceFact->setRawValue(videoDisabled);
108 }
109 }
110 connect(_videoSourceFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
111 }
112 return _videoSourceFact;
113}
114
116{
117 if (!_forceVideoDecoderFact) {
118 _forceVideoDecoderFact = _createSettingsFact(forceVideoDecoderName);
119
120 _forceVideoDecoderFact->setUserVisible(kGstEnabled);
121
122 connect(_forceVideoDecoderFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
123 }
124 return _forceVideoDecoderFact;
125}
126
128{
129 if (!_lowLatencyModeFact) {
130 _lowLatencyModeFact = _createSettingsFact(lowLatencyModeName);
131
132 _lowLatencyModeFact->setUserVisible(kGstEnabled);
133
134 connect(_lowLatencyModeFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
135 }
136 return _lowLatencyModeFact;
137}
138
140{
141 if (!_rtpJitterLatencyMsFact) {
142 _rtpJitterLatencyMsFact = _createSettingsFact(rtpJitterLatencyMsName);
143 _rtpJitterLatencyMsFact->setUserVisible(kGstEnabled);
144 connect(_rtpJitterLatencyMsFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
145 }
146 return _rtpJitterLatencyMsFact;
147}
148
150{
151 if (!_rtspAutoReconnectFact) {
152 _rtspAutoReconnectFact = _createSettingsFact(rtspAutoReconnectName);
153 _rtspAutoReconnectFact->setUserVisible(kGstEnabled);
154 connect(_rtspAutoReconnectFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
155 }
156 return _rtspAutoReconnectFact;
157}
158
160{
161 if (!_forceCpuVideoPathFact) {
162 _forceCpuVideoPathFact = _createSettingsFact(forceCpuVideoPathName);
163
164#if defined(QGC_HAS_ANY_GPU_PATH)
165 _forceCpuVideoPathFact->setUserVisible(kGstEnabled);
166#else
167 _forceCpuVideoPathFact->setUserVisible(false);
168#endif
169 }
170 return _forceCpuVideoPathFact;
171}
172
173// videoConversionElement / disablePixelAspectRatio are read by VideoBackend::createSink()
174// into a VideoSinkConfig and passed as construct-only bin properties — no env-var indirection.
176{
177 if (!_videoConversionElementFact) {
178 _videoConversionElementFact = _createSettingsFact(videoConversionElementName);
179 _videoConversionElementFact->setUserVisible(kGstEnabled);
180 }
181 return _videoConversionElementFact;
182}
183
185{
186 if (!_disablePixelAspectRatioFact) {
187 _disablePixelAspectRatioFact = _createSettingsFact(disablePixelAspectRatioName);
188 _disablePixelAspectRatioFact->setUserVisible(kGstEnabled);
189 }
190 return _disablePixelAspectRatioFact;
191}
192
193
195{
196 if (!_rtspTimeoutFact) {
197 _rtspTimeoutFact = _createSettingsFact(rtspTimeoutName);
198
199 _rtspTimeoutFact->setUserVisible(kGstEnabled);
200
201 connect(_rtspTimeoutFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
202 }
203 return _rtspTimeoutFact;
204}
205
207{
208 if (!_udpUrlFact) {
209 _udpUrlFact = _createSettingsFact(udpUrlName);
210 connect(_udpUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
211 }
212 return _udpUrlFact;
213}
214
216{
217 if (!_rtspUrlFact) {
218 _rtspUrlFact = _createSettingsFact(rtspUrlName);
219 connect(_rtspUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
220 }
221 return _rtspUrlFact;
222}
223
225{
226 if (!_tcpUrlFact) {
227 _tcpUrlFact = _createSettingsFact(tcpUrlName);
228 connect(_tcpUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
229 }
230 return _tcpUrlFact;
231}
232
234{
235 //-- First, check if it's autoconfigured
236 if(VideoManager::instance()->autoStreamConfigured()) {
237 qCDebug(VideoSettingsLog) << "Stream auto configured";
238 return true;
239 }
240 //-- Check if it's disabled
241 QString vSource = videoSource()->rawValue().toString();
242 if(vSource == videoSourceNoVideo || vSource == videoDisabled) {
243 return false;
244 }
245 //-- If UDP, check for URL
246 if(vSource == videoSourceUDPH264 || vSource == videoSourceUDPH265) {
247 const QString url = udpUrl()->rawValue().toString();
248 qCDebug(VideoSettingsLog) << "Testing configuration for UDP Stream:"
250 return !url.isEmpty();
251 }
252 //-- If RTSP, check for URL
253 if(vSource == videoSourceRTSP) {
254 const QString url = rtspUrl()->rawValue().toString();
255 qCDebug(VideoSettingsLog) << "Testing configuration for RTSP Stream:"
257 return !url.isEmpty();
258 }
259 //-- If TCP, check for URL
260 if(vSource == videoSourceTCP) {
261 const QString url = tcpUrl()->rawValue().toString();
262 qCDebug(VideoSettingsLog) << "Testing configuration for TCP Stream:"
264 return !url.isEmpty();
265 }
266 //-- If MPEG-TS, check for URL
267 if(vSource == videoSourceMPEGTS) {
268 const QString url = udpUrl()->rawValue().toString();
269 qCDebug(VideoSettingsLog) << "Testing configuration for MPEG-TS Stream:"
271 return !url.isEmpty();
272 }
273 //-- If Herelink Air unit, good to go
274 if(vSource == videoSourceHerelinkAirUnit) {
275 qCDebug(VideoSettingsLog) << "Stream configured for Herelink Air Unit";
276 return true;
277 }
278 //-- If Herelink Hotspot, good to go
279 if(vSource == videoSourceHerelinkHotspot) {
280 qCDebug(VideoSettingsLog) << "Stream configured for Herelink Hotspot";
281 return true;
282 }
284 qCDebug(VideoSettingsLog) << "Stream configured for UVC";
285 return true;
286 }
287 return false;
288}
289
290void VideoSettings::_configChanged(QVariant)
291{
293}
294
295void VideoSettings::_setForceVideoDecodeList()
296{
297#ifdef QGC_GST_STREAMING
298 static const QList<GStreamer::VideoDecoderOptions> removeForceVideoDecodeList{
299#if defined(Q_OS_ANDROID)
305#elif defined(Q_OS_LINUX)
308#elif defined(Q_OS_WIN)
311#elif defined(Q_OS_MACOS)
314#elif defined(Q_OS_IOS)
319#endif
320 };
321
322 for (const auto &value : removeForceVideoDecodeList) {
323 _nameToMetaDataMap[forceVideoDecoderName]->removeEnumInfo(value);
324 }
325#endif
326}
327
329{
330#ifdef QGC_GST_STREAMING
331 static const QList<GStreamer::VideoDecoderOptions> hardwareFamilies{
338 };
339
340 const QList<GStreamer::VideoDecoderOptions> available = GStreamer::availableDecoderFamilies();
341 const auto metaIt = _nameToMetaDataMap.constFind(forceVideoDecoderName);
342 if (metaIt == _nameToMetaDataMap.constEnd() || !metaIt.value()) {
343 return;
344 }
345 FactMetaData* const metaData = metaIt.value();
346 bool pruned = false;
347 for (const auto family : hardwareFamilies) {
348 // removeEnumInfo() qWarns on an absent value, so skip families not in the enum; values are
349 // stored as QVariant(int), so match that representation.
350 const QVariant familyValue = static_cast<int>(family);
351 if (!available.contains(family) && metaData->enumValues().contains(familyValue)) {
352 metaData->removeEnumInfo(familyValue);
353 pruned = true;
354 }
355 }
356
357 Fact* const fact = forceVideoDecoder();
358 if (pruned) {
359 // Backend init is async — refresh any live FactComboBox bound to this fact.
360 emit fact->enumsChanged();
361 }
362 if (!metaData->enumValues().contains(fact->rawValue())) {
364 }
365#endif
366}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
#define DECLARE_SETTINGSFACT_NO_FUNC(CLASS, NAME)
#define DECLARE_SETTINGSFACT(CLASS, NAME)
#define DECLARE_SETTINGGROUP(NAME, GROUP)
static constexpr bool kGstEnabled
Holds the meta data associated with a Fact.
QVariantList enumValues() const
void removeEnumInfo(const QVariant &value)
Used to remove values from the enum lists after the meta data has been loaded.
A Fact is used to hold a single value within the system.
Definition Fact.h:15
void setRawValue(const QVariant &value)
Definition Fact.cc:135
void enumsChanged()
QVariant rawValue() const
Definition Fact.h:87
void valueChanged(const QVariant &value)
This signal is only meant for use by the QT property system. It should not be connected to by client ...
QMap< QString, FactMetaData * > _nameToMetaDataMap
static bool enabled()
static bool deviceExists(const QString &device)
static QStringList getDeviceNameList()
static VideoManager * instance()
static constexpr const char * videoSourceTCP
static constexpr const char * videoSourceUDPH264
static constexpr const char * videoSourceHerelinkHotspot
bool streamConfigured()
static constexpr const char * videoSourceRTSP
static constexpr const char * videoDisabled
void pruneUnavailableDecoders()
static constexpr const char * videoSourceUDPH265
void streamConfiguredChanged(bool configured)
static constexpr const char * videoSourceNoVideo
static constexpr const char * videoSourceMPEGTS
static constexpr const char * videoSourceHerelinkAirUnit
QList< VideoDecoderOptions > availableDecoderFamilies()
Definition GStreamer.cc:471
@ ForceVideoDecoderDefault
Definition GStreamer.h:19
@ ForceVideoDecoderIntel
Definition GStreamer.h:25
@ ForceVideoDecoderVulkan
Definition GStreamer.h:26
@ ForceVideoDecoderNVIDIA
Definition GStreamer.h:21
@ ForceVideoDecoderVAAPI
Definition GStreamer.h:22
@ ForceVideoDecoderVideoToolbox
Definition GStreamer.h:24
@ ForceVideoDecoderDirectX3D
Definition GStreamer.h:23
QString redactedUrlForLogging(const QUrl &url)