VideoManager.cc 25.1 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9 10 11 12 13
 *
 * 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>
14
#include <QUrl>
15
#include <QDir>
16 17

#ifndef QGC_DISABLE_UVC
18
#include <QCameraInfo>
19
#endif
20 21 22

#include "ScreenToolsController.h"
#include "VideoManager.h"
23 24 25
#include "QGCToolbox.h"
#include "QGCCorePlugin.h"
#include "QGCOptions.h"
26
#include "MultiVehicleManager.h"
27
#include "Settings/SettingsManager.h"
28
#include "Vehicle.h"
29
#include "QGCCameraManager.h"
30

31 32 33 34 35 36
#if defined(QGC_GST_STREAMING)
#include "GStreamer.h"
#else
#include "GLVideoItemStub.h"
#endif

Andrew Voznytsa's avatar
Andrew Voznytsa committed
37 38 39 40
#ifdef QGC_GST_TAISYNC_ENABLED
#include "TaisyncHandler.h"
#endif

41 42
QGC_LOGGING_CATEGORY(VideoManagerLog, "VideoManagerLog")

43
#if defined(QGC_GST_STREAMING)
44 45 46 47 48
static const char* kFileExtension[VideoReceiver::FILE_FORMAT_MAX - VideoReceiver::FILE_FORMAT_MIN] = {
    "mkv",
    "mov",
    "mp4"
};
49
#endif
50

51
//-----------------------------------------------------------------------------
52 53
VideoManager::VideoManager(QGCApplication* app, QGCToolbox* toolbox)
    : QGCTool(app, toolbox)
54
{
55 56 57 58 59 60 61
#if !defined(QGC_GST_STREAMING)
    static bool once = false;
    if (!once) {
        qmlRegisterType<GLVideoItemStub>("org.freedesktop.gstreamer.GLVideoItem", 1, 0, "GstGLVideoItem");
        once = true;
    }
#endif
62 63 64 65 66
}

//-----------------------------------------------------------------------------
VideoManager::~VideoManager()
{
67 68 69 70
    delete _videoReceiver;
    _videoReceiver = nullptr;
    delete _thermalVideoReceiver;
    _thermalVideoReceiver = nullptr;
71
#if defined(QGC_GST_STREAMING)
72 73 74 75
    GStreamer::releaseVideoSink(_thermalVideoSink);
    _thermalVideoSink = nullptr;
    GStreamer::releaseVideoSink(_videoSink);
    _videoSink = nullptr;
76
#endif
77 78 79 80 81 82 83 84
}

