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 }