视频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