File indexing completed on 2024-05-12 05:53:13
0001 /* 0002 * Copyright 2020 Tomaz Canabrava <tcanabrava@kde.org> 0003 * 0004 * This program is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU General Public License as 0006 * published by the Free Software Foundation; either version 2 of 0007 * the License or (at your option) version 3 or any later version 0008 * accepted by the membership of KDE e.V. (or its successor approved 0009 * by the membership of KDE e.V.), which shall act as a proxy 0010 * defined in Section 14 of version 3 of the license. 0011 * 0012 * This program is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 * GNU General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU General Public License 0018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include "videosurface.h" 0022 #include "gstreamerintegration.h" 0023 0024 #include <gst/gst.h> 0025 0026 Q_LOGGING_CATEGORY(surfaceLogging, "kirogi.videosuppoert.videosurface") 0027 0028 VideoSurface::VideoSurface(QQuickItem *parent) 0029 : QQuickItem(parent) 0030 , _videoItem(nullptr) 0031 , _videoReceiver(nullptr) 0032 , _shouldStartVideo(false) 0033 , _shouldPauseVideo(false) 0034 { 0035 Q_INIT_RESOURCE(gstreamer); 0036 0037 // This flag is needed so the item will call updatePaintNode. 0038 setFlag(ItemHasContents); 0039 } 0040 0041 VideoSurface::~VideoSurface() 0042 { 0043 if (_videoReceiver) { 0044 auto *sink = _videoReceiver->videoSink(); 0045 auto *pipeline = _videoReceiver->pipeline(); 0046 0047 if (pipeline) { 0048 gst_element_set_state(pipeline, GST_STATE_NULL); 0049 } 0050 if (sink) { 0051 g_object_set(sink, "widget", nullptr, nullptr); 0052 } 0053 } 0054 } 0055 0056 void VideoSurface::pauseVideo() 0057 { 0058 _shouldPauseVideo = true; 0059 update(); 0060 } 0061 0062 void VideoSurface::startVideo() 0063 { 0064 createVideoItem(); 0065 0066 if (!_videoItem || !_videoReceiver) { 0067 qDebug() << "Can't start the video yet"; 0068 return; 0069 } 0070 0071 auto *pipeline = _videoReceiver->pipeline(); 0072 if (!pipeline) { 0073 qCDebug(surfaceLogging) << "It appears that we don't have a pipeline yet. Probably a misconfiguration in the vehicle plugins."; 0074 return; 0075 } 0076 0077 auto *sink = _videoReceiver->videoSink(); 0078 if (!sink) { 0079 qCDebug(surfaceLogging) << "Could not retrieve the video sink, can't start the video."; 0080 return; 0081 } 0082 0083 /* the qtqmlsink needs a property named 'Widget' to be set, 0084 * then it will send data to that widget directly without our intervention 0085 * this widget is the GstGlVideoItem we need to create in Qml 0086 */ 0087 GObject *videoSinkHasWidget = nullptr; 0088 g_object_get(sink, "widget", videoSinkHasWidget, nullptr); 0089 if (!videoSinkHasWidget) { 0090 g_object_set(sink, "widget", _videoItem, nullptr); 0091 } 0092 0093 _shouldStartVideo = true; 0094 update(); 0095 } 0096 0097 void VideoSurface::setVideoReceiver(GStreamerIntegration *videoReceiver) 0098 { 0099 if (!videoReceiver) { 0100 return; 0101 } 0102 0103 if (_videoReceiver != videoReceiver) { 0104 _videoReceiver = videoReceiver; 0105 Q_EMIT videoReceiverChanged(_videoReceiver); 0106 } 0107 startVideo(); 0108 } 0109 0110 GStreamerIntegration *VideoSurface::videoReceiver() const 0111 { 0112 return _videoReceiver; 0113 } 0114 0115 QSGNode *VideoSurface::updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) 0116 { 0117 Q_UNUSED(data) 0118 if (!_shouldStartVideo && !_shouldPauseVideo) { 0119 return node; 0120 } 0121 0122 Q_ASSERT_X(_videoReceiver, "video receiver", "No video receiver, and trying to start video."); 0123 auto *pipeline = _videoReceiver->pipeline(); 0124 Q_ASSERT_X(pipeline, "gstreamer pipeline", "No pipeline, and trying to start video"); 0125 0126 // Do not try to play an already playing pipeline, pause it first. 0127 if (_shouldStartVideo) { 0128 qCInfo(surfaceLogging) << "Setting the state to PLAYING"; 0129 gst_element_set_state(pipeline, GST_STATE_PLAYING); 0130 } else if (_shouldPauseVideo) { 0131 qCInfo(surfaceLogging) << "Setting the state to PAUSED"; 0132 gst_element_set_state(pipeline, GST_STATE_PAUSED); 0133 } 0134 _shouldStartVideo = false; 0135 _shouldPauseVideo = false; 0136 return node; 0137 } 0138 0139 bool VideoSurface::playing() const 0140 { 0141 return _playing; 0142 } 0143 0144 void VideoSurface::setPlaying(bool value) 0145 { 0146 if (_playing == value) { 0147 return; 0148 } 0149 _playing = value; 0150 if (_playing) { 0151 startVideo(); 0152 } else { 0153 pauseVideo(); 0154 } 0155 Q_EMIT playingChanged(_playing); 0156 } 0157 0158 void VideoSurface::createVideoItem() 0159 { 0160 // Video widget already exist, abort 0161 if (_videoItem) { 0162 return; 0163 } 0164 0165 QQmlEngine *engine = qmlEngine(this); 0166 if (!engine) { 0167 qCWarning(surfaceLogging) << "No qml engine to load visualization."; 0168 return; 0169 } 0170 0171 // Create GST video widget 0172 QQmlComponent component(engine, QStringLiteral("qrc:/video/GstVideo.qml"), this); 0173 _videoItem = qobject_cast<QQuickItem *>(component.create()); 0174 if (!_videoItem) { 0175 qCCritical(surfaceLogging) << "Failed to load QML component."; 0176 qCDebug(surfaceLogging) << "Component status:" << component.status(); 0177 if (component.isError()) { 0178 qCDebug(surfaceLogging) << "Error list:" << component.errors(); 0179 } 0180 return; 0181 } 0182 0183 // Set this item as parent to user anchors 0184 _videoItem->setParentItem(this); 0185 qvariant_cast<QObject *>(_videoItem->property("anchors"))->setProperty("fill", QVariant::fromValue<VideoSurface *>(this)); 0186 }