QGC二次开发----转载

视频GUI代码分析
Gui代码的根:MainRootWindow.qml
配置文件的代码:

GeneralSetting.qml
	_videoSettings.videoSource
		settingmanager -> _videosettings
			videosetting.h -> DEFINE_SETTINGFACT(videoSource)

视频GUI代码

FlyView.qml			- 		MainRootWindow除了header(上面的toolbar部分)的部分是flyview

开启视频设置之后有一个小的窗口FlyViewVideo.qml
根据gstreamer状态分为FlightDisplayViewUVC,FlightDisplayViewVideo,前者是UVC摄像头,本地的设备,这一部分不走gstreamer处理,里面有qml的camera组建和videooutput组件

FlightDisplayViewVideo.qml是gsteamer处理的流的关键

	!gstreamer
		FlightDisplayViewUVC.qml
			camera.start()
		FlightDisplayViewDummy.qml
	gstreamer:
		FlightDisplayViewVideo.qml
		在这个qml中,有一个QGCVideoBackground组件,

QGCVideoBackground {
                id:             videoContent	
                objectName:     "videoContent"   // 标记

                Connections {
                    target: QGroundControl.videoManager
                    onImageFileChanged: {
                        videoContent.grabToImage(function(result) {
                            if (!result.saveToFile(QGroundControl.videoManager.imageFile)) {
                                console.error('Error capturing video frame');
                            }
                        });
                    }
                }
app->_initForNormalAppBoot()  // 在main.cc中,在app.exec()前面执行,用来做初始化
	// QGCApplication.cc
		bool QGCApplication::_initForNormalAppBoot()
			if (rootWindow) 
				rootWindow->scheduleRenderJob (new FinishVideoInitialization (toolbox()->videoManager()),QQuickWindow::BeforeSynchronizingStage);	
					scheduleRenderJob的用法,执行的代码是class里的run()函数部分的代码
		class FinishVideoInitialization : public QRunnable
			run(){	_manager->_initVideo(); }
				void VideoManager::_initVideo();	// 显示视频

_initVideo拿到了FlightDisplayViewVideo中的videoContent组件

void VideoManager::_initVideo()
	QQuickItem* widget = root->findChild<QQuickItem*>("videoContent");
		QGCVideoBackground.qml

视频业务代码分析

VideoSettings所在的路径

settings/settingsgroup.h

=> class SettingsGroup : public QObject
		QMap<QString, FactMetaData*> _nameToMetaDataMap;

settings/settingsgroup.cc

	_nameToMetaDataMap[videoSourceName]->setEnumInfo(videoSourceList,videoSourceVarList)

Video入口,类似LinkManager

int main(int argc, char *argv[])
->QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
   -> _toolbox = new QGCToolbox(this);
   ->  _toolbox->setChildToolboxes();
        -> _videoManager->setToolbox(this); // 核心函数 - 视频流入口
        // 设置了视频配置变化的监听信号
           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);

在Sources/VideoManager/VideoManager.cc下setToolbox函数中实例化了

_videoSettings = toolbox->settingsManager()->videoSettings(); // 拿到video配置
QString videoSource = _videoSettings->videoSource()->rawValue().toString(); // video URI
.....
_videoReceiver[0] = toolbox->corePlugin()->createVideoReceiver(this);
_videoReceiver[1] = toolbox->corePlugin()->createVideoReceiver(this);

createVideoReceiver跟下去是:

VideoReceiver* QGCCorePlugin::createVideoReceiver(QObject* parent)
	return GStreamer::createVideoReceiver(parent);
		VideoReceiver* new GstVideoReceiver(nullptr);
			class GstVideoReceiver : public VideoReceiver
			// GstVideoReceiver => /Sources/VideoManager/GstVideoReceiver.h
			// VideoReceiver => /Sources/VideoManager/VideoReceiver.h => QObject

Sources/VideoManager/VideoManager.cc下setToolbox函数:

    connect(_videoReceiver[0], &VideoReceiver::streamingChanged, this, [this](bool active){
        _streaming = active;
        emit streamingChanged();
    });

    connect(_videoReceiver[0], &VideoReceiver::onStartComplete, this, [this](VideoReceiver::STATUS status) {
        if (status == VideoReceiver::STATUS_OK) { // 信号:解码成功
            _videoStarted[0] = true;
            if (_videoSink[0] != nullptr) {
                // It is absolytely ok to have video receiver active (streaming) and decoding not active
                // It should be handy for cases when you have many streams and want to show only some of them
                // NOTE that even if decoder did not start it is still possible to record video
                _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();
    });
    // 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::onStartComplete, this, [this](VideoReceiver::STATUS status) {
            if (status == VideoReceiver::STATUS_OK) { // 信号:解码成功
                _videoStarted[1] = true;
                if (_videoSink[1] != nullptr) {
                    _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);
        });
    }

	_updateSettings(0);
    _updateSettings(1);
    if(isGStreamer()) {
        startVideo();
    } else {
        stopVideo();
    }

这里需要看gstreamer库,VideoReceiver是在gstreamer之上封装的

下面这个打开视频的小窗口,对应的代码在:Resources/qgroundcontrol.qrc/qml/FlightDisplay/FlyView.qml
具体视频部分对应的是FlyViewVideo.qml

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值