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
81{
82 using namespace std::chrono_literals;
83
84 if (sink == m_gstVideoSink)
85 return;
86
87 m_gstVideoSink = sink;
88 if (m_gstVideoSink) {
89 m_gstVideoSink->setActive(m_isActive);
90 if (m_nativeSize.isValid())
91 m_gstVideoSink->setNativeSize(m_nativeSize);
92 }
93 QGstElement videoSink;
94 if (m_gstVideoSink) {
95 videoSink = m_gstVideoSink->gstSink();
96 } else {
97 videoSink = QGstElement::createFromFactory("fakesink", "fakevideosink");
98 Q_ASSERT(videoSink);
99 videoSink.set("sync", true);
100 }
101
102 QObject::disconnect(m_subtitleConnection);
103 if (sink) {
104 m_subtitleConnection = QObject::connect(this, &QGstreamerVideoOutput::subtitleChanged, sink,
105 [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([&] {
115 if (m_videoSink)
116 m_outputBin.stopAndRemoveElements(m_videoSink);
117
118 m_videoSink = std::move(videoSink);
119 m_outputBin.add(m_videoSink);
120
121 qLinkGstElements(m_videoConvertScale, m_videoSink);
122
123 GstEvent *event = gst_event_new_reconfigure();
124 gst_element_send_event(m_videoSink.element(), event);
125 m_videoSink.syncStateWithParent();
126 });
127
128 qCDebug(qLcMediaVideoOutput) << "sinkChanged" << m_videoSink.name();
129 m_videoConvertScale.dumpPipelineGraph(m_videoSink.name().constData());
130}
131
133{
134 if (m_isActive == isActive)
135 return;
136
137 m_isActive = isActive;
138 if (m_gstVideoSink)
139 m_gstVideoSink->setActive(isActive);
140}
141
142void QGstreamerVideoOutput::updateNativeSize()
143{
144 if (!m_gstVideoSink)
145 return;
146
147 m_gstVideoSink->setNativeSize(qRotatedFrameSize(m_nativeSize, m_rotation));
148}
149
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("leaky", 2 /*downstream*/);
155 m_videoQueue.set("silent", true);
156 m_videoQueue.set("max-size-buffers", int(1));
157 m_videoQueue.set("max-size-bytes", unsigned(0));
158 m_videoQueue.set("max-size-time", uint64_t(0));
159}
160
162{
163 if (m_subtitleSink) {
164 auto pad = m_subtitleSink.staticPad("sink");
165 auto *event = gst_event_new_flush_start();
166 pad.sendEvent(event);
167 event = gst_event_new_flush_stop(false);
168 pad.sendEvent(event);
169 }
170}
171
173{
174 m_nativeSize = sz;
175 updateNativeSize();
176}
177
178void QGstreamerVideoOutput::setRotation(QtVideo::Rotation rot)
179{
180 m_rotation = rot;
181 updateNativeSize();
182}
183
185{
186 // GStreamer thread
187
188 QMetaObject::invokeMethod(this, [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"
QGstElement & operator=(QGstElement &&) noexcept=default
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:956
void setVideoSink(QGstreamerRelayVideoSink *sink)
void updateSubtitle(QString) override
void setRotation(QtVideo::Rotation)
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")