GstVideoReceiver.h 5.46 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
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
Gus Grubba's avatar
Gus Grubba committed
9 10 11 12

/**
 * @file
 *   @brief QGC Video Receiver
Gus Grubba's avatar
Gus Grubba committed
13
 *   @author Gus Grubba <gus@auterion.com>
Gus Grubba's avatar
Gus Grubba committed
14 15
 */

16
#pragma once
Gus Grubba's avatar
Gus Grubba committed
17

18
#include "QGCLoggingCategory.h"
19
#include <QTimer>
20 21 22 23
#include <QThread>
#include <QWaitCondition>
#include <QMutex>
#include <QQueue>
24 25 26
#include <QQuickItem>

#include "VideoReceiver.h"
27

Gus Grubba's avatar
Gus Grubba committed
28 29
#include <gst/gst.h>

30 31
Q_DECLARE_LOGGING_CATEGORY(VideoReceiverLog)

32
class Worker : public QThread
Gus Grubba's avatar
Gus Grubba committed
33 34
{
    Q_OBJECT
35

36
public:
37 38
    bool needDispatch() {
        return QThread::currentThread() != this;
39 40
    }

41 42 43 44
    void dispatch(std::function<void()> t) {
        QMutexLocker lock(&_taskQueueSync);
        _taskQueue.enqueue(t);
        _taskQueueUpdate.wakeOne();
45 46
    }

47 48 49 50 51 52 53 54 55
    void shutdown() {
        if (needDispatch()) {
            dispatch([this](){
                _shutdown = true;
            });
            QThread::wait();
        } else {
            QThread::terminate();
        }
56 57
    }

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
protected:
    void run() {
        while(!_shutdown) {
            _taskQueueSync.lock();

            while (_taskQueue.isEmpty()) {
                _taskQueueUpdate.wait(&_taskQueueSync);
            }

            Task t = _taskQueue.dequeue();

            _taskQueueSync.unlock();

            t();
        }
73
    }
74

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
private:
    typedef std::function<void()> Task;
    QWaitCondition      _taskQueueUpdate;
    QMutex              _taskQueueSync;
    QQueue<Task>        _taskQueue;
    bool                _shutdown = false;
};

class GstVideoReceiver : public VideoReceiver
{
    Q_OBJECT

public:
    explicit GstVideoReceiver(QObject* parent = nullptr);
    ~GstVideoReceiver(void);
90

91
public slots:
92 93
    virtual void start(const QString& uri, unsigned timeout);
    virtual void stop(void);
94
    virtual void startDecoding(void* sink);
95 96 97 98
    virtual void stopDecoding(void);
    virtual void startRecording(const QString& videoFile, FILE_FORMAT format);
    virtual void stopRecording(void);
    virtual void takeScreenshot(const QString& imageFile);
99

100 101 102
protected slots:
    virtual void _watchdog(void);
    virtual void _handleEOS(void);
Gus Grubba's avatar
Gus Grubba committed
103

104
protected:
105 106 107 108 109 110 111 112 113 114 115
    virtual GstElement* _makeSource(const QString& uri);
    virtual GstElement* _makeDecoder(GstCaps* caps, GstElement* videoSink);
    virtual GstElement* _makeFileSink(const QString& videoFile, FILE_FORMAT format);

    virtual void _onNewSourcePad(GstPad* pad);
    virtual void _onNewDecoderPad(GstPad* pad);
    virtual bool _addDecoder(GstElement* src);
    virtual bool _addVideoSink(GstPad* pad);
    virtual void _noteTeeFrame(void);
    virtual void _noteVideoSinkFrame(void);
    virtual void _noteEndOfStream(void);
116
    virtual bool _unlinkBranch(GstElement* from);
117 118 119
    virtual void _shutdownDecodingBranch (void);
    virtual void _shutdownRecordingBranch(void);

120 121
    bool _needDispatch(void);
    void _dispatchSignal(std::function<void()> emitter);
122

123 124 125 126 127 128
private:
    static gboolean _onBusMessage(GstBus* bus, GstMessage* message, gpointer user_data);
    static void _onNewPad(GstElement* element, GstPad* pad, gpointer data);
    static void _wrapWithGhostPad(GstElement* element, GstPad* pad, gpointer data);
    static void _linkPadWithOptionalBuffer(GstElement* element, GstPad* pad, gpointer data);
    static gboolean _padProbe(GstElement* element, GstPad* pad, gpointer user_data);
129
    static gboolean _filterParserCaps(GstElement* bin, GstPad* pad, GstElement* element, GstQuery* query, gpointer data);
130 131 132 133 134 135 136 137
    static gboolean _autoplugQueryCaps(GstElement* bin, GstPad* pad, GstElement* element, GstQuery* query, gpointer data);
    static gboolean _autoplugQueryContext(GstElement* bin, GstPad* pad, GstElement* element, GstQuery* query, gpointer data);
    static gboolean _autoplugQuery(GstElement* bin, GstPad* pad, GstElement* element, GstQuery* query, gpointer data);
    static GstPadProbeReturn _teeProbe(GstPad* pad, GstPadProbeInfo* info, gpointer user_data);
    static GstPadProbeReturn _videoSinkProbe(GstPad* pad, GstPadProbeInfo* info, gpointer user_data);
    static GstPadProbeReturn _eosProbe(GstPad* pad, GstPadProbeInfo* info, gpointer user_data);
    static GstPadProbeReturn _keyframeWatch(GstPad* pad, GstPadProbeInfo* info, gpointer user_data);

138 139 140
    bool                _streaming;
    bool                _decoding;
    bool                _recording;
141 142 143
    bool                _removingDecoder;
    bool                _removingRecorder;
    GstElement*         _source;
144
    GstElement*         _tee;
145 146 147 148 149 150
    GstElement*         _decoderValve;
    GstElement*         _recorderValve;
    GstElement*         _decoder;
    GstElement*         _videoSink;
    GstElement*         _fileSink;
    GstElement*         _pipeline;
151

152 153 154 155
    qint64              _lastSourceFrameTime;
    qint64              _lastVideoFrameTime;
    bool                _resetVideoSink;
    gulong              _videoSinkProbeId;
156

157
    QTimer              _watchdogTimer;
158

159 160
    //-- RTSP UDP reconnect timeout
    uint64_t            _udpReconnect_us;
161

162
    unsigned            _timeout;
Gus Grubba's avatar
Gus Grubba committed
163

164 165
    Worker              _slotHandler;
    uint32_t            _signalDepth;
166

167
    bool                _endOfStream;
168

169
    static const char*  _kFileMux[FILE_FORMAT_MAX - FILE_FORMAT_MIN];
Gus Grubba's avatar
Gus Grubba committed
170
};
171 172 173 174

void* createVideoSink(void* widget);

void initializeVideoReceiver(int argc, char* argv[], int debuglevel);