You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
802 lines
26 KiB
802 lines
26 KiB
/**************************************************************************** |
|
* |
|
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> |
|
* |
|
* QGroundControl is licensed according to the terms in the file |
|
* COPYING.md in the root of the source code directory. |
|
* |
|
****************************************************************************/ |
|
|
|
|
|
#include <QQmlContext> |
|
#include <QQmlEngine> |
|
#include <QSettings> |
|
#include <QUrl> |
|
#include <QDir> |
|
|
|
#ifndef QGC_DISABLE_UVC |
|
#include <QCameraInfo> |
|
#endif |
|
|
|
#include "ScreenToolsController.h" |
|
#include "VideoManager.h" |
|
#include "QGCToolbox.h" |
|
#include "QGCCorePlugin.h" |
|
#include "QGCOptions.h" |
|
#include "MultiVehicleManager.h" |
|
#include "Settings/SettingsManager.h" |
|
#include "Vehicle.h" |
|
#include "QGCCameraManager.h" |
|
|
|
#if defined(QGC_GST_STREAMING) |
|
#include "GStreamer.h" |
|
#else |
|
#include "GLVideoItemStub.h" |
|
#endif |
|
|
|
#ifdef QGC_GST_TAISYNC_ENABLED |
|
#include "TaisyncHandler.h" |
|
#endif |
|
|
|
QGC_LOGGING_CATEGORY(VideoManagerLog, "VideoManagerLog") |
|
|
|
#if defined(QGC_GST_STREAMING) |
|
static const char* kFileExtension[VideoReceiver::FILE_FORMAT_MAX - VideoReceiver::FILE_FORMAT_MIN] = { |
|
"mkv", |
|
"mov", |
|
"mp4" |
|
}; |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
VideoManager::VideoManager(QGCApplication* app, QGCToolbox* toolbox) |
|
: QGCTool(app, toolbox) |
|
{ |
|
#if !defined(QGC_GST_STREAMING) |
|
static bool once = false; |
|
if (!once) { |
|
qmlRegisterType<GLVideoItemStub>("org.freedesktop.gstreamer.GLVideoItem", 1, 0, "GstGLVideoItem"); |
|
once = true; |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
VideoManager::~VideoManager() |
|
{ |
|
for (int i = 0; i < 2; i++) { |
|
if (_videoReceiver[i] != nullptr) { |
|
delete _videoReceiver[i]; |
|
_videoReceiver[i] = nullptr; |
|
} |
|
#if defined(QGC_GST_STREAMING) |
|
if (_videoSink[i] != nullptr) { |
|
GStreamer::releaseVideoSink(_videoSink[i]); |
|
_videoSink[i] = nullptr; |
|
} |
|
#endif |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::setToolbox(QGCToolbox *toolbox) |
|
{ |
|
QGCTool::setToolbox(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); |
|
connect(_videoSettings->udpPort(), &Fact::rawValueChanged, this, &VideoManager::_udpPortChanged); |
|
connect(_videoSettings->rtspUrl(), &Fact::rawValueChanged, this, &VideoManager::_rtspUrlChanged); |
|
connect(_videoSettings->tcpUrl(), &Fact::rawValueChanged, this, &VideoManager::_tcpUrlChanged); |
|
connect(_videoSettings->aspectRatio(), &Fact::rawValueChanged, this, &VideoManager::_aspectRatioChanged); |
|
connect(_videoSettings->lowLatencyMode(),&Fact::rawValueChanged, this, &VideoManager::_lowLatencyModeChanged); |
|
MultiVehicleManager *pVehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); |
|
connect(pVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &VideoManager::_setActiveVehicle); |
|
|
|
#if defined(QGC_GST_STREAMING) |
|
#ifndef QGC_DISABLE_UVC |
|
// If we are using a UVC camera setup the device name |
|
_updateUVC(); |
|
#endif |
|
|
|
emit isGStreamerChanged(); |
|
qCDebug(VideoManagerLog) << "New Video Source:" << videoSource; |
|
#if defined(QGC_GST_STREAMING) |
|
_videoReceiver[0] = toolbox->corePlugin()->createVideoReceiver(this); |
|
_videoReceiver[1] = toolbox->corePlugin()->createVideoReceiver(this); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::timeout, this, [this](){ |
|
_restartVideo(0); |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::streamingChanged, this, [this](bool active){ |
|
_streaming = active; |
|
emit streamingChanged(); |
|
_restartVideo(0); |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::onStartComplete, this, [this](VideoReceiver::STATUS status) { |
|
if (status == VideoReceiver::STATUS_OK) { |
|
_videoStarted[0] = true; |
|
_videoReceiver[0]->startDecoding(_videoSink[0]); |
|
} else if (status == VideoReceiver::STATUS_INVALID_URL) { |
|
// Invalid URL - don't restart |
|
} else if (status == VideoReceiver::STATUS_INVALID_STATE) { |
|
// Already running |
|
} else { |
|
_restartVideo(0); |
|
} |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::onStopComplete, this, [this](VideoReceiver::STATUS) { |
|
_videoStarted[0] = false; |
|
_startReceiver(0); |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::decodingChanged, this, [this](bool active){ |
|
_decoding = active; |
|
emit decodingChanged(); |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::recordingChanged, this, [this](bool active){ |
|
_recording = active; |
|
if (!active) { |
|
_subtitleWriter.stopCapturingTelemetry(); |
|
} |
|
emit recordingChanged(); |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::recordingStarted, this, [this](){ |
|
_subtitleWriter.startCapturingTelemetry(_videoFile); |
|
}); |
|
|
|
connect(_videoReceiver[0], &VideoReceiver::videoSizeChanged, this, [this](QSize size){ |
|
_videoSize = ((quint32)size.width() << 16) | (quint32)size.height(); |
|
emit videoSizeChanged(); |
|
}); |
|
|
|
//connect(_videoReceiver, &VideoReceiver::onTakeScreenshotComplete, this, [this](VideoReceiver::STATUS status){ |
|
// if (status == VideoReceiver::STATUS_OK) { |
|
// } |
|
//}); |
|
|
|
// FIXME: AV: I believe _thermalVideoReceiver should be handled just like _videoReceiver in terms of event |
|
// and I expect that it will be changed during multiple video stream activity |
|
if (_videoReceiver[1] != nullptr) { |
|
connect(_videoReceiver[1], &VideoReceiver::timeout, this, [this](){ |
|
_restartVideo(1); |
|
}); |
|
|
|
connect(_videoReceiver[1], &VideoReceiver::streamingChanged, this, [this](bool active){ |
|
_restartVideo(1); |
|
}); |
|
|
|
connect(_videoReceiver[1], &VideoReceiver::onStartComplete, this, [this](VideoReceiver::STATUS status) { |
|
if (status == VideoReceiver::STATUS_OK) { |
|
_videoStarted[1] = true; |
|
_videoReceiver[1]->startDecoding(_videoSink[1]); |
|
} else if (status == VideoReceiver::STATUS_INVALID_URL) { |
|
// Invalid URL - don't restart |
|
} else if (status == VideoReceiver::STATUS_INVALID_STATE) { |
|
// Already running |
|
} else { |
|
_restartVideo(1); |
|
} |
|
}); |
|
|
|
connect(_videoReceiver[1], &VideoReceiver::onStopComplete, this, [this](VideoReceiver::STATUS) { |
|
_videoStarted[1] = false; |
|
_startReceiver(1); |
|
}); |
|
} |
|
#endif |
|
_updateSettings(0); |
|
_updateSettings(1); |
|
if(isGStreamer()) { |
|
startVideo(); |
|
} else { |
|
stopVideo(); |
|
} |
|
|
|
#endif |
|
} |
|
|
|
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(size_t i = 0; i < sizeof(kFileExtension) / sizeof(kFileExtension[0]); i += 1) { |
|
nameFilters << QString("*.") + kFileExtension[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(VideoManagerLog) << "Removing old video file:" << vidList.last().filePath(); |
|
QFile file (vidList.last().filePath()); |
|
file.remove(); |
|
vidList.removeLast(); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::startVideo() |
|
{ |
|
if (qgcApp()->runningUnitTests()) { |
|
return; |
|
} |
|
|
|
if(!_videoSettings->streamEnabled()->rawValue().toBool() || !_videoSettings->streamConfigured()) { |
|
qCDebug(VideoManagerLog) << "Stream not enabled/configured"; |
|
return; |
|
} |
|
|
|
_startReceiver(0); |
|
_startReceiver(1); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::stopVideo() |
|
{ |
|
if (qgcApp()->runningUnitTests()) { |
|
return; |
|
} |
|
|
|
_stopReceiver(1); |
|
_stopReceiver(0); |
|
} |
|
|
|
void |
|
VideoManager::startRecording(const QString& videoFile) |
|
{ |
|
if (qgcApp()->runningUnitTests()) { |
|
return; |
|
} |
|
#if defined(QGC_GST_STREAMING) |
|
if (!_videoReceiver[0]) { |
|
qgcApp()->showAppMessage(tr("Video receiver is not ready.")); |
|
return; |
|
} |
|
|
|
const VideoReceiver::FILE_FORMAT fileFormat = static_cast<VideoReceiver::FILE_FORMAT>(_videoSettings->recordingFormat()->rawValue().toInt()); |
|
|
|
if(fileFormat < VideoReceiver::FILE_FORMAT_MIN || fileFormat >= VideoReceiver::FILE_FORMAT_MAX) { |
|
qgcApp()->showAppMessage(tr("Invalid video format defined.")); |
|
return; |
|
} |
|
|
|
//-- Disk usage maintenance |
|
_cleanupOldVideos(); |
|
|
|
QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath(); |
|
|
|
if(savePath.isEmpty()) { |
|
qgcApp()->showAppMessage(tr("Unabled to record video. Video save path must be specified in Settings.")); |
|
return; |
|
} |
|
|
|
_videoFile = savePath + "/" |
|
+ (videoFile.isEmpty() ? QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") : videoFile) |
|
+ "." + kFileExtension[fileFormat - VideoReceiver::FILE_FORMAT_MIN]; |
|
|
|
_videoReceiver[0]->startRecording(_videoFile, fileFormat); |
|
#else |
|
Q_UNUSED(videoFile) |
|
#endif |
|
} |
|
|
|
void |
|
VideoManager::stopRecording() |
|
{ |
|
if (qgcApp()->runningUnitTests()) { |
|
return; |
|
} |
|
#if defined(QGC_GST_STREAMING) |
|
if (!_videoReceiver[0]) { |
|
return; |
|
} |
|
|
|
_videoReceiver[0]->stopRecording(); |
|
#endif |
|
} |
|
|
|
void |
|
VideoManager::grabImage(const QString& imageFile) |
|
{ |
|
if (qgcApp()->runningUnitTests()) { |
|
return; |
|
} |
|
#if defined(QGC_GST_STREAMING) |
|
if (!_videoReceiver[0]) { |
|
return; |
|
} |
|
|
|
_imageFile = imageFile; |
|
|
|
emit imageFileChanged(); |
|
|
|
_videoReceiver[0]->takeScreenshot(_imageFile); |
|
#else |
|
Q_UNUSED(imageFile) |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
double VideoManager::aspectRatio() |
|
{ |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance(); |
|
if(pInfo) { |
|
qCDebug(VideoManagerLog) << "Primary AR: " << pInfo->aspectRatio(); |
|
return pInfo->aspectRatio(); |
|
} |
|
} |
|
// FIXME: AV: use _videoReceiver->videoSize() to calculate AR (if AR is not specified in the settings?) |
|
return _videoSettings->aspectRatio()->rawValue().toDouble(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
double VideoManager::thermalAspectRatio() |
|
{ |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance(); |
|
if(pInfo) { |
|
qCDebug(VideoManagerLog) << "Thermal AR: " << pInfo->aspectRatio(); |
|
return pInfo->aspectRatio(); |
|
} |
|
} |
|
return 1.0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
double VideoManager::hfov() |
|
{ |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance(); |
|
if(pInfo) { |
|
return pInfo->hfov(); |
|
} |
|
} |
|
return 1.0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
double VideoManager::thermalHfov() |
|
{ |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance(); |
|
if(pInfo) { |
|
return pInfo->aspectRatio(); |
|
} |
|
} |
|
return _videoSettings->aspectRatio()->rawValue().toDouble(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
VideoManager::hasThermal() |
|
{ |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance(); |
|
if(pInfo) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
QString |
|
VideoManager::imageFile() |
|
{ |
|
return _imageFile; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
VideoManager::autoStreamConfigured() |
|
{ |
|
#if defined(QGC_GST_STREAMING) |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance(); |
|
if(pInfo) { |
|
return !pInfo->uri().isEmpty(); |
|
} |
|
} |
|
#endif |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_updateUVC() |
|
{ |
|
#ifndef QGC_DISABLE_UVC |
|
QString videoSource = _videoSettings->videoSource()->rawValue().toString(); |
|
QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); |
|
for (const QCameraInfo &cameraInfo: cameras) { |
|
if(cameraInfo.description() == videoSource) { |
|
_videoSourceID = cameraInfo.deviceName(); |
|
emit videoSourceIDChanged(); |
|
qCDebug(VideoManagerLog) << "Found USB source:" << _videoSourceID << " Name:" << videoSource; |
|
break; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_videoSourceChanged() |
|
{ |
|
_updateUVC(); |
|
emit hasVideoChanged(); |
|
emit isGStreamerChanged(); |
|
emit isAutoStreamChanged(); |
|
_restartVideo(0); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_udpPortChanged() |
|
{ |
|
_restartVideo(0); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_rtspUrlChanged() |
|
{ |
|
_restartVideo(0); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_tcpUrlChanged() |
|
{ |
|
_restartVideo(0); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_lowLatencyModeChanged() |
|
{ |
|
//restartVideo(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
VideoManager::hasVideo() |
|
{ |
|
if(autoStreamConfigured()) { |
|
return true; |
|
} |
|
QString videoSource = _videoSettings->videoSource()->rawValue().toString(); |
|
return !videoSource.isEmpty() && videoSource != VideoSettings::videoSourceNoVideo && videoSource != VideoSettings::videoDisabled; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
VideoManager::isGStreamer() |
|
{ |
|
#if defined(QGC_GST_STREAMING) |
|
QString videoSource = _videoSettings->videoSource()->rawValue().toString(); |
|
return |
|
videoSource == VideoSettings::videoSourceUDPH264 || |
|
videoSource == VideoSettings::videoSourceUDPH265 || |
|
videoSource == VideoSettings::videoSourceRTSP || |
|
videoSource == VideoSettings::videoSourceTCP || |
|
videoSource == VideoSettings::videoSourceMPEGTS || |
|
autoStreamConfigured(); |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
#ifndef QGC_DISABLE_UVC |
|
bool |
|
VideoManager::uvcEnabled() |
|
{ |
|
return QCameraInfo::availableCameras().count() > 0; |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::setfullScreen(bool f) |
|
{ |
|
if(f) { |
|
//-- No can do if no vehicle or connection lost |
|
if(!_activeVehicle || _activeVehicle->connectionLost()) { |
|
f = false; |
|
} |
|
} |
|
_fullScreen = f; |
|
emit fullScreenChanged(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_initVideo() |
|
{ |
|
#if defined(QGC_GST_STREAMING) |
|
QQuickItem* root = qgcApp()->mainRootWindow(); |
|
|
|
if (root == nullptr) { |
|
qCDebug(VideoManagerLog) << "mainRootWindow() failed. No root window"; |
|
return; |
|
} |
|
|
|
QQuickItem* widget = root->findChild<QQuickItem*>("videoContent"); |
|
|
|
if (widget != nullptr && _videoReceiver[0] != nullptr) { |
|
_videoSink[0] = qgcApp()->toolbox()->corePlugin()->createVideoSink(this, widget); |
|
if (_videoSink[0] == nullptr) { |
|
qCDebug(VideoManagerLog) << "createVideoSink() failed"; |
|
} |
|
} else { |
|
qCDebug(VideoManagerLog) << "video receiver disabled"; |
|
} |
|
|
|
widget = root->findChild<QQuickItem*>("thermalVideo"); |
|
|
|
if (widget != nullptr && _videoReceiver[1] != nullptr) { |
|
_videoSink[1] = qgcApp()->toolbox()->corePlugin()->createVideoSink(this, widget); |
|
if (_videoSink[1] == nullptr) { |
|
qCDebug(VideoManagerLog) << "createVideoSink() failed"; |
|
} |
|
} else { |
|
qCDebug(VideoManagerLog) << "thermal video receiver disabled"; |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
VideoManager::_updateSettings(unsigned id) |
|
{ |
|
if(!_videoSettings) |
|
return false; |
|
//-- Auto discovery |
|
if(_activeVehicle && _activeVehicle->dynamicCameras()) { |
|
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance(); |
|
if(pInfo) { |
|
bool status = false; |
|
if (id == 0) { |
|
qCDebug(VideoManagerLog) << "Configure primary stream:" << pInfo->uri(); |
|
switch(pInfo->type()) { |
|
case VIDEO_STREAM_TYPE_RTSP: |
|
if ((status = _updateVideoUri(id, pInfo->uri()))) { |
|
_toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceRTSP); |
|
} |
|
break; |
|
case VIDEO_STREAM_TYPE_TCP_MPEG: |
|
if ((status = _updateVideoUri(id, pInfo->uri()))) { |
|
_toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceTCP); |
|
} |
|
break; |
|
case VIDEO_STREAM_TYPE_RTPUDP: |
|
if ((status = _updateVideoUri(id, QStringLiteral("udp://0.0.0.0:%1").arg(pInfo->uri())))) { |
|
_toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceUDPH264); |
|
} |
|
break; |
|
case VIDEO_STREAM_TYPE_MPEG_TS_H264: |
|
if ((status = _updateVideoUri(id, QStringLiteral("mpegts://0.0.0.0:%1").arg(pInfo->uri())))) { |
|
_toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceMPEGTS); |
|
} |
|
break; |
|
default: |
|
status = _updateVideoUri(id, pInfo->uri()); |
|
break; |
|
} |
|
} |
|
else if (id == 1) { //-- Thermal stream (if any) |
|
QGCVideoStreamInfo* pTinfo = _activeVehicle->dynamicCameras()->thermalStreamInstance(); |
|
if (pTinfo) { |
|
qCDebug(VideoManagerLog) << "Configure secondary stream:" << pTinfo->uri(); |
|
switch(pTinfo->type()) { |
|
case VIDEO_STREAM_TYPE_RTSP: |
|
case VIDEO_STREAM_TYPE_TCP_MPEG: |
|
status |= _updateVideoUri(id, pTinfo->uri()); |
|
break; |
|
case VIDEO_STREAM_TYPE_RTPUDP: |
|
status |= _updateVideoUri(id, QStringLiteral("udp://0.0.0.0:%1").arg(pTinfo->uri())); |
|
break; |
|
case VIDEO_STREAM_TYPE_MPEG_TS_H264: |
|
status |= _updateVideoUri(id, QStringLiteral("mpegts://0.0.0.0:%1").arg(pTinfo->uri())); |
|
break; |
|
default: |
|
status |= _updateVideoUri(id, pTinfo->uri()); |
|
break; |
|
} |
|
} |
|
} |
|
return status; |
|
} |
|
} |
|
QString source = _videoSettings->videoSource()->rawValue().toString(); |
|
if (source == VideoSettings::videoSourceUDPH264) |
|
return _updateVideoUri(0, QStringLiteral("udp://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt())); |
|
else if (source == VideoSettings::videoSourceUDPH265) |
|
return _updateVideoUri(0, QStringLiteral("udp265://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt())); |
|
else if (source == VideoSettings::videoSourceMPEGTS) |
|
return _updateVideoUri(0, QStringLiteral("mpegts://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt())); |
|
else if (source == VideoSettings::videoSourceRTSP) |
|
return _updateVideoUri(0, _videoSettings->rtspUrl()->rawValue().toString()); |
|
else if (source == VideoSettings::videoSourceTCP) |
|
return _updateVideoUri(0, QStringLiteral("tcp://%1").arg(_videoSettings->tcpUrl()->rawValue().toString())); |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool |
|
VideoManager::_updateVideoUri(unsigned id, const QString& uri) |
|
{ |
|
#if defined(QGC_GST_TAISYNC_ENABLED) && (defined(__android__) || defined(__ios__)) |
|
//-- Taisync on iOS or Android sends a raw h.264 stream |
|
if (isTaisync()) { |
|
if (id == 0) { |
|
return _updateVideoUri(0, QString("tsusb://0.0.0.0:%1").arg(TAISYNC_VIDEO_UDP_PORT)); |
|
} if (id == 1) { |
|
// FIXME: AV: TAISYNC_VIDEO_UDP_PORT is used by video stream, thermal stream should go via its own proxy |
|
if (!_videoUri[1].isEmpty()) { |
|
_videoUri[1].clear(); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
} |
|
#endif |
|
if (uri == _videoUri[id]) { |
|
return false; |
|
} |
|
|
|
_videoUri[id] = uri; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_restartVideo(unsigned id) |
|
{ |
|
#if defined(QGC_GST_STREAMING) |
|
QString oldUri = _videoUri[id]; |
|
_updateSettings(id); |
|
QString newUri = _videoUri[id]; |
|
|
|
if (oldUri == newUri && _videoStarted[id]) { |
|
qCDebug(VideoManagerLog) << "No sense to restart video streaming, skipped" << id; |
|
return; |
|
} |
|
|
|
qCDebug(VideoManagerLog) << "Restart video streaming" << id; |
|
|
|
if (_videoStarted[id]) { |
|
_stopReceiver(id); |
|
} else { |
|
_startReceiver(id); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void |
|
VideoManager::_restartAllVideos() |
|
{ |
|
_restartVideo(0); |
|
_restartVideo(1); |
|
} |
|
|
|
//---------------------------------------------------------------------------------------- |
|
void |
|
VideoManager::_startReceiver(unsigned id) |
|
{ |
|
#if defined(QGC_GST_STREAMING) |
|
const unsigned timeout = _videoSettings->rtspTimeout()->rawValue().toUInt(); |
|
|
|
if (id > 1) { |
|
qCDebug(VideoManagerLog) << "Unsupported receiver id" << id; |
|
} else if (_videoReceiver[id] != nullptr && _videoSink[id] != nullptr) { |
|
if (!_videoUri[id].isEmpty()) { |
|
_videoReceiver[id]->start(_videoUri[id], timeout); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
//---------------------------------------------------------------------------------------- |
|
void |
|
VideoManager::_stopReceiver(unsigned id) |
|
{ |
|
#if defined(QGC_GST_STREAMING) |
|
if (id > 1) { |
|
qCDebug(VideoManagerLog) << "Unsupported receiver id" << id; |
|
} else if (_videoReceiver[id] != nullptr) { |
|
_videoReceiver[id]->stop(); |
|
} |
|
#endif |
|
} |
|
|
|
//---------------------------------------------------------------------------------------- |
|
void |
|
VideoManager::_setActiveVehicle(Vehicle* vehicle) |
|
{ |
|
if(_activeVehicle) { |
|
disconnect(_activeVehicle, &Vehicle::connectionLostChanged, this, &VideoManager::_connectionLostChanged); |
|
if(_activeVehicle->dynamicCameras()) { |
|
QGCCameraControl* pCamera = _activeVehicle->dynamicCameras()->currentCameraInstance(); |
|
if(pCamera) { |
|
pCamera->stopStream(); |
|
} |
|
disconnect(_activeVehicle->dynamicCameras(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartAllVideos); |
|
} |
|
} |
|
_activeVehicle = vehicle; |
|
if(_activeVehicle) { |
|
connect(_activeVehicle, &Vehicle::connectionLostChanged, this, &VideoManager::_connectionLostChanged); |
|
if(_activeVehicle->dynamicCameras()) { |
|
connect(_activeVehicle->dynamicCameras(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartAllVideos); |
|
QGCCameraControl* pCamera = _activeVehicle->dynamicCameras()->currentCameraInstance(); |
|
if(pCamera) { |
|
pCamera->resumeStream(); |
|
} |
|
} |
|
} else { |
|
//-- Disable full screen video if vehicle is gone |
|
setfullScreen(false); |
|
} |
|
emit autoStreamConfiguredChanged(); |
|
_restartAllVideos(); |
|
} |
|
|
|
//---------------------------------------------------------------------------------------- |
|
void |
|
VideoManager::_connectionLostChanged(bool connectionLost) |
|
{ |
|
if(connectionLost) { |
|
//-- Disable full screen video if connection is lost |
|
setfullScreen(false); |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------------------------- |
|
void |
|
VideoManager::_aspectRatioChanged() |
|
{ |
|
emit aspectRatioChanged(); |
|
}
|
|
|