//-----------------------------------------------------------------------------
void
VideoManager::setToolbox(QGCToolbox *toolbox)
{
   QGCTool::setToolbox(toolbox);
   QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
85 86
   qmlRegisterUncreatableType<VideoManager> ("QGroundControl.VideoManager", 1, 0, "VideoManager", "Reference only");
   qmlRegisterUncreatableType<VideoReceiver>("QGroundControl",              1, 0, "VideoReceiver","Reference only");
87 88

   // TODO: Those connections should be Per Video, not per VideoManager.
89 90
   _videoSettings = toolbox->settingsManager()->videoSettings();
   QString videoSource = _videoSettings->videoSource()->rawValue().toString();
91 92 93
   connect(_videoSettings->videoSource(),   &Fact::rawValueChanged, this, &VideoManager::_videoSourceChanged);
   connect(_videoSettings->udpPort(),       &Fact::rawValueChanged, this, &VideoManager::_udpPortChanged);
   connect(_videoSettings->rtspUrl(),       &Fact::rawValueChanged, this, &VideoManager::_rtspUrlChanged);
94
   connect(_videoSettings->tcpUrl(),        &Fact::rawValueChanged, this, &VideoManager::_tcpUrlChanged);
95
   connect(_videoSettings->aspectRatio(),   &Fact::rawValueChanged, this, &VideoManager::_aspectRatioChanged);
96
   connect(_videoSettings->lowLatencyMode(),&Fact::rawValueChanged, this, &VideoManager::_lowLatencyModeChanged);
97 98
   MultiVehicleManager *pVehicleMgr = qgcApp()->toolbox()->multiVehicleManager();
   connect(pVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &VideoManager::_setActiveVehicle);
99

100
#if defined(QGC_GST_STREAMING)
101 102
#ifndef QGC_DISABLE_UVC
   // If we are using a UVC camera setup the device name
Gus Grubba's avatar
Gus Grubba committed
103
   _updateUVC();
104
#endif
105 106 107

    emit isGStreamerChanged();
    qCDebug(VideoManagerLog) << "New Video Source:" << videoSource;
108
#if defined(QGC_GST_STREAMING)
109
    _videoReceiver = toolbox->corePlugin()->createVideoReceiver(this);
110
    _thermalVideoReceiver = toolbox->corePlugin()->createVideoReceiver(this);
111

112 113 114 115 116 117 118 119 120 121
    connect(_videoReceiver, &VideoReceiver::timeout, this, [this](){
        if (!_enableVideoRestart) return;
        _startReceiver(0);
    });

    connect(_videoReceiver, &VideoReceiver::streamingChanged, this, [this](){
        if (!_enableVideoRestart || _videoReceiver->streaming())  return;
        _startReceiver(0);
    });

122 123
    connect(_videoReceiver, &VideoReceiver::recordingStarted, this,  &VideoManager::_recordingStarted);
    connect(_videoReceiver, &VideoReceiver::recordingChanged, this,  &VideoManager::_recordingChanged);
124
    connect(_videoReceiver, &VideoReceiver::onTakeScreenshotComplete, this,  &VideoManager::_onTakeScreenshotComplete);
125

126 127
    // 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
128
    if (_thermalVideoReceiver != nullptr) {
129 130 131 132 133 134 135 136 137
        connect(_thermalVideoReceiver, &VideoReceiver::timeout, this, [this](){
            if (!_enableVideoRestart) return;
            _startReceiver(1);
        });

        connect(_thermalVideoReceiver, &VideoReceiver::streamingChanged, this, [this](){
            if (!_enableVideoRestart) return;
            _startReceiver(1);
        });
138
    }
139
#endif
140 141
    _updateSettings();
    if(isGStreamer()) {
142
        startVideo();
143
    } else {
144
        stopVideo();
145 146
    }

147
#endif
148 149
}

150
void VideoManager::_cleanupOldVideos()
151 152 153 154 155 156 157 158 159 160 161 162
{
#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;
163 164 165

    for(size_t i = 0; i < sizeof(kFileExtension) / sizeof(kFileExtension[0]); i += 1) {
        nameFilters << QString("*.") + kFileExtension[i];
166
    }
167

168 169 170 171 172 173 174 175 176 177 178 179 180 181
    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();
182
            qCDebug(VideoManagerLog) << "Removing old video file:" << vidList.last().filePath();
183 184 185 186 187 188 189 190
            QFile file (vidList.last().filePath());
            file.remove();
            vidList.removeLast();
        }
    }
#endif
}

191 192 193 194
//-----------------------------------------------------------------------------
void
VideoManager::startVideo()
{
195 196 197 198 199
    if (qgcApp()->runningUnitTests()) {
        return;
    }

    if(!_videoSettings->streamEnabled()->rawValue().toBool() || !_videoSettings->streamConfigured()) {
200
        qCDebug(VideoManagerLog) << "Stream not enabled/configured";
201 202 203
        return;
    }

204
    _enableVideoRestart = true;
205

206 207
    _startReceiver(0);
    _startReceiver(1);
208 209 210 211 212 213
}

//-----------------------------------------------------------------------------
void
VideoManager::stopVideo()
{
214 215 216
    if (qgcApp()->runningUnitTests()) {
        return;
    }
217

218
    _enableVideoRestart = false;
219

220 221
    _stopReceiver(1);
    _stopReceiver(0);
222 223
}

