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 "qgstreamervideooutput_p.h"
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(pipelineDescription: "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(arg: "fakesink", args: "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(arg: "videoconvert", args: "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(name: "videoOutput"),
53 },
54 m_videoQueue{
55 QGstElement::createFromFactory(factory: "queue", name: "videoQueue"),
56 },
57 m_videoConvertScale{
58 makeVideoConvertScale(name: "videoConvertScale"),
59 },
60 m_videoSink{
61 QGstElement::createFromFactory(factory: "fakesink", name: "fakeVideoSink"),
62 }
63{
64 m_videoSink.set(property: "sync", b: true);
65
66 m_outputBin.add(ts: m_videoQueue, ts: m_videoConvertScale, ts: m_videoSink);
67 qLinkGstElements(ts: m_videoQueue, ts: m_videoConvertScale, ts: m_videoSink);
68
69 m_subtitleSink = QGstSubtitleSink::createSink(observer: this);
70
71 m_outputBin.addGhostPad(child: m_videoQueue, name: "sink");
72}
73
74QGstreamerVideoOutput::~QGstreamerVideoOutput()
75{
76 QObject::disconnect(m_subtitleConnection);
77 m_outputBin.setStateSync(state: 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(factory: "fakesink", name: "fakevideosink");
99 Q_ASSERT(videoSink);
100 videoSink.set(property: "sync", b: true);
101 }
102
103 QObject::disconnect(m_subtitleConnection);
104 if (sink) {
105 m_subtitleConnection = QObject::connect(sender: this, signal: &QGstreamerVideoOutput::subtitleChanged, context: sink,
106 slot: [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(f: [&] {
116 if (m_videoSink)
117 m_outputBin.stopAndRemoveElements(ts&: m_videoSink);
118
119 m_videoSink = std::move(videoSink);
120 m_outputBin.add(ts: m_videoSink);
121
122 qLinkGstElements(ts: m_videoConvertScale, ts: m_videoSink);
123
124 GstEvent *event = gst_event_new_reconfigure();
125 gst_element_send_event(element: m_videoSink.element(), event);
126 m_videoSink.syncStateWithParent();
127 });
128
129 qCDebug(qLcMediaVideoOutput) << "sinkChanged" << m_videoSink.name();
130 m_videoConvertScale.dumpPipelineGraph(filename: m_videoSink.name().constData());
131}
132
133void QGstreamerVideoOutput::setActive(bool isActive)
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(size: m_nativeSize, rotation: m_rotation));
149}
150
151void QGstreamerVideoOutput::setIsPreview()
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(property: "leaky", i: 2 /*downstream*/);
156 m_videoQueue.set(property: "silent", b: true);
157 m_videoQueue.set(property: "max-size-buffers", i: int(1));
158 m_videoQueue.set(property: "max-size-bytes", i: unsigned(0));
159 m_videoQueue.set(property: "max-size-time", i: uint64_t(0));
160}
161
162void QGstreamerVideoOutput::flushSubtitles()
163{
164 if (m_subtitleSink) {
165 auto pad = m_subtitleSink.staticPad(name: "sink");
166 auto *event = gst_event_new_flush_start();
167 pad.sendEvent(event);
168 event = gst_event_new_flush_stop(reset_time: false);
169 pad.sendEvent(event);
170 }
171}
172
173void QGstreamerVideoOutput::setNativeSize(QSize sz)
174{
175 m_nativeSize = sz;
176 updateNativeSize();
177}
178
179void QGstreamerVideoOutput::setRotation(QtVideo::Rotation rot)
180{
181 m_rotation = rot;
182 updateNativeSize();
183}
184
185void QGstreamerVideoOutput::updateSubtitle(QString string)
186{
187 // GStreamer thread
188
189 QMetaObject::invokeMethod(object: this, function: [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"
198

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