Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
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
12
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/private/quniquehandle_p.h>
15
17
19{
20 if (tee.isNull() || sink.isNull())
21 return;
22
23 auto source = tee.getRequestPad("src_%u");
24 source.link(sink);
25}
26
28{
29 if (tee.isNull() || sink.isNull())
30 return;
31
32 auto source = sink.peer();
33 source.unlink(sink);
34
35 tee.releaseRequestPad(source);
36}
37
38QMaybe<QPlatformMediaCaptureSession *> QGstreamerMediaCapture::create()
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 : capturePipeline(QGstPipeline::create("mediaCapturePipeline")), gstVideoOutput(videoOutput)
49{
50 gstVideoOutput->setParent(this);
51 gstVideoOutput->setIsPreview();
52 gstVideoOutput->setPipeline(capturePipeline);
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
59 QGstClockHandle systemClock{
60 gst_system_clock_obtain(),
61 };
62 gst_pipeline_use_clock(capturePipeline.pipeline(), systemClock.get());
63
64 // This is the recording pipeline with only live sources, thus the pipeline
65 // will be always in the playing state.
66 capturePipeline.setState(GST_STATE_PLAYING);
67 capturePipeline.setInStoppedState(false);
68
69 capturePipeline.dumpGraph("initial");
70}
71
73{
74 setMediaRecorder(nullptr);
75 setImageCapture(nullptr);
76 setCamera(nullptr);
77 capturePipeline.setStateSync(GST_STATE_NULL);
78}
79
81{
82 return gstCamera;
83}
84
86{
87 QGstreamerCamera *camera = static_cast<QGstreamerCamera *>(platformCamera);
88 if (gstCamera == camera)
89 return;
90
91 if (gstCamera) {
92 QObject::disconnect(gstCameraActiveConnection);
93 if (gstVideoTee)
94 setCameraActive(false);
95 }
96
97 gstCamera = camera;
98
99 if (gstCamera) {
100 gstCameraActiveConnection = QObject::connect(camera, &QGstreamerCamera::activeChanged, this,
101 &QGstreamerMediaCapture::setCameraActive);
102 if (gstCamera->isActive())
103 setCameraActive(true);
104 }
105
107}
108
109void QGstreamerMediaCapture::setCameraActive(bool activate)
110{
111 capturePipeline.modifyPipelineWhileNotRunning([&] {
112 if (activate) {
113 QGstElement cameraElement = gstCamera->gstElement();
114 gstVideoTee = QGstElement::createFromFactory("tee", "videotee");
115 gstVideoTee.set("allow-not-linked", true);
116
117 capturePipeline.add(gstVideoOutput->gstElement(), cameraElement, gstVideoTee);
118
119 linkTeeToPad(gstVideoTee, encoderVideoSink);
120 linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
121 linkTeeToPad(gstVideoTee, imageCaptureSink);
122
123 qLinkGstElements(cameraElement, gstVideoTee);
124
125 capturePipeline.syncChildrenState();
126 } else {
127 unlinkTeeFromPad(gstVideoTee, encoderVideoSink);
128 unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
129
130 auto camera = gstCamera->gstElement();
131
132 capturePipeline.stopAndRemoveElements(camera, gstVideoTee,
133 gstVideoOutput->gstElement());
134
135 gstVideoTee = {};
136 gstCamera->setCaptureSession(nullptr);
137 }
138 });
139
140 capturePipeline.dumpGraph("camera");
141}
142
144{
145 return m_imageCapture;
146}
147
149{
151 if (m_imageCapture == control)
152 return;
153
154 capturePipeline.modifyPipelineWhileNotRunning([&] {
155 if (m_imageCapture) {
156 unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
157 capturePipeline.stopAndRemoveElements(m_imageCapture->gstElement());
158 imageCaptureSink = {};
159 m_imageCapture->setCaptureSession(nullptr);
160 }
161
162 m_imageCapture = control;
163 if (m_imageCapture) {
164 imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
165 capturePipeline.add(m_imageCapture->gstElement());
166 m_imageCapture->gstElement().syncStateWithParent();
167 linkTeeToPad(gstVideoTee, imageCaptureSink);
168 m_imageCapture->setCaptureSession(this);
169 }
170 });
171
172 capturePipeline.dumpGraph("imageCapture");
173
175}
176
178{
179 QGstreamerMediaEncoder *control = static_cast<QGstreamerMediaEncoder *>(recorder);
180 if (m_mediaEncoder == control)
181 return;
182
183 if (m_mediaEncoder)
184 m_mediaEncoder->setCaptureSession(nullptr);
185 m_mediaEncoder = control;
186 if (m_mediaEncoder)
187 m_mediaEncoder->setCaptureSession(this);
188
190 capturePipeline.dumpGraph("encoder");
191}
192
194{
195 return m_mediaEncoder;
196}
197
199{
200 capturePipeline.modifyPipelineWhileNotRunning([&] {
201 if (!gstVideoTee.isNull() && !videoSink.isNull()) {
202 QGstCaps caps = gstVideoTee.sink().currentCaps();
203
204 encoderVideoCapsFilter =
205 QGstElement::createFromFactory("capsfilter", "encoderVideoCapsFilter");
206 Q_ASSERT(encoderVideoCapsFilter);
207 encoderVideoCapsFilter.set("caps", caps);
208
209 capturePipeline.add(encoderVideoCapsFilter);
210
211 encoderVideoCapsFilter.src().link(videoSink);
212 linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
213 encoderVideoSink = encoderVideoCapsFilter.sink();
214 }
215
216 if (!gstAudioTee.isNull() && !audioSink.isNull()) {
217 QGstCaps caps = gstAudioTee.sink().currentCaps();
218
219 encoderAudioCapsFilter =
220 QGstElement::createFromFactory("capsfilter", "encoderAudioCapsFilter");
221 Q_ASSERT(encoderAudioCapsFilter);
222 encoderAudioCapsFilter.set("caps", caps);
223
224 capturePipeline.add(encoderAudioCapsFilter);
225
226 encoderAudioCapsFilter.src().link(audioSink);
227 linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
228 encoderAudioSink = encoderAudioCapsFilter.sink();
229 }
230 });
231}
232
234{
235 capturePipeline.modifyPipelineWhileNotRunning([&] {
236 if (!encoderVideoCapsFilter.isNull()) {
237 encoderVideoCapsFilter.src().unlinkPeer();
238 unlinkTeeFromPad(gstVideoTee, encoderVideoCapsFilter.sink());
239 capturePipeline.stopAndRemoveElements(encoderVideoCapsFilter);
240 encoderVideoCapsFilter = {};
241 }
242
243 if (!encoderAudioCapsFilter.isNull()) {
244 encoderAudioCapsFilter.src().unlinkPeer();
245 unlinkTeeFromPad(gstAudioTee, encoderAudioCapsFilter.sink());
246 capturePipeline.stopAndRemoveElements(encoderAudioCapsFilter);
247 encoderAudioCapsFilter = {};
248 }
249
250 encoderAudioSink = {};
251 encoderVideoSink = {};
252 });
253}
254
256{
257 if (gstAudioInput == input)
258 return;
259
260 capturePipeline.modifyPipelineWhileNotRunning([&] {
261 if (gstAudioInput) {
262 unlinkTeeFromPad(gstAudioTee, encoderAudioSink);
263
264 if (gstAudioOutput) {
265 unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
266 capturePipeline.remove(gstAudioOutput->gstElement());
267 gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL);
268 }
269
270 capturePipeline.stopAndRemoveElements(gstAudioInput->gstElement(), gstAudioTee);
271 gstAudioTee = {};
272 }
273
274 gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
275 if (gstAudioInput) {
276 Q_ASSERT(gstAudioTee.isNull());
277 gstAudioTee = QGstElement::createFromFactory("tee", "audiotee");
278 gstAudioTee.set("allow-not-linked", true);
279 capturePipeline.add(gstAudioInput->gstElement(), gstAudioTee);
280 qLinkGstElements(gstAudioInput->gstElement(), gstAudioTee);
281
282 if (gstAudioOutput) {
283 capturePipeline.add(gstAudioOutput->gstElement());
284 gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
285 linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
286 }
287
288 capturePipeline.syncChildrenState();
289
290 linkTeeToPad(gstAudioTee, encoderAudioSink);
291 }
292 });
293}
294
299
301{
302 if (gstAudioOutput == output)
303 return;
304
305 capturePipeline.modifyPipelineWhileNotRunning([&] {
306 if (gstAudioOutput && gstAudioInput) {
307 // If audio input is set, the output is in the pipeline
308 unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
309 capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
310 }
311
312 gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
313 if (gstAudioOutput && gstAudioInput) {
314 capturePipeline.add(gstAudioOutput->gstElement());
315 capturePipeline.syncChildrenState();
316 linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
317 }
318 });
319}
320
322{
323 return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr;
324}
325
327{
328 return capturePipeline.pipeline();
329}
330
332
333#include "moc_qgstreamermediacapture_p.cpp"
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void add)(const Ts &...ts)
Definition qgst_p.h:690
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void remove)(const Ts &...ts)
Definition qgst_p.h:699
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void stopAndRemoveElements)(Ts... ts)
Definition qgst_p.h:709
bool syncChildrenState()
Definition qgst.cpp:1126
GstStateChangeReturn setState(GstState state)
Definition qgst.cpp:942
bool setStateSync(GstState state, std::chrono::nanoseconds timeout=std::chrono::seconds(1))
Definition qgst.cpp:947
bool syncStateWithParent()
Definition qgst.cpp:969
QGstPad sink() const
Definition qgst.cpp:908
QGstPad staticPad(const char *name) const
Definition qgst.cpp:898
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:835
QGstPad src() const
Definition qgst.cpp:903
void set(const char *property, const char *str)
Definition qgst.cpp:538
bool unlinkPeer() const
Definition qgst.cpp:767
GstStateChangeReturn setState(GstState state)
GstPipeline * pipeline() const
void dumpGraph(const char *fileName)
void setInStoppedState(bool stopped)
void modifyPipelineWhileNotRunning(Functor &&fn)
QGstElement gstElement() const
QGstElement gstElement() const
bool isActive() const override
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)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2195
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
virtual void setCaptureSession(QPlatformMediaCaptureSession *)
void activeChanged(bool)
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
QMediaRecorder * recorder
Definition camera.cpp:20
QImageCapture * imageCapture
Definition camera.cpp:21
Combined button and popup list for selecting options.
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void qLinkGstElements)(const Ts &...ts)
Definition qgst_p.h:643
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
if(qFloatDistance(a, b)<(1<< 7))
[0]
view create()