Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
qgstreamermediacapture.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
8#include <qgstpipeline_p.h>
9
13
14#include <qloggingcategory.h>
15
17
19{
20 if (tee.isNull() || sink.isNull())
21 return;
22
23 auto source = tee.getRequestPad("src_%u");
25}
26
28{
29 if (tee.isNull() || sink.isNull())
30 return;
31
32 auto source = sink.peer();
33 source.unlink(sink);
34
36}
37
39{
40 auto videoOutput = QGstreamerVideoOutput::create();
41 if (!videoOutput)
42 return videoOutput.error();
43
44 return new QGstreamerMediaCapture(videoOutput.value());
45}
46
47QGstreamerMediaCapture::QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput)
48 : gstPipeline("pipeline"), gstVideoOutput(videoOutput)
49{
50 gstVideoOutput->setParent(this);
51 gstVideoOutput->setIsPreview();
52 gstVideoOutput->setPipeline(gstPipeline);
53
54 // Use system clock to drive all elements in the pipeline. Otherwise,
55 // the clock is sourced from the elements (e.g. from an audio source).
56 // Since the elements are added and removed dynamically the clock would
57 // also change causing lost of synchronization in the pipeline.
58 gst_pipeline_use_clock(gstPipeline.pipeline(), gst_system_clock_obtain());
59
60 // This is the recording pipeline with only live sources, thus the pipeline
61 // will be always in the playing state.
62 gstPipeline.setState(GST_STATE_PLAYING);
63 gstPipeline.setInStoppedState(false);
64
65 gstPipeline.dumpGraph("initial");
66}
67
69{
70 setMediaRecorder(nullptr);
71 setImageCapture(nullptr);
72 setCamera(nullptr);
73 gstPipeline.setStateSync(GST_STATE_NULL);
74}
75
77{
78 return gstCamera;
79}
80
82{
83 QGstreamerCamera *control = static_cast<QGstreamerCamera *>(camera);
84 if (gstCamera == control)
85 return;
86
87 if (gstCamera) {
88 unlinkTeeFromPad(gstVideoTee, encoderVideoSink);
89 unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
90
91 auto camera = gstCamera->gstElement();
92
93 gstPipeline.remove(camera);
94 gstPipeline.remove(gstVideoTee);
95 gstPipeline.remove(gstVideoOutput->gstElement());
96
97 camera.setStateSync(GST_STATE_NULL);
98 gstVideoTee.setStateSync(GST_STATE_NULL);
99 gstVideoOutput->gstElement().setStateSync(GST_STATE_NULL);
100
101 gstVideoTee = {};
102 gstCamera->setCaptureSession(nullptr);
103 }
104
105 gstCamera = control;
106 if (gstCamera) {
107 QGstElement camera = gstCamera->gstElement();
108 gstVideoTee = QGstElement("tee", "videotee");
109 gstVideoTee.set("allow-not-linked", true);
110
111 gstPipeline.add(gstVideoOutput->gstElement(), camera, gstVideoTee);
112
113 linkTeeToPad(gstVideoTee, encoderVideoSink);
114 linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
115 linkTeeToPad(gstVideoTee, imageCaptureSink);
116
117 camera.link(gstVideoTee);
118
119 gstVideoOutput->gstElement().setState(GST_STATE_PLAYING);
120 gstVideoTee.setState(GST_STATE_PLAYING);
121 camera.setState(GST_STATE_PLAYING);
122 }
123
124 gstPipeline.dumpGraph("camera");
125
127}
128
130{
131 return m_imageCapture;
132}
133
135{
137 if (m_imageCapture == control)
138 return;
139
140 if (m_imageCapture) {
141 unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
142 gstPipeline.remove(m_imageCapture->gstElement());
143 m_imageCapture->gstElement().setStateSync(GST_STATE_NULL);
144 imageCaptureSink = {};
145 m_imageCapture->setCaptureSession(nullptr);
146 }
147
148 m_imageCapture = control;
149 if (m_imageCapture) {
150 imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
151 m_imageCapture->gstElement().setState(GST_STATE_PLAYING);
152 gstPipeline.add(m_imageCapture->gstElement());
153 linkTeeToPad(gstVideoTee, imageCaptureSink);
154 m_imageCapture->setCaptureSession(this);
155 }
156
157 gstPipeline.dumpGraph("imageCapture");
158
160}
161
163{
164 QGstreamerMediaEncoder *control = static_cast<QGstreamerMediaEncoder *>(recorder);
165 if (m_mediaEncoder == control)
166 return;
167
168 if (m_mediaEncoder)
169 m_mediaEncoder->setCaptureSession(nullptr);
170 m_mediaEncoder = control;
171 if (m_mediaEncoder)
172 m_mediaEncoder->setCaptureSession(this);
173
175 gstPipeline.dumpGraph("encoder");
176}
177
179{
180 return m_mediaEncoder;
181}
182
184{
185 if (!gstVideoTee.isNull() && !videoSink.isNull()) {
186 auto caps = gst_pad_get_current_caps(gstVideoTee.sink().pad());
187
188 encoderVideoCapsFilter = QGstElement("capsfilter", "encoderVideoCapsFilter");
189 Q_ASSERT(encoderVideoCapsFilter);
190 encoderVideoCapsFilter.set("caps", QGstCaps(caps, QGstCaps::HasRef));
191
192 gstPipeline.add(encoderVideoCapsFilter);
193
194 encoderVideoCapsFilter.src().link(videoSink);
195 linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
196 encoderVideoCapsFilter.setState(GST_STATE_PLAYING);
197 encoderVideoSink = encoderVideoCapsFilter.sink();
198 }
199
200 if (!gstAudioTee.isNull() && !audioSink.isNull()) {
201 auto caps = gst_pad_get_current_caps(gstAudioTee.sink().pad());
202
203 encoderAudioCapsFilter = QGstElement("capsfilter", "encoderAudioCapsFilter");
204 Q_ASSERT(encoderAudioCapsFilter);
205 encoderAudioCapsFilter.set("caps", QGstCaps(caps, QGstCaps::HasRef));
206
207 gstPipeline.add(encoderAudioCapsFilter);
208
209 encoderAudioCapsFilter.src().link(audioSink);
210 linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
211 encoderAudioCapsFilter.setState(GST_STATE_PLAYING);
212 encoderAudioSink = encoderAudioCapsFilter.sink();
213 }
214}
215
217{
218 if (!encoderVideoCapsFilter.isNull()) {
219 encoderVideoCapsFilter.src().unlinkPeer();
220 unlinkTeeFromPad(gstVideoTee, encoderVideoCapsFilter.sink());
221 gstPipeline.remove(encoderVideoCapsFilter);
222 encoderVideoCapsFilter.setStateSync(GST_STATE_NULL);
223 encoderVideoCapsFilter = {};
224 }
225
226 if (!encoderAudioCapsFilter.isNull()) {
227 encoderAudioCapsFilter.src().unlinkPeer();
228 unlinkTeeFromPad(gstAudioTee, encoderAudioCapsFilter.sink());
229 gstPipeline.remove(encoderAudioCapsFilter);
230 encoderAudioCapsFilter.setStateSync(GST_STATE_NULL);
231 encoderAudioCapsFilter = {};
232 }
233
234 encoderAudioSink = {};
235 encoderVideoSink = {};
236}
237
239{
240 if (gstAudioInput == input)
241 return;
242
243 if (gstAudioInput) {
244 unlinkTeeFromPad(gstAudioTee, encoderAudioSink);
245
246 if (gstAudioOutput) {
247 unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
248 gstPipeline.remove(gstAudioOutput->gstElement());
249 gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL);
250 }
251
252 gstPipeline.remove(gstAudioInput->gstElement());
253 gstPipeline.remove(gstAudioTee);
254 gstAudioInput->gstElement().setStateSync(GST_STATE_NULL);
255 gstAudioTee.setStateSync(GST_STATE_NULL);
256 gstAudioTee = {};
257 }
258
259 gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
260 if (gstAudioInput) {
261 Q_ASSERT(gstAudioTee.isNull());
262 gstAudioTee = QGstElement("tee", "audiotee");
263 gstAudioTee.set("allow-not-linked", true);
264 gstPipeline.add(gstAudioInput->gstElement(), gstAudioTee);
265 gstAudioInput->gstElement().link(gstAudioTee);
266
267 if (gstAudioOutput) {
268 gstPipeline.add(gstAudioOutput->gstElement());
269 gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
270 linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
271 }
272
273 gstAudioTee.setState(GST_STATE_PLAYING);
274 gstAudioInput->gstElement().setStateSync(GST_STATE_PLAYING);
275
276 linkTeeToPad(gstAudioTee, encoderAudioSink);
277 }
278}
279
281{
282 gstVideoOutput->setVideoSink(sink);
283}
284
286{
287 if (gstAudioOutput == output)
288 return;
289
290 if (gstAudioOutput && gstAudioInput) {
291 // If audio input is set, the output is in the pipeline
292 unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
293 gstPipeline.remove(gstAudioOutput->gstElement());
294 gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL);
295 }
296
297 gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
298 if (gstAudioOutput && gstAudioInput) {
299 gstPipeline.add(gstAudioOutput->gstElement());
300 gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
301 linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
302 }
303}
304
306{
307 return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr;
308}
309
310
312
313#include "moc_qgstreamermediacapture_p.cpp"
void remove(const QGstElement &element)
Definition qgst_p.h:561
void add(const QGstElement &element)
Definition qgst_p.h:549
@ HasRef
Definition qgst_p.h:163
GstStateChangeReturn setState(GstState state)
Definition qgst_p.h:462
QGstPad getRequestPad(const char *name) const
Definition qgst_p.h:446
bool setStateSync(GstState state)
Definition qgst_p.h:463
void releaseRequestPad(const QGstPad &pad) const
Definition qgst_p.h:454
QGstPad sink() const
Definition qgst_p.h:445
QGstPad staticPad(const char *name) const
Definition qgst_p.h:443
bool link(const QGstElement &next)
Definition qgst_p.h:429
QGstPad src() const
Definition qgst_p.h:444
bool isNull() const
Definition qgst_p.h:288
void set(const char *property, const char *str)
Definition qgst_p.h:290
bool link(const QGstPad &sink) const
Definition qgst_p.h:338
bool unlinkPeer() const
Definition qgst_p.h:340
GstPad * pad() const
Definition qgst_p.h:344
GstStateChangeReturn setState(GstState state)
GstPipeline * pipeline() const
void dumpGraph(const char *fileName)
void setInStoppedState(bool stopped)
QGstElement gstElement() const
QGstElement gstElement() const
QGstElement gstElement() const
void setCaptureSession(QPlatformMediaCaptureSession *session)
static QMaybe< QPlatformMediaCaptureSession * > create()
void setImageCapture(QPlatformImageCapture *imageCapture) override
QGstreamerVideoSink * gstreamerVideoSink() const
void setAudioOutput(QPlatformAudioOutput *output) override
void setCamera(QPlatformCamera *camera) override
void setAudioInput(QPlatformAudioInput *input) override
QPlatformCamera * camera() override
QPlatformMediaRecorder * mediaRecorder() override
void setVideoPreview(QVideoSink *sink) override
void setMediaRecorder(QPlatformMediaRecorder *recorder) override
QPlatformImageCapture * imageCapture() override
void linkEncoder(QGstPad audioSink, QGstPad videoSink)
void setCaptureSession(QPlatformMediaCaptureSession *session)
void setPipeline(const QGstPipeline &pipeline)
QGstreamerVideoSink * gstreamerVideoSink() const
QGstElement gstElement() const
void setVideoSink(QVideoSink *sink)
static QMaybe< QGstreamerVideoOutput * > create(QObject *parent=nullptr)
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2142
virtual void setCaptureSession(QPlatformMediaCaptureSession *)
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
QMediaRecorder * recorder
Definition camera.cpp:20
QCamera * camera
Definition camera.cpp:19
QImageCapture * imageCapture
Definition camera.cpp:21
Combined button and popup list for selecting options.
static void unlinkTeeFromPad(QGstElement tee, QGstPad sink)
static QT_BEGIN_NAMESPACE void linkTeeToPad(QGstElement tee, QGstPad sink)
GLsizei GLsizei GLchar * source
GLsizei GLenum GLboolean sink
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
QT_BEGIN_NAMESPACE typedef uchar * output