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 | #include <qgstreamervideosink_p.h> |
6 | #include <qgstsubtitlesink_p.h> |
7 | #include <qvideosink.h> |
8 | |
9 | #include <QtCore/qloggingcategory.h> |
10 | #include <qthread.h> |
11 | |
12 | static Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput" ) |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | QMaybe<QGstreamerVideoOutput *> QGstreamerVideoOutput::create(QObject *parent) |
17 | { |
18 | QGstElement videoConvert("videoconvert" , "videoConvert" ); |
19 | if (!videoConvert) |
20 | return errorMessageCannotFindElement(element: "videoconvert" ); |
21 | |
22 | QGstElement videoSink("fakesink" , "fakeVideoSink" ); |
23 | if (!videoSink) |
24 | return errorMessageCannotFindElement(element: "fakesink" ); |
25 | |
26 | return new QGstreamerVideoOutput(videoConvert, videoSink, parent); |
27 | } |
28 | |
29 | QGstreamerVideoOutput::QGstreamerVideoOutput(QGstElement convert, QGstElement sink, |
30 | QObject *parent) |
31 | : QObject(parent), |
32 | gstVideoOutput("videoOutput" ), |
33 | videoConvert(std::move(convert)), |
34 | videoSink(std::move(sink)) |
35 | { |
36 | videoQueue = QGstElement("queue" , "videoQueue" ); |
37 | videoSink.set(property: "sync" , b: true); |
38 | gstVideoOutput.add(e1: videoQueue, e2: videoConvert, e3: videoSink); |
39 | if (!videoQueue.link(n1: videoConvert, n2: videoSink)) |
40 | qCDebug(qLcMediaVideoOutput) << ">>>>>> linking failed" ; |
41 | |
42 | gstVideoOutput.addGhostPad(child: videoQueue, name: "sink" ); |
43 | } |
44 | |
45 | QGstreamerVideoOutput::~QGstreamerVideoOutput() |
46 | { |
47 | gstVideoOutput.setStateSync(GST_STATE_NULL); |
48 | } |
49 | |
50 | void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink) |
51 | { |
52 | auto *gstVideoSink = sink ? static_cast<QGstreamerVideoSink *>(sink->platformVideoSink()) : nullptr; |
53 | if (gstVideoSink == m_videoSink) |
54 | return; |
55 | |
56 | if (m_videoSink) |
57 | m_videoSink->setPipeline({}); |
58 | |
59 | m_videoSink = gstVideoSink; |
60 | if (m_videoSink) |
61 | m_videoSink->setPipeline(gstPipeline); |
62 | |
63 | QGstElement gstSink; |
64 | if (m_videoSink) { |
65 | gstSink = m_videoSink->gstSink(); |
66 | isFakeSink = false; |
67 | } else { |
68 | gstSink = QGstElement("fakesink" , "fakevideosink" ); |
69 | Q_ASSERT(gstSink); |
70 | gstSink.set(property: "sync" , b: true); |
71 | isFakeSink = true; |
72 | } |
73 | |
74 | if (videoSink == gstSink) |
75 | return; |
76 | |
77 | gstPipeline.beginConfig(); |
78 | if (!videoSink.isNull()) { |
79 | gstVideoOutput.remove(element: videoSink); |
80 | videoSink.setStateSync(GST_STATE_NULL); |
81 | } |
82 | videoSink = gstSink; |
83 | gstVideoOutput.add(element: videoSink); |
84 | |
85 | videoConvert.link(next: videoSink); |
86 | GstEvent *event = gst_event_new_reconfigure(); |
87 | gst_element_send_event(element: videoSink.element(), event); |
88 | videoSink.syncStateWithParent(); |
89 | |
90 | doLinkSubtitleStream(); |
91 | |
92 | gstPipeline.endConfig(); |
93 | |
94 | qCDebug(qLcMediaVideoOutput) << "sinkChanged" << gstSink.name(); |
95 | |
96 | GST_DEBUG_BIN_TO_DOT_FILE(gstPipeline.bin(), |
97 | GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL |*/ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | |
98 | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES), |
99 | videoSink.name()); |
100 | |
101 | } |
102 | |
103 | void QGstreamerVideoOutput::setPipeline(const QGstPipeline &pipeline) |
104 | { |
105 | gstPipeline = pipeline; |
106 | if (m_videoSink) |
107 | m_videoSink->setPipeline(gstPipeline); |
108 | } |
109 | |
110 | void QGstreamerVideoOutput::linkSubtitleStream(QGstElement src) |
111 | { |
112 | qCDebug(qLcMediaVideoOutput) << "link subtitle stream" << src.isNull(); |
113 | if (src == subtitleSrc) |
114 | return; |
115 | |
116 | gstPipeline.beginConfig(); |
117 | subtitleSrc = src; |
118 | doLinkSubtitleStream(); |
119 | gstPipeline.endConfig(); |
120 | } |
121 | |
122 | void QGstreamerVideoOutput::unlinkSubtitleStream() |
123 | { |
124 | if (subtitleSrc.isNull()) |
125 | return; |
126 | qCDebug(qLcMediaVideoOutput) << "unlink subtitle stream" ; |
127 | subtitleSrc = {}; |
128 | if (!subtitleSink.isNull()) { |
129 | gstPipeline.beginConfig(); |
130 | gstPipeline.remove(element: subtitleSink); |
131 | gstPipeline.endConfig(); |
132 | subtitleSink.setStateSync(GST_STATE_NULL); |
133 | subtitleSink = {}; |
134 | } |
135 | if (m_videoSink) |
136 | m_videoSink->setSubtitleText({}); |
137 | } |
138 | |
139 | void QGstreamerVideoOutput::doLinkSubtitleStream() |
140 | { |
141 | if (!subtitleSink.isNull()) { |
142 | gstPipeline.remove(element: subtitleSink); |
143 | subtitleSink.setStateSync(GST_STATE_NULL); |
144 | subtitleSink = {}; |
145 | } |
146 | if (!m_videoSink || subtitleSrc.isNull()) |
147 | return; |
148 | if (subtitleSink.isNull()) { |
149 | subtitleSink = m_videoSink->subtitleSink(); |
150 | gstPipeline.add(element: subtitleSink); |
151 | } |
152 | if (!subtitleSrc.link(next: subtitleSink)) |
153 | qCDebug(qLcMediaVideoOutput) << "link subtitle stream failed" ; |
154 | } |
155 | |
156 | void QGstreamerVideoOutput::setIsPreview() |
157 | { |
158 | // configures the queue to be fast and lightweight for camera preview |
159 | // also avoids blocking the queue in case we have an encodebin attached to the tee as well |
160 | videoQueue.set(property: "leaky" , i: 2 /*downstream*/); |
161 | videoQueue.set(property: "silent" , b: true); |
162 | videoQueue.set(property: "max-size-buffers" , i: uint(1)); |
163 | videoQueue.set(property: "max-size-bytes" , i: uint(0)); |
164 | videoQueue.set(property: "max-size-time" , i: quint64(0)); |
165 | } |
166 | |
167 | void QGstreamerVideoOutput::flushSubtitles() |
168 | { |
169 | if (!subtitleSink.isNull()) { |
170 | auto pad = subtitleSink.staticPad(name: "sink" ); |
171 | auto *event = gst_event_new_flush_start(); |
172 | pad.sendEvent(event); |
173 | event = gst_event_new_flush_stop(reset_time: false); |
174 | pad.sendEvent(event); |
175 | } |
176 | } |
177 | |
178 | QT_END_NAMESPACE |
179 | |
180 | #include "moc_qgstreamervideooutput_p.cpp" |
181 | |