1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtMultimedia/qvideosink.h>
5
6#include <QtCore/qloggingcategory.h>
7
8#include <common/qgstreamervideooutput_p.h>
9#include <common/qgstreamervideosink_p.h>
10#include <common/qgstsubtitlesink_p.h>
11
12static Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput")
13
14QT_BEGIN_NAMESPACE
15
16static QGstElement makeVideoConvertScale(const char *name)
17{
18 QGstElementFactoryHandle factory = QGstElement::findFactory("videoconvertscale");
19 if (factory) // videoconvertscale is only available in gstreamer 1.20
20 return QGstElement::createFromFactory(factory, name);
21
22 return QGstBin::createFromPipelineDescription(pipelineDescription: "videoconvert ! videoscale", name,
23 /*ghostUnlinkedPads=*/true);
24}
25
26QMaybe<QGstreamerVideoOutput *> QGstreamerVideoOutput::create(QObject *parent)
27{
28 QGstElementFactoryHandle factory = QGstElement::findFactory("videoconvertscale");
29
30 static std::optional<QString> elementCheck = []() -> std::optional<QString> {
31 std::optional<QString> error = qGstErrorMessageIfElementsNotAvailable(arg: "fakesink", args: "queue");
32 if (error)
33 return error;
34
35 QGstElementFactoryHandle factory = QGstElement::findFactory("videoconvertscale");
36 if (factory)
37 return std::nullopt;
38
39 return qGstErrorMessageIfElementsNotAvailable(arg: "videoconvert", args: "videoscale");
40 }();
41
42 if (elementCheck)
43 return *elementCheck;
44
45 return new QGstreamerVideoOutput(parent);
46}
47
48QGstreamerVideoOutput::QGstreamerVideoOutput(QObject *parent)
49 : QObject(parent),
50 m_outputBin{
51 QGstBin::create(name: "videoOutput"),
52 },
53 m_videoQueue{
54 QGstElement::createFromFactory(factory: "queue", name: "videoQueue"),
55 },
56 m_videoConvertScale{
57 makeVideoConvertScale(name: "videoConvertScale"),
58 },
59 m_videoSink{
60 QGstElement::createFromFactory(factory: "fakesink", name: "fakeVideoSink"),
61 }
62{
63 m_videoSink.set(property: "sync", b: true);
64
65 m_outputBin.add(ts: m_videoQueue, ts: m_videoConvertScale, ts: m_videoSink);
66 qLinkGstElements(ts: m_videoQueue, ts: m_videoConvertScale, ts: m_videoSink);
67
68 m_subtitleSink = QGstSubtitleSink::createSink(observer: this);
69
70 m_outputBin.addGhostPad(child: m_videoQueue, name: "sink");
71}
72
73QGstreamerVideoOutput::~QGstreamerVideoOutput()
74{
75 QObject::disconnect(m_subtitleConnection);
76 m_outputBin.setStateSync(state: GST_STATE_NULL);
77}
78
79void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink)
80{
81 using namespace std::chrono_literals;
82
83 auto *gstSink = sink ? static_cast<QGstreamerVideoSink *>(sink->platformVideoSink()) : nullptr;
84 if (gstSink == m_platformVideoSink)
85 return;
86
87 m_platformVideoSink = gstSink;
88 if (m_platformVideoSink) {
89 m_platformVideoSink->setActive(m_isActive);
90 if (m_nativeSize.isValid())
91 m_platformVideoSink->setNativeSize(m_nativeSize);
92 }
93 QGstElement videoSink;
94 if (m_platformVideoSink) {
95 videoSink = m_platformVideoSink->gstSink();
96 } else {
97 videoSink = QGstElement::createFromFactory(factory: "fakesink", name: "fakevideosink");
98 Q_ASSERT(videoSink);
99 videoSink.set(property: "sync", b: true);
100 }
101
102 QObject::disconnect(m_subtitleConnection);
103 if (sink) {
104 m_subtitleConnection = QObject::connect(sender: this, signal: &QGstreamerVideoOutput::subtitleChanged, context: sink,
105 slot: [sink](const QString &subtitle) {
106 sink->setSubtitleText(subtitle);
107 });
108 sink->setSubtitleText(m_lastSubtitleString);
109 }
110
111 if (m_videoSink == videoSink)
112 return;
113
114 m_videoConvertScale.src().modifyPipelineInIdleProbe(f: [&] {
115 if (m_videoSink)
116 m_outputBin.stopAndRemoveElements(ts&: m_videoSink);
117
118 m_videoSink = std::move(videoSink);
119 m_outputBin.add(ts: m_videoSink);
120
121 qLinkGstElements(ts: m_videoConvertScale, ts: m_videoSink);
122
123 GstEvent *event = gst_event_new_reconfigure();
124 gst_element_send_event(element: m_videoSink.element(), event);
125 m_videoSink.syncStateWithParent();
126 });
127
128 qCDebug(qLcMediaVideoOutput) << "sinkChanged" << m_videoSink.name();
129 m_videoConvertScale.dumpPipelineGraph(filename: m_videoSink.name().constData());
130}
131
132void QGstreamerVideoOutput::setActive(bool isActive)
133{
134 if (m_isActive == isActive)
135 return;
136
137 m_isActive = isActive;
138 if (m_platformVideoSink)
139 m_platformVideoSink->setActive(isActive);
140}
141
142void QGstreamerVideoOutput::updateNativeSize()
143{
144 if (!m_platformVideoSink)
145 return;
146
147 m_platformVideoSink->setNativeSize(qRotatedFrameSize(size: m_nativeSize, rotation: m_rotation));
148}
149
150void QGstreamerVideoOutput::setIsPreview()
151{
152 // configures the queue to be fast and lightweight for camera preview
153 // also avoids blocking the queue in case we have an encodebin attached to the tee as well
154 m_videoQueue.set(property: "leaky", i: 2 /*downstream*/);
155 m_videoQueue.set(property: "silent", b: true);
156 m_videoQueue.set(property: "max-size-buffers", i: int(1));
157 m_videoQueue.set(property: "max-size-bytes", i: unsigned(0));
158 m_videoQueue.set(property: "max-size-time", i: uint64_t(0));
159}
160
161void QGstreamerVideoOutput::flushSubtitles()
162{
163 if (!m_subtitleSink.isNull()) {
164 auto pad = m_subtitleSink.staticPad(name: "sink");
165 auto *event = gst_event_new_flush_start();
166 pad.sendEvent(event);
167 event = gst_event_new_flush_stop(reset_time: false);
168 pad.sendEvent(event);
169 }
170}
171
172void QGstreamerVideoOutput::setNativeSize(QSize sz)
173{
174 m_nativeSize = sz;
175 updateNativeSize();
176}
177
178void QGstreamerVideoOutput::setRotation(QtVideo::Rotation rot)
179{
180 m_rotation = rot;
181 updateNativeSize();
182}
183
184void QGstreamerVideoOutput::updateSubtitle(QString string)
185{
186 // GStreamer thread
187
188 QMetaObject::invokeMethod(object: this, function: [this, string = std::move(string)]() mutable {
189 m_lastSubtitleString = string;
190 Q_EMIT subtitleChanged(std::move(string));
191 });
192}
193
194QT_END_NAMESPACE
195
196#include "moc_qgstreamervideooutput_p.cpp"
197

source code of qtmultimedia/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp