14#ifdef QGC_GST_STREAMING
23#include <QtCore/QApplicationStatic>
25#include <QtQml/QQmlEngine>
26#include <QtQuick/QQuickItem>
27#include <QtQuick/QQuickWindow>
28#include <QtCore/QTimer>
40VideoManager::VideoManager(QObject *parent)
45 qCDebug(VideoManagerLog) <<
this;
47 (void) qRegisterMetaType<VideoReceiver::STATUS>(
"STATUS");
49#ifdef QGC_GST_STREAMING
50 const bool skipGStreamerForUnitTests =
51 qgcApp() &&
qgcApp()->runningUnitTests() && !qEnvironmentVariableIsSet(
"QGC_TEST_ENABLE_GSTREAMER");
53 if (skipGStreamerForUnitTests) {
54 (void) qmlRegisterType<VideoItemStub>(
"org.freedesktop.gstreamer.Qt6GLVideoItem", 1, 0,
"GstGLQt6VideoItem");
55 qCInfo(VideoManagerLog) <<
"Skipping GStreamer initialization for unit tests";
57 qCCritical(VideoManagerLog) <<
"Failed To Initialize GStreamer";
60 (void) qmlRegisterType<VideoItemStub>(
"org.freedesktop.gstreamer.Qt6GLVideoItem", 1, 0,
"GstGLQt6VideoItem");
64VideoManager::~VideoManager()
66 qCDebug(VideoManagerLog) <<
this;
71 return _videoManagerInstance();
74void VideoManager::init(QQuickWindow *mainWindow)
77 qCDebug(VideoManagerLog) <<
"Video Manager already initialized";
82 qCCritical(VideoManagerLog) <<
"Failed To Init Video Manager - mainWindow is NULL";
85 _mainWindow = mainWindow;
103void VideoManager::_initAfterQmlIsReady()
105 if (_initAfterQmlIsReadyDone) {
106 qCWarning(VideoManagerLog) <<
"_initAfterQmlIsReady called multiple times";
110 qCCritical(VideoManagerLog) <<
"_initAfterQmlIsReady called with NULL mainWindow";
113 _initAfterQmlIsReadyDone =
true;
115 qCDebug(VideoManagerLog) <<
"_initAfterQmlIsReady";
117 static const QStringList videoStreamList = {
121 for (
const QString &streamName : videoStreamList) {
122 VideoReceiver *receiver = QGCCorePlugin::instance()->createVideoReceiver(
this);
128 _initVideoReceiver(receiver, _mainWindow);
132void VideoManager::cleanup()
134 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
135 QGCCorePlugin::instance()->releaseVideoSink(receiver->
sink());
139void VideoManager::_cleanupOldVideos()
141 if (!SettingsManager::instance()->videoSettings()->enableStorageLimit()->rawValue().toBool()) {
145 const QString savePath = SettingsManager::instance()->appSettings()->
videoSavePath();
146 QDir videoDir = QDir(savePath);
147 videoDir.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable);
148 videoDir.setSorting(QDir::Time);
150 QStringList nameFilters;
155 videoDir.setNameFilters(nameFilters);
156 QFileInfoList vidList = videoDir.entryInfoList();
157 if (vidList.isEmpty()) {
162 for (
const QFileInfo &video : std::as_const(vidList)) {
163 total += video.size();
166 const uint64_t maxSize = SettingsManager::instance()->videoSettings()->
maxVideoSize()->rawValue().toUInt() * qPow(1024, 2);
167 while ((total >= maxSize) && !vidList.isEmpty()) {
168 const QFileInfo info = vidList.takeLast();
169 total -= info.size();
170 const QString path = info.filePath();
171 qCDebug(VideoManagerLog) <<
"Removing old video file:" << path;
172 (void) QFile::remove(path);
176void VideoManager::startRecording(
const QString &videoFile)
180 qgcApp()->showAppMessage(tr(
"Invalid video format defined."));
186 const QString savePath = SettingsManager::instance()->appSettings()->
videoSavePath();
187 if (savePath.isEmpty()) {
188 qgcApp()->showAppMessage(tr(
"Unabled to record video. Video save path must be specified in Settings."));
192 const QString videoFileUrl = videoFile.isEmpty() ? QDateTime::currentDateTime().toString(
"yyyy-MM-dd_hh.mm.ss") : videoFile;
195 const QString videoFileNameTemplate = savePath +
"/" + videoFileUrl +
".%1" + ext;
197 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
199 qCDebug(VideoManagerLog) <<
"Video receiver is not ready.";
202 const QString streamName = (receiver->
name() == QStringLiteral(
"videoContent")) ?
"" : (receiver->name() +
".");
203 const QString videoFileName = videoFileNameTemplate.arg(streamName);
208void VideoManager::stopRecording()
210 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
215void VideoManager::grabImage(
const QString &imageFile)
217 if (imageFile.isEmpty()) {
218 _imageFile = SettingsManager::instance()->appSettings()->
photoSavePath();
219 _imageFile += QStringLiteral(
"/") + QDateTime::currentDateTime().toString(
"yyyy-MM-dd_hh.mm.ss.zzz") + QStringLiteral(
".jpg");
221 _imageFile = imageFile;
226 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
232double VideoManager::aspectRatio()
const
236 if (!receiver->
isThermal() && pInfo && !pInfo->isThermal()) {
237 return pInfo->aspectRatio();
242 return _videoSettings->
aspectRatio()->rawValue().toDouble();
245double VideoManager::thermalAspectRatio()
const
249 if (receiver->
isThermal() && pInfo && pInfo->isThermal()) {
250 return pInfo->aspectRatio();
257double VideoManager::hfov()
const
261 if (!receiver->
isThermal() && pInfo && !pInfo->isThermal()) {
262 return pInfo->hfov();
269double VideoManager::thermalHfov()
const
273 if (receiver->
isThermal() && pInfo && pInfo->isThermal()) {
274 return pInfo->hfov();
278 return _videoSettings->
aspectRatio()->rawValue().toDouble();
281bool VideoManager::hasThermal()
const
285 if (receiver->
isThermal() && pInfo && pInfo->isThermal()) {
293bool VideoManager::hasVideo()
const
295 return (_videoSettings->
streamEnabled()->rawValue().toBool() && _videoSettings->streamConfigured());
298bool VideoManager::isUvc()
const
300 return (!_uvcVideoSourceID.isEmpty() && uvcEnabled() && hasVideo());
303bool VideoManager::gstreamerEnabled()
305#ifdef QGC_GST_STREAMING
312bool VideoManager::uvcEnabled()
317bool VideoManager::qtmultimediaEnabled()
322void VideoManager::setfullScreen(
bool on)
330 if (on != _fullScreen) {
336bool VideoManager::isStreamSource()
const
338 static const QStringList videoSourceList = {
350 const QString videoSource = _videoSettings->
videoSource()->rawValue().toString();
351 return (videoSourceList.contains(videoSource) || autoStreamConfigured());
354void VideoManager::_videoSourceChanged()
356 bool changed =
false;
357 if (_activeVehicle) {
359 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
362 info = camMgr ? camMgr->thermalStreamInstance() :
nullptr;
364 info = camMgr ? camMgr->currentStreamInstance() :
nullptr;
368 changed |= _updateSettings(receiver);
371 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
373 changed |= _updateSettings(receiver);
388 qCDebug(VideoManagerLog) <<
"New Video Source:" << _videoSettings->
videoSource()->rawValue().toString();
396 const QString oldUvcVideoSrcID = _uvcVideoSourceID;
398 if (!uvcEnabled() || !hasVideo() || isStreamSource()) {
399 _uvcVideoSourceID = QString();
404 if (oldUvcVideoSrcID != _uvcVideoSourceID) {
405 qCDebug(VideoManagerLog) <<
"UVC changed from [" << oldUvcVideoSrcID <<
"] to [" << _uvcVideoSourceID <<
"]";
406 if (!_uvcVideoSourceID.isEmpty()) {
417bool VideoManager::autoStreamConfigured()
const
421 if (!receiver->
isThermal() && pInfo && !pInfo->isThermal()) {
422 return !pInfo->uri().isEmpty();
436 qCDebug(VideoManagerLog) << QString(
"Configure stream (%1):").arg(receiver->
name()) << pInfo->uri();
439 switch (pInfo->type()) {
440 case VIDEO_STREAM_TYPE_RTSP:
444 _videoSettings->
rtspUrl()->setRawValue(url);
447 case VIDEO_STREAM_TYPE_TCP_MPEG:
451 case VIDEO_STREAM_TYPE_RTPUDP:
452 if (pInfo->encoding() == VIDEO_STREAM_ENCODING_H265) {
454 url = pInfo->uri().contains(
"udp265://") ? pInfo->uri() : QStringLiteral(
"udp265://0.0.0.0:%1").arg(pInfo->uri());
457 url = pInfo->uri().contains(
"udp://") ? pInfo->uri() : QStringLiteral(
"udp://0.0.0.0:%1").arg(pInfo->uri());
460 case VIDEO_STREAM_TYPE_MPEG_TS:
462 url = pInfo->uri().contains(
"mpegts://") ? pInfo->uri() : QStringLiteral(
"mpegts://0.0.0.0:%1").arg(pInfo->uri());
465 qCWarning(VideoManagerLog) <<
"Unknown VIDEO_STREAM_TYPE";
471 const bool settingsChanged = _updateVideoUri(receiver, url);
472 if (settingsChanged) {
474 _videoSettings->
videoSource()->setRawValue(source);
480 return settingsChanged;
483bool VideoManager::_updateVideoUri(
VideoReceiver *receiver,
const QString &uri)
486 qCDebug(VideoManagerLog) <<
"VideoReceiver is NULL";
490 if ((uri == receiver->
uri()) && !receiver->
uri().isNull()) {
494 qCDebug(VideoManagerLog) <<
"New Video URI" << uri;
504 qCDebug(VideoManagerLog) <<
"VideoReceiver is NULL";
508 bool settingsChanged =
false;
510 const bool lowLatency = _videoSettings->
lowLatencyMode()->rawValue().toBool();
513 settingsChanged =
true;
517 return settingsChanged;
520 settingsChanged |= _updateUVC(receiver);
521 settingsChanged |= _updateAutoStream(receiver);
523 const QString source = _videoSettings->
videoSource()->rawValue().toString();
525 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"udp://%1").arg(_videoSettings->
udpUrl()->rawValue().toString()));
527 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"udp265://%1").arg(_videoSettings->
udpUrl()->rawValue().toString()));
529 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"mpegts://%1").arg(_videoSettings->
udpUrl()->rawValue().toString()));
531 settingsChanged |= _updateVideoUri(receiver, _videoSettings->
rtspUrl()->rawValue().toString());
533 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"tcp://%1").arg(_videoSettings->
tcpUrl()->rawValue().toString()));
535 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"udp://0.0.0.0:5600"));
537 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"udp://0.0.0.0:8888"));
539 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"rtsp://192.168.42.1:554/live"));
541 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"rtsp://192.168.0.10:8554/H264Video"));
543 settingsChanged |= _updateVideoUri(receiver, QStringLiteral(
"rtsp://192.168.43.1:8554/fpv_stream"));
545 settingsChanged |= _updateVideoUri(receiver, QString());
547 settingsChanged |= _updateVideoUri(receiver, QString());
549 qCCritical(VideoManagerLog) <<
"Video source URI \"" << source <<
"\" is not supported. Please add support!";
553 return settingsChanged;
556void VideoManager::_setActiveVehicle(
Vehicle *vehicle)
558 qCDebug(VideoManagerLog) << Q_FUNC_INFO <<
"new vehicle" << vehicle <<
"old active vehicle" << _activeVehicle;
560 if (_activeVehicle) {
562 auto cameraManager = _activeVehicle->cameraManager();
571 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
577 _activeVehicle = vehicle;
578 if (_activeVehicle) {
580 if (_activeVehicle->cameraManager()) {
588 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
589 if (_activeVehicle->cameraManager()) {
591 receiver->
setVideoStreamInfo(_activeVehicle->cameraManager()->thermalStreamInstance());
593 receiver->
setVideoStreamInfo(_activeVehicle->cameraManager()->currentStreamInstance());
601 setfullScreen(
false);
605void VideoManager::_communicationLostChanged(
bool connectionLost)
607 if (connectionLost) {
608 setfullScreen(
false);
612void VideoManager::_restartAllVideos()
614 for (
VideoReceiver *videoReceiver : std::as_const(_videoReceivers)) {
615 _restartVideo(videoReceiver);
622 qCDebug(VideoManagerLog) <<
"VideoReceiver is NULL";
626 qCDebug(VideoManagerLog) <<
"Restart video receiver" << receiver->
name();
629 _stopReceiver(receiver);
632 _startReceiver(receiver);
639 qCDebug(VideoManagerLog) <<
"VideoReceiver is NULL";
648void VideoManager::stopVideo()
650 for (
VideoReceiver *receiver : std::as_const(_videoReceivers)) {
651 _stopReceiver(receiver);
658 qCDebug(VideoManagerLog) <<
"VideoReceiver is NULL";
663 qCDebug(VideoManagerLog) <<
"VideoReceiver is already started" << receiver->
name();
667 if (receiver->
uri().isEmpty()) {
668 qCDebug(VideoManagerLog) <<
"VideoUri is NULL" << receiver->
name();
672 const QString source = _videoSettings->
videoSource()->rawValue().toString();
678 receiver->
start(timeout);
681void VideoManager::_initVideoReceiver(
VideoReceiver *receiver, QQuickWindow *window)
683 if (_videoReceivers.contains(receiver)) {
684 qCWarning(VideoManagerLog) <<
"Receiver already initialized";
687 QQuickItem *widget = window->findChild<QQuickItem*>(receiver->
name());
689 qCCritical(VideoManagerLog) <<
"stream widget not found" << receiver->
name();
693 void *sink = QGCCorePlugin::instance()->createVideoSink(receiver->
widget(), receiver);
695 qCCritical(VideoManagerLog) <<
"createVideoSink() failed" << receiver->
name();
704 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"Start complete, status:" << status;
708 if (receiver->
sink()) {
716 _restartVideo(receiver);
722 qCDebug(VideoManagerLog) <<
"Stop complete" << receiver->
name() << receiver->
uri() <<
", status:" << status;
725 qCDebug(VideoManagerLog) <<
"Invalid video URL. Not restarting";
727 QTimer::singleShot(1000, receiver, [
this, receiver]() {
728 qCDebug(VideoManagerLog) <<
"Restarting video receiver" << receiver->
name() << receiver->
uri();
729 _startReceiver(receiver);
735 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"streaming changed, active:" << (active ?
"yes" :
"no");
743 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"decoding changed, active:" << (active ?
"yes" :
"no");
751 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"recording changed, active:" << (active ?
"yes" :
"no");
762 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"recording started";
769 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"resized. New resolution:" << size.width() <<
"x" << size.height();
778 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"screenshot taken";
780 qCWarning(VideoManagerLog) <<
"Video" << receiver->
name() <<
"screenshot failed";
786 qCDebug(VideoManagerLog) <<
"Video" << receiver->
name() <<
"stream info:" << (videoStreamInfo ?
"received" :
"lost");
788 (void) _updateAutoStream(receiver);
791 (void) _updateSettings(receiver);
793 _videoReceivers.append(receiver);
796 _startReceiver(receiver);
800void VideoManager::startVideo()
802 qCDebug(VideoManagerLog) <<
"startVideo";
805 qCDebug(VideoManagerLog) <<
"Stream not enabled/configured";
827 qCDebug(VideoManagerLog) <<
"FinishVideoInitialization::run";
828 QMetaObject::invokeMethod(VideoManager::instance(), &VideoManager::_initAfterQmlIsReady, Qt::QueuedConnection);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static constexpr const char * kFileExtension[VideoReceiver::FILE_FORMAT_MAX+1]
Q_APPLICATION_STATIC(VideoManager, _videoManagerInstance)
void rawValueChanged(const QVariant &value)
FinishVideoInitialization()
~FinishVideoInitialization()
Abstract base class for all camera controls: real and simulated.
virtual void resumeStream()=0
virtual void stopStream()=0
void activeVehicleChanged(Vehicle *activeVehicle)
Encapsulates the contents of a VIDEO_STREAM_INFORMATION message.
Provides access to all app settings.
void stopCapturingTelemetry()
void startCapturingTelemetry(const QString &videoFile, QSize size)
static QString getSourceId()
static void checkPermission()
void communicationLostChanged(bool communicationLost)
bool communicationLost() const
VehicleLinkManager * vehicleLinkManager()
friend class FinishVideoInitialization
void uvcVideoSourceIDChanged()
void isStreamSourceChanged()
void autoStreamConfiguredChanged()
void imageFileChanged(const QString &filename)
void recordingChanged(bool recording)
void isAutoStreamChanged()
void aspectRatioChanged()
void recordingStarted(const QString &filename)
void setLowLatency(bool lowLatency)
void setName(const QString &name)
void videoSizeChanged(QSize size)
void streamingChanged(bool active)
virtual void stopRecording()=0
virtual void startRecording(const QString &videoFile, FILE_FORMAT format)=0
void recordingChanged(bool active)
void videoStreamInfoChanged()
virtual void setSink(void *sink)
virtual void start(uint32_t timeout)=0
void onTakeScreenshotComplete(STATUS status)
void decodingChanged(bool active)
void onStartComplete(STATUS status)
void setStarted(bool started)
void setUri(const QString &uri)
static bool isValidFileFormat(FILE_FORMAT format)
virtual void setWidget(QQuickItem *widget)
virtual void startDecoding(void *sink)=0
void setVideoStreamInfo(QGCVideoStreamInfo *videoStreamInfo)
void onStopComplete(STATUS status)
virtual void takeScreenshot(const QString &imageFile)=0
QGCVideoStreamInfo * videoStreamInfo()
static constexpr const char * videoSource3DRSolo
Fact *rtspUrl READ rtspUrl CONSTANT Fact * rtspUrl()
static constexpr const char * videoSourceParrotDiscovery
static constexpr const char * videoSourceTCP
Fact *recordingFormat READ recordingFormat CONSTANT Fact * recordingFormat()
static constexpr const char * videoSourceUDPH264
static constexpr const char * videoSourceHerelinkHotspot
Fact *aspectRatio READ aspectRatio CONSTANT Fact * aspectRatio()
static constexpr const char * videoSourceRTSP
Fact *streamEnabled READ streamEnabled CONSTANT Fact * streamEnabled()
Fact *rtspTimeout READ rtspTimeout CONSTANT Fact * rtspTimeout()
static constexpr const char * videoDisabled
Fact *maxVideoSize READ maxVideoSize CONSTANT Fact * maxVideoSize()
Fact *udpUrl READ udpUrl CONSTANT Fact * udpUrl()
Fact *tcpUrl READ tcpUrl CONSTANT Fact * tcpUrl()
Fact *videoSource READ videoSource CONSTANT Fact * videoSource()
static constexpr const char * videoSourceYuneecMantisG
static constexpr const char * videoSourceUDPH265
static constexpr const char * videoSourceNoVideo
Fact *lowLatencyMode READ lowLatencyMode CONSTANT Fact * lowLatencyMode()
static constexpr const char * videoSourceMPEGTS
static constexpr const char * videoSourceHerelinkAirUnit