From 4c53be1c81fc02090b4c5bdb4ceec4da046fbe58 Mon Sep 17 00:00:00 2001
From: Andrew Voznytsa <andrew.voznytsa@gmail.com>
Date: Sat, 21 Dec 2019 21:49:51 +0200
Subject: [PATCH] Workaround gstreamer app blocking for TCP streaming services

---
 src/VideoStreaming/VideoReceiver.cc | 80 +++++++++++++++++++++++++++++++++++++
 src/VideoStreaming/VideoReceiver.h  |  7 ++++
 2 files changed, 87 insertions(+)

diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc
index 052e05b..d50fb1a 100644
--- a/src/VideoStreaming/VideoReceiver.cc
+++ b/src/VideoStreaming/VideoReceiver.cc
@@ -65,6 +65,9 @@ VideoReceiver::VideoReceiver(QObject* parent)
     , _pipelineStopRec(nullptr)
     , _videoSink(nullptr)
     , _restart_time_ms(1389)
+    , _socket(nullptr)
+    , _serverPresent(false)
+    , _tcpTestInterval_ms(5000)
     , _udpReconnect_us(5000000)
 #endif
     , _videoSurface(nullptr)
@@ -81,6 +84,8 @@ VideoReceiver::VideoReceiver(QObject* parent)
     _setVideoSink(_videoSurface->videoSink());
     _restart_timer.setSingleShot(true);
     connect(&_restart_timer, &QTimer::timeout, this, &VideoReceiver::_restart_timeout);
+    _tcp_timer.setSingleShot(true);
+    connect(&_tcp_timer, &QTimer::timeout, this, &VideoReceiver::_tcp_timeout);
     connect(this, &VideoReceiver::msgErrorReceived, this, &VideoReceiver::_handleError);
     connect(this, &VideoReceiver::msgEOSReceived, this, &VideoReceiver::_handleEOS);
     connect(this, &VideoReceiver::msgStateChangedReceived, this, &VideoReceiver::_handleStateChanged);
@@ -93,6 +98,10 @@ VideoReceiver::~VideoReceiver()
 {
 #if defined(QGC_GST_STREAMING)
     stop();
+    if(_socket) {
+        delete _socket;
+        _socket = nullptr;
+    }
     if (_videoSink) {
         gst_object_unref(_videoSink);
     }
@@ -150,6 +159,69 @@ VideoReceiver::_restart_timeout()
 #endif
 
 //-----------------------------------------------------------------------------
+#if defined(QGC_GST_STREAMING)
+void
+VideoReceiver::_tcp_timeout()
+{
+    //-- If socket is live, we got no connection nor a socket error
+    if(_socket) {
+        delete _socket;
+        _socket = nullptr;
+    }
+    if(_videoSettings->streamEnabled()->rawValue().toBool()) {
+        //-- RTSP will try to connect to the server. If it cannot connect,
+        //   it will simply give up and never try again. Instead, we keep
+        //   attempting a connection on this timer. Once a connection is
+        //   found to be working, only then we actually start the stream.
+        QUrl url(_uri);
+        //-- If RTSP and no port is defined, set default RTSP port (554)
+        if(_uri.contains("rtsp://") && url.port() <= 0) {
+            url.setPort(554);
+        }
+        _socket = new QTcpSocket;
+        QNetworkProxy tempProxy;
+        tempProxy.setType(QNetworkProxy::DefaultProxy);
+        _socket->setProxy(tempProxy);
+        connect(_socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QTcpSocket::error), this, &VideoReceiver::_socketError);
+        connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected);
+        _socket->connectToHost(url.host(), static_cast<uint16_t>(url.port()));
+        _tcp_timer.start(_tcpTestInterval_ms);
+    }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+#if defined(QGC_GST_STREAMING)
+void
+VideoReceiver::_connected()
+{
+    //-- Server showed up. Now we start the stream.
+    _tcp_timer.stop();
+    _socket->deleteLater();
+    _socket = nullptr;
+    if(_videoSettings->streamEnabled()->rawValue().toBool()) {
+        _serverPresent = true;
+        start();
+    }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+#if defined(QGC_GST_STREAMING)
+void
+VideoReceiver::_socketError(QAbstractSocket::SocketError socketError)
+{
+    Q_UNUSED(socketError);
+    _socket->deleteLater();
+    _socket = nullptr;
+    //-- Try again in a while
+    if(_videoSettings->streamEnabled()->rawValue().toBool()) {
+        _tcp_timer.start(_tcpTestInterval_ms);
+    }
+}
+#endif
+
+//-----------------------------------------------------------------------------
 // When we finish our pipeline will look like this:
 //
 //                                   +-->queue-->decoder-->_videosink
@@ -184,6 +256,7 @@ VideoReceiver::start()
     bool isTaisyncUSB = false;
 #endif
     bool isUdp264   = _uri.contains("udp://")  && !isTaisyncUSB;
+    bool isRtsp     = _uri.contains("rtsp://") && !isTaisyncUSB;
     bool isUdp265   = _uri.contains("udp265://")  && !isTaisyncUSB;
     bool isTCP      = _uri.contains("tcp://")  && !isTaisyncUSB;
     bool isMPEGTS   = _uri.contains("mpegts://")  && !isTaisyncUSB;
@@ -208,6 +281,12 @@ VideoReceiver::start()
 
     _starting = true;
 
+    //-- For RTSP and TCP, check to see if server is there first
+    if(!_serverPresent && (isRtsp || isTCP)) {
+        _tcp_timer.start(100);
+        return;
+    }
+
     bool running    = false;
     bool pipelineUp = false;
 
@@ -477,6 +556,7 @@ VideoReceiver::_shutdownPipeline() {
     _pipeline = nullptr;
     delete _sink;
     _sink = nullptr;
+    _serverPresent = false;
     _streaming = false;
     _recording = false;
     _stopping = false;
diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h
index 882e6e9..1203c29 100644
--- a/src/VideoStreaming/VideoReceiver.h
+++ b/src/VideoStreaming/VideoReceiver.h
@@ -93,6 +93,9 @@ protected slots:
     virtual void _updateTimer               ();
 #if defined(QGC_GST_STREAMING)
     virtual void _restart_timeout           ();
+    virtual void _tcp_timeout               ();
+    virtual void _connected                 ();
+    virtual void _socketError               (QAbstractSocket::SocketError socketError);
     virtual void _handleError               ();
     virtual void _handleEOS                 ();
     virtual void _handleStateChanged        ();
@@ -138,6 +141,10 @@ protected:
     QTimer          _frameTimer;
     QTimer          _restart_timer;
     int             _restart_time_ms;
+    QTimer          _tcp_timer;
+    QTcpSocket*     _socket;
+    bool            _serverPresent;
+    int             _tcpTestInterval_ms;
 
     //-- RTSP UDP reconnect timeout
     uint64_t        _udpReconnect_us;