224 225 226 227 228 229
void
VideoManager::startRecording(const QString& videoFile)
{
    if (qgcApp()->runningUnitTests()) {
        return;
    }
230
#if defined(QGC_GST_STREAMING)
231
    if (!_videoReceiver) {
232
        qgcApp()->showAppMessage(tr("Video receiver is not ready."));
233 234 235 236 237 238
        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) {
239
        qgcApp()->showAppMessage(tr("Invalid video format defined."));
240 241 242 243 244 245 246 247 248
        return;
    }

    //-- Disk usage maintenance
    _cleanupOldVideos();

    QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath();

    if(savePath.isEmpty()) {
249
        qgcApp()->showAppMessage(tr("Unabled to record video. Video save path must be specified in Settings."));
250 251 252 253 254 255 256 257
        return;
    }

    _videoFile = savePath + "/"
            + (videoFile.isEmpty() ? QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") : videoFile)
            + "." + kFileExtension[fileFormat - VideoReceiver::FILE_FORMAT_MIN];

    _videoReceiver->startRecording(_videoFile, fileFormat);
258 259 260
#else
    Q_UNUSED(videoFile)
#endif
261 262 263 264 265 266 267 268
}

void
VideoManager::stopRecording()
{
    if (qgcApp()->runningUnitTests()) {
        return;
    }
269
#if defined(QGC_GST_STREAMING)
270 271 272 273 274
    if (!_videoReceiver) {
        return;
    }

    _videoReceiver->stopRecording();
275
#endif
276 277 278 279 280 281 282 283
}

void
VideoManager::grabImage(const QString& imageFile)
{
    if (qgcApp()->runningUnitTests()) {
        return;
    }
284
#if defined(QGC_GST_STREAMING)
285 286 287 288 289 290 291 292 293
    if (!_videoReceiver) {
        return;
    }

    _imageFile = imageFile;

    emit imageFileChanged();

    _videoReceiver->takeScreenshot(_imageFile);
294 295 296
#else
    Q_UNUSED(imageFile)
#endif
297 298
}

299 300 301
//-----------------------------------------------------------------------------
double VideoManager::aspectRatio()
{
302
    if(_activeVehicle && _activeVehicle->dynamicCameras()) {
303
        QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
304 305 306 307 308
        if(pInfo) {
            qCDebug(VideoManagerLog) << "Primary AR: " << pInfo->aspectRatio();
            return pInfo->aspectRatio();
        }
    }
309
    // FIXME: AV: use _videoReceiver->videoSize() to calculate AR (if AR is not specified in the settings?)
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    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();
343 344 345 346 347 348 349
        if(pInfo) {
            return pInfo->aspectRatio();
        }
    }
    return _videoSettings->aspectRatio()->rawValue().toDouble();
}

350 351 352 353 354 355 356 357 358 359 360 361 362
//-----------------------------------------------------------------------------
bool
VideoManager::hasThermal()
{
    if(_activeVehicle && _activeVehicle->dynamicCameras()) {
        QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance();
        if(pInfo) {
            return true;
        }
    }
    return false;
}

363 364 365 366 367 368 369
//-----------------------------------------------------------------------------
QString
VideoManager::imageFile()
{
    return _imageFile;
}

370 371 372 373
//-----------------------------------------------------------------------------
bool
VideoManager::autoStreamConfigured()
{
Gus Grubba's avatar
Gus Grubba committed
374
#if defined(QGC_GST_STREAMING)
375
    if(_activeVehicle && _activeVehicle->dynamicCameras()) {
376 377 378
        QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
        if(pInfo) {
            return !pInfo->uri().isEmpty();
379 380
        }
    }
Gus Grubba's avatar
Gus Grubba committed
381
#endif
382
    return false;
383 384
}

Gus Grubba's avatar
Gus Grubba committed
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
//-----------------------------------------------------------------------------
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
}

403 404 405
//-----------------------------------------------------------------------------
void
VideoManager::_videoSourceChanged()
406
{
Gus Grubba's avatar
Gus Grubba committed
407
    _updateUVC();
408 409
    emit hasVideoChanged();
    emit isGStreamerChanged();
410
    emit isAutoStreamChanged();
411
    _restartVideo();
412 413
}

414 415 416
//-----------------------------------------------------------------------------
void
VideoManager::_udpPortChanged()
417
{
418
    _restartVideo();
419 420
}

421 422
//-----------------------------------------------------------------------------
void
423
VideoManager::_rtspUrlChanged()
424
{
425
    _restartVideo();
426 427
}

428 429 430 431
//-----------------------------------------------------------------------------
void
VideoManager::_tcpUrlChanged()
{
432
    _restartVideo();
433 434
}

435 436 437 438
//-----------------------------------------------------------------------------
void
VideoManager::_lowLatencyModeChanged()
{
DoinLakeFlyer's avatar
DoinLakeFlyer committed
439
    //restartVideo();
440 441
}

