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 <QtCore/QSettings>
6#include <QtCore/QVariantList>
7
8QGC_LOGGING_CATEGORY(VideoSettingsLog, "Settings.VideoSettings")
9
10#ifdef QGC_GST_STREAMING
11#include "GStreamer.h"
12static constexpr bool kGstEnabled = true;
13#else
14static constexpr bool kGstEnabled = false;
15#endif
16#include "UVCReceiver.h"
17
18DECLARE_SETTINGGROUP(Video, "Video")
19{
20 // Setup enum values for videoSource settings into meta data
21 QVariantList videoSourceList;
22 videoSourceList.append(videoSourceRTSP);
23 videoSourceList.append(videoSourceUDPH264);
24 videoSourceList.append(videoSourceUDPH265);
25 videoSourceList.append(videoSourceTCP);
26 videoSourceList.append(videoSourceMPEGTS);
27 videoSourceList.append(videoSource3DRSolo);
28 videoSourceList.append(videoSourceParrotDiscovery);
29 videoSourceList.append(videoSourceYuneecMantisG);
30
31#ifdef QGC_HERELINK_AIRUNIT_VIDEO
32 videoSourceList.append(videoSourceHerelinkAirUnit);
33#else
34 videoSourceList.append(videoSourceHerelinkHotspot);
35#endif
36 QStringList uvcDevices = UVCReceiver::getDeviceNameList();
37 for (const QString& device : uvcDevices) {
38 videoSourceList.append(device);
39 }
40 if (videoSourceList.count() == 0) {
41 _noVideo = true;
42 videoSourceList.append(videoSourceNoVideo);
43 setUserVisible(false);
44 } else {
45 videoSourceList.insert(0, videoDisabled);
46 }
47
48 // make translated strings
49 QStringList videoSourceCookedList;
50 for (const QVariant& videoSource: videoSourceList) {
51 videoSourceCookedList.append( VideoSettings::tr(videoSource.toString().toStdString().c_str()) );
52 }
53
54 _nameToMetaDataMap[videoSourceName]->setEnumInfo(videoSourceCookedList, videoSourceList);
55
56 _setForceVideoDecodeList();
57
58 // Migrate legacy gpuZeroCopyEnabled (pre-rename) into the new force-CPU semantics.
59 {
60 QSettings settings;
61 settings.beginGroup(settingsGroup);
62 const bool hasLegacy = settings.contains(QStringLiteral("gpuZeroCopyEnabled"));
63 const bool hasNew = settings.contains(forceCpuVideoPathName);
64 if (hasLegacy) {
65 if (!hasNew) {
66 const bool gpuZeroCopy = settings.value(QStringLiteral("gpuZeroCopyEnabled")).toBool();
67 forceCpuVideoPath()->setRawValue(!gpuZeroCopy);
68 }
69 settings.remove(QStringLiteral("gpuZeroCopyEnabled"));
70 }
71 settings.endGroup();
72 }
73
74 // Set default value for videoSource
75 _setDefaults();
76}
77
78void VideoSettings::_setDefaults()
79{
80 if (_noVideo) {
81 _nameToMetaDataMap[videoSourceName]->setRawDefaultValue(videoSourceNoVideo);
82 } else {
83 _nameToMetaDataMap[videoSourceName]->setRawDefaultValue(videoDisabled);
84 }
85}
86
91DECLARE_SETTINGSFACT(VideoSettings, recordingFormat)
93DECLARE_SETTINGSFACT(VideoSettings, enableStorageLimit)
95DECLARE_SETTINGSFACT(VideoSettings, disableWhenDisarmed)
96
98{
99 if (!_videoSourceFact) {
100 _videoSourceFact = _createSettingsFact(videoSourceName);
101 //-- Check for sources no longer available
102 if(!_videoSourceFact->enumValues().contains(_videoSourceFact->rawValue().toString())) {
103 if (_noVideo) {
104 _videoSourceFact->setRawValue(videoSourceNoVideo);
105 } else {
106 _videoSourceFact->setRawValue(videoDisabled);
107 }
108 }
109 connect(_videoSourceFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
110 }
111 return _videoSourceFact;
112}
113
115{
116 if (!_forceVideoDecoderFact) {
117 _forceVideoDecoderFact = _createSettingsFact(forceVideoDecoderName);
118
119 _forceVideoDecoderFact->setUserVisible(kGstEnabled);
120
121 connect(_forceVideoDecoderFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
122 }
123 return _forceVideoDecoderFact;
124}
125
127{
128 if (!_lowLatencyModeFact) {
129 _lowLatencyModeFact = _createSettingsFact(lowLatencyModeName);
130
131 _lowLatencyModeFact->setUserVisible(kGstEnabled);
132
133 connect(_lowLatencyModeFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
134 }
135 return _lowLatencyModeFact;
136}
137
139{
140 if (!_rtpJitterLatencyMsFact) {
141 _rtpJitterLatencyMsFact = _createSettingsFact(rtpJitterLatencyMsName);
142 _rtpJitterLatencyMsFact->setUserVisible(kGstEnabled);
143 connect(_rtpJitterLatencyMsFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
144 }
145 return _rtpJitterLatencyMsFact;
146}
147
149{
150 if (!_rtspAutoReconnectFact) {
151 _rtspAutoReconnectFact = _createSettingsFact(rtspAutoReconnectName);
152 _rtspAutoReconnectFact->setUserVisible(kGstEnabled);
153 connect(_rtspAutoReconnectFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
154 }
155 return _rtspAutoReconnectFact;
156}
157
159{
160 if (!_forceCpuVideoPathFact) {
161 _forceCpuVideoPathFact = _createSettingsFact(forceCpuVideoPathName);
162
163#if defined(QGC_HAS_ANY_GPU_PATH)
164 _forceCpuVideoPathFact->setUserVisible(kGstEnabled);
165#else
166 _forceCpuVideoPathFact->setUserVisible(false);
167#endif
168 }
169 return _forceCpuVideoPathFact;
170}
171
172// videoConversionElement / disablePixelAspectRatio are read by VideoBackend::createSink()
173// into a VideoSinkConfig and passed as construct-only bin properties — no env-var indirection.
175{
176 if (!_videoConversionElementFact) {
177 _videoConversionElementFact = _createSettingsFact(videoConversionElementName);
178 _videoConversionElementFact->setUserVisible(kGstEnabled);
179 }
180 return _videoConversionElementFact;
181}
182
184{
185 if (!_disablePixelAspectRatioFact) {
186 _disablePixelAspectRatioFact = _createSettingsFact(disablePixelAspectRatioName);
187 _disablePixelAspectRatioFact->setUserVisible(kGstEnabled);
188 }
189 return _disablePixelAspectRatioFact;
190}
191
192
194{
195 if (!_rtspTimeoutFact) {
196 _rtspTimeoutFact = _createSettingsFact(rtspTimeoutName);
197
198 _rtspTimeoutFact->setUserVisible(kGstEnabled);
199
200 connect(_rtspTimeoutFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
201 }
202 return _rtspTimeoutFact;
203}
204
206{
207 if (!_udpUrlFact) {
208 _udpUrlFact = _createSettingsFact(udpUrlName);
209 connect(_udpUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
210 }
211 return _udpUrlFact;
212}
213
215{
216 if (!_rtspUrlFact) {
217 _rtspUrlFact = _createSettingsFact(rtspUrlName);
218 connect(_rtspUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
219 }
220 return _rtspUrlFact;
221}
222
224{
225 if (!_tcpUrlFact) {
226 _tcpUrlFact = _createSettingsFact(tcpUrlName);
227 connect(_tcpUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged);
228 }
229 return _tcpUrlFact;
230}
231
233{
234 //-- First, check if it's autoconfigured
235 if(VideoManager::instance()->autoStreamConfigured()) {
236 qCDebug(VideoSettingsLog) << "Stream auto configured";
237 return true;
238 }
239 //-- Check if it's disabled
240 QString vSource = videoSource()->rawValue().toString();
241 if(vSource == videoSourceNoVideo || vSource == videoDisabled) {
242 return false;
243 }
244 //-- If UDP, check for URL
245 if(vSource == videoSourceUDPH264 || vSource == videoSourceUDPH265) {
246 qCDebug(VideoSettingsLog) << "Testing configuration for UDP Stream:" << udpUrl()->rawValue().toString();
247 return !udpUrl()->rawValue().toString().isEmpty();
248 }
249 //-- If RTSP, check for URL
250 if(vSource == videoSourceRTSP) {
251 qCDebug(VideoSettingsLog) << "Testing configuration for RTSP Stream:" << rtspUrl()->rawValue().toString();
252 return !rtspUrl()->rawValue().toString().isEmpty();
253 }
254 //-- If TCP, check for URL
255 if(vSource == videoSourceTCP) {
256 qCDebug(VideoSettingsLog) << "Testing configuration for TCP Stream:" << tcpUrl()->rawValue().toString();
257 return !tcpUrl()->rawValue().toString().isEmpty();
258 }
259 //-- If MPEG-TS, check for URL
260 if(vSource == videoSourceMPEGTS) {
261 qCDebug(VideoSettingsLog) << "Testing configuration for MPEG-TS Stream:" << udpUrl()->rawValue().toString();
262 return !udpUrl()->rawValue().toString().isEmpty();
263 }
264 //-- If Herelink Air unit, good to go
265 if(vSource == videoSourceHerelinkAirUnit) {
266 qCDebug(VideoSettingsLog) << "Stream configured for Herelink Air Unit";
267 return true;
268 }
269 //-- If Herelink Hotspot, good to go
270 if(vSource == videoSourceHerelinkHotspot) {
271 qCDebug(VideoSettingsLog) << "Stream configured for Herelink Hotspot";
272 return true;
273 }
275 qCDebug(VideoSettingsLog) << "Stream configured for UVC";
276 return true;
277 }
278 return false;
279}
280
281void VideoSettings::_configChanged(QVariant)
282{
284}
285
286void VideoSettings::_setForceVideoDecodeList()
287{
288#ifdef QGC_GST_STREAMING
289 static const QList<GStreamer::VideoDecoderOptions> removeForceVideoDecodeList{
290#if defined(Q_OS_ANDROID)
296#elif defined(Q_OS_LINUX)
299#elif defined(Q_OS_WIN)
302#elif defined(Q_OS_MACOS)
305#elif defined(Q_OS_IOS)
310#endif
311 };
312
313 for (const auto &value : removeForceVideoDecodeList) {
314 _nameToMetaDataMap[forceVideoDecoderName]->removeEnumInfo(value);
315 }
316#endif
317}
318
320{
321#ifdef QGC_GST_STREAMING
322 static const QList<GStreamer::VideoDecoderOptions> hardwareFamilies{
329 };
330
331 const QList<GStreamer::VideoDecoderOptions> available = GStreamer::availableDecoderFamilies();
332 const auto metaIt = _nameToMetaDataMap.constFind(forceVideoDecoderName);
333 if (metaIt == _nameToMetaDataMap.constEnd() || !metaIt.value()) {
334 return;
335 }
336 FactMetaData* const metaData = metaIt.value();
337 bool pruned = false;
338 for (const auto family : hardwareFamilies) {
339 // removeEnumInfo() qWarns on an absent value, so skip families not in the enum; values are
340 // stored as QVariant(int), so match that representation.
341 const QVariant familyValue = static_cast<int>(family);
342 if (!available.contains(family) && metaData->enumValues().contains(familyValue)) {
343 metaData->removeEnumInfo(familyValue);
344 pruned = true;
345 }
346 }
347
348 Fact* const fact = forceVideoDecoder();
349 if (pruned) {
350 // Backend init is async — refresh any live FactComboBox bound to this fact.
351 emit fact->enumsChanged();
352 }
353 if (!metaData->enumValues().contains(fact->rawValue())) {
355 }
356#endif
357}
#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:17
void setRawValue(const QVariant &value)
Definition Fact.cc:134
void enumsChanged()
QVariant rawValue() const
Value after translation.
Definition Fact.h:85
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