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