442 443 444 445
//-----------------------------------------------------------------------------
bool
VideoManager::hasVideo()
{
446 447 448
    if(autoStreamConfigured()) {
        return true;
    }
449
    QString videoSource = _videoSettings->videoSource()->rawValue().toString();
450
    return !videoSource.isEmpty() && videoSource != VideoSettings::videoSourceNoVideo && videoSource != VideoSettings::videoDisabled;
451 452 453 454 455 456 457
}

//-----------------------------------------------------------------------------
bool
VideoManager::isGStreamer()
{
#if defined(QGC_GST_STREAMING)
458
    QString videoSource = _videoSettings->videoSource()->rawValue().toString();
459
    return
Gus Grubba's avatar
Gus Grubba committed
460 461
        videoSource == VideoSettings::videoSourceUDPH264 ||
        videoSource == VideoSettings::videoSourceUDPH265 ||
462
        videoSource == VideoSettings::videoSourceRTSP ||
463
        videoSource == VideoSettings::videoSourceTCP ||
464 465
        videoSource == VideoSettings::videoSourceMPEGTS ||
        autoStreamConfigured();
466 467 468 469 470
#else
    return false;
#endif
}

471 472 473 474 475 476 477 478 479
//-----------------------------------------------------------------------------
#ifndef QGC_DISABLE_UVC
bool
VideoManager::uvcEnabled()
{
    return QCameraInfo::availableCameras().count() > 0;
}
#endif

480 481 482 483 484 485 486 487 488 489 490 491 492 493
//-----------------------------------------------------------------------------
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();
}

494 495 496 497
void
VideoManager::_initVideo()
{
#if defined(QGC_GST_STREAMING)
498 499 500
    QQuickItem* root = qgcApp()->mainRootWindow();

    if (root == nullptr) {
501
        qCDebug(VideoManagerLog) << "mainRootWindow() failed. No root window";
502 503 504 505 506
        return;
    }

    QQuickItem* widget = root->findChild<QQuickItem*>("videoContent");

507
    if (widget != nullptr && _videoReceiver != nullptr) {
508
        if ((_videoSink = qgcApp()->toolbox()->corePlugin()->createVideoSink(this, widget)) != nullptr) {
509 510
            _videoReceiver->startDecoding(_videoSink);
        } else {
511
            qCDebug(VideoManagerLog) << "createVideoSink() failed";
512
        }
513
    } else {
514
        qCDebug(VideoManagerLog) << "video receiver disabled";
515 516 517 518
    }

    widget = root->findChild<QQuickItem*>("thermalVideo");

519
    if (widget != nullptr && _thermalVideoReceiver != nullptr) {
520
        if ((_thermalVideoSink = qgcApp()->toolbox()->corePlugin()->createVideoSink(this, widget)) != nullptr) {
521 522
            _thermalVideoReceiver->startDecoding(_thermalVideoSink);
        } else {
523
            qCDebug(VideoManagerLog) << "createVideoSink() failed";
524
        }
525
    } else {
526
        qCDebug(VideoManagerLog) << "thermal video receiver disabled";
527
    }
528 529 530
#endif
}

531
//-----------------------------------------------------------------------------
532
bool
533
VideoManager::_updateSettings()
534
{
535
    if(!_videoSettings)
536
        return false;
537
    //-- Auto discovery
538
    if(_activeVehicle && _activeVehicle->dynamicCameras()) {
539
        QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
540
        if(pInfo) {
541
            bool status = false;
542
            qCDebug(VideoManagerLog) << "Configure primary stream: " << pInfo->uri();
543 544
            switch(pInfo->type()) {
                case VIDEO_STREAM_TYPE_RTSP:
545 546 547
                    if (status = _updateVideoUri(pInfo->uri())) {
                        _toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceRTSP);
                    }
548
                    break;
549
                case VIDEO_STREAM_TYPE_TCP_MPEG:
550 551 552
                    if (status = _updateVideoUri(pInfo->uri())) {
                        _toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceTCP);
                    }
553 554
                    break;
                case VIDEO_STREAM_TYPE_RTPUDP:
555 556 557
                    if (status = _updateVideoUri(QStringLiteral("udp://0.0.0.0:%1").arg(pInfo->uri()))) {
                        _toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceUDPH264);
                    }
558 559
                    break;
                case VIDEO_STREAM_TYPE_MPEG_TS_H264:
560 561 562
                    if (status = _updateVideoUri(QStringLiteral("mpegts://0.0.0.0:%1").arg(pInfo->uri()))) {
                        _toolbox->settingsManager()->videoSettings()->videoSource()->setRawValue(VideoSettings::videoSourceMPEGTS);
                    }
563 564
                    break;
                default:
565
                    status = _updateVideoUri(pInfo->uri());
566 567
                    break;
            }
568 569 570 571 572 573 574
            //-- 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:
575
                        status |= _updateThermalVideoUri(pTinfo->uri());
576 577
                        break;
                    case VIDEO_STREAM_TYPE_RTPUDP:
578
                        status |= _updateThermalVideoUri(QStringLiteral("udp://0.0.0.0:%1").arg(pTinfo->uri()));
579 580
                        break;
                    case VIDEO_STREAM_TYPE_MPEG_TS_H264:
581
                        status |= _updateThermalVideoUri(QStringLiteral("mpegts://0.0.0.0:%1").arg(pTinfo->uri()));
582 583
                        break;
                    default:
584
                        status |= _updateThermalVideoUri(pTinfo->uri());
585 586 587
                        break;
                }
            }
588
            return status;
589 590
        }
    }
591
    QString source = _videoSettings->videoSource()->rawValue().toString();
Gus Grubba's avatar
Gus Grubba committed
592
    if (source == VideoSettings::videoSourceUDPH264)
593
        return _updateVideoUri(QStringLiteral("udp://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt()));
Gus Grubba's avatar
Gus Grubba committed
594
    else if (source == VideoSettings::videoSourceUDPH265)
595
        return _updateVideoUri(QStringLiteral("udp265://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt()));
596
    else if (source == VideoSettings::videoSourceMPEGTS)
597
        return _updateVideoUri(QStringLiteral("mpegts://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt()));
598
    else if (source == VideoSettings::videoSourceRTSP)
599
        return _updateVideoUri(_videoSettings->rtspUrl()->rawValue().toString());
600
    else if (source == VideoSettings::videoSourceTCP)
601
        return _updateVideoUri(QStringLiteral("tcp://%1").arg(_videoSettings->tcpUrl()->rawValue().toString()));
602 603
}

604 605
bool
VideoManager::_updateVideoUri(const QString& uri)
606 607 608 609
{
#if defined(QGC_GST_TAISYNC_ENABLED) && (defined(__android__) || defined(__ios__))
    //-- Taisync on iOS or Android sends a raw h.264 stream
    if (isTaisync()) {
Andrew Voznytsa's avatar
Andrew Voznytsa committed
610
        return _updateVideoUri(QString("tsusb://0.0.0.0:%1").arg(TAISYNC_VIDEO_UDP_PORT));
611 612
    }
#endif
613 614 615 616
    if (uri == _videoUri) {
        return false;
    }

617
    _videoUri = uri;
618 619

    return true;
620 621
}

622
bool
623
VideoManager::_updateThermalVideoUri(const QString& uri)
624 625 626 627 628
{
#if defined(QGC_GST_TAISYNC_ENABLED) && (defined(__android__) || defined(__ios__))
    //-- Taisync on iOS or Android sends a raw h.264 stream
    if (isTaisync()) {
        // FIXME: AV: TAISYNC_VIDEO_UDP_PORT is used by video stream, thermal stream should go via its own proxy
Andrew Voznytsa's avatar
Andrew Voznytsa committed
629 630 631 632 633 634
        if (!_thermalVideoUri.isEmpty()) {
            _thermalVideoUri.clear();
            return true;
        } else {
            return false;
        }
635 636
    }
#endif
637 638
    if (uri == _thermalVideoUri) {
        return false;
639 640
    }

641
    _thermalVideoUri = uri;
642

643
    return true;
644 645
}

646
//-----------------------------------------------------------------------------
647
void
648
VideoManager::_restartVideo()
649
{
650
#if defined(QGC_GST_STREAMING)
651 652 653 654 655
    if (!_updateSettings()) {
        qCDebug(VideoManagerLog) << "No sense to restart video streaming, skipped";
        return;
    }

656
    qCDebug(VideoManagerLog) << "Restart video streaming";
657

658 659 660 661 662 663 664 665 666
    if (!_enableVideoRestart) {
        _enableVideoRestart = true;
        _startReceiver(0);
        _startReceiver(1);
    } else {
        _stopReceiver(1);
        _stopReceiver(0);
    }

667
//    emit aspectRatioChanged();
668 669
#endif
}
670

671 672 673 674 675 676 677 678 679 680 681
//-----------------------------------------------------------------------------
void
VideoManager::_recordingStarted()
{
    _subtitleWriter.startCapturingTelemetry(_videoFile);
}

//-----------------------------------------------------------------------------
void
VideoManager::_recordingChanged()
{
682
#if defined(QGC_GST_STREAMING)
683 684 685
    if (_videoReceiver && !_videoReceiver->recording()) {
        _subtitleWriter.stopCapturingTelemetry();
    }
686
#endif
687 688 689 690
}

//----------------------------------------------------------------------------------------
void
691
VideoManager::_onTakeScreenshotComplete(VideoReceiver::STATUS status)
692 693 694
{
}

695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
//----------------------------------------------------------------------------------------
void
VideoManager::_startReceiver(unsigned id)
{
#if defined(QGC_GST_STREAMING)
    const unsigned timeout = _videoSettings->rtspTimeout()->rawValue().toUInt();

    if (id == 0 && _videoReceiver != nullptr) {
        _videoReceiver->start(_videoUri, timeout);
        if (_videoSink != nullptr) {
            _videoReceiver->startDecoding(_videoSink);
        }
    } else if (id == 1 && _thermalVideoReceiver != nullptr) {
        _thermalVideoReceiver->start(_thermalVideoUri, timeout);
        if (_thermalVideoSink != nullptr) {
            _thermalVideoReceiver->startDecoding(_thermalVideoSink);
        }
    } else if (id > 1) {
        qCDebug(VideoManagerLog) << "Unsupported receiver id" << id;
    }
#endif
}

//----------------------------------------------------------------------------------------
void
VideoManager::_stopReceiver(unsigned id)
{
#if defined(QGC_GST_STREAMING)
    if (id == 0 && _videoReceiver != nullptr) {
        _videoReceiver->stop();
    } else if (id == 1 && _thermalVideoReceiver != nullptr){
        _thermalVideoReceiver->stop();
    } else if (id > 1) {
        qCDebug(VideoManagerLog) << "Unsupported receiver id" << id;
    }
#endif
}

733 734 735 736 737
//----------------------------------------------------------------------------------------
void
VideoManager::_setActiveVehicle(Vehicle* vehicle)
{
    if(_activeVehicle) {
738
        disconnect(_activeVehicle, &Vehicle::connectionLostChanged, this, &VideoManager::_connectionLostChanged);
739 740 741 742 743
        if(_activeVehicle->dynamicCameras()) {
            QGCCameraControl* pCamera = _activeVehicle->dynamicCameras()->currentCameraInstance();
            if(pCamera) {
                pCamera->stopStream();
            }
744
            disconnect(_activeVehicle->dynamicCameras(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartVideo);
745
        }
746 747 748
    }
    _activeVehicle = vehicle;
    if(_activeVehicle) {
749
        connect(_activeVehicle, &Vehicle::connectionLostChanged, this, &VideoManager::_connectionLostChanged);
750
        if(_activeVehicle->dynamicCameras()) {
751
            connect(_activeVehicle->dynamicCameras(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartVideo);
752 753 754 755
            QGCCameraControl* pCamera = _activeVehicle->dynamicCameras()->currentCameraInstance();
            if(pCamera) {
                pCamera->resumeStream();
            }
756
        }
757 758 759
    } else {
        //-- Disable full screen video if vehicle is gone
        setfullScreen(false);
760
    }
761
    emit autoStreamConfiguredChanged();
762
    _restartVideo();
763 764
}

765 766 767 768 769 770 771 772 773 774
//----------------------------------------------------------------------------------------
void
VideoManager::_connectionLostChanged(bool connectionLost)
{
    if(connectionLost) {
        //-- Disable full screen video if connection is lost
        setfullScreen(false);
    }
}

775 776
//----------------------------------------------------------------------------------------
void
777
VideoManager::_aspectRatioChanged()
778
{
779
    emit aspectRatioChanged();
780
}