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
27QMaybe<QPlatformMediaCaptureSession *> QGstreamerMediaCapture::create()
28{
29 auto videoOutput = QGstreamerVideoOutput::create();
30 if (!videoOutput)
31 return videoOutput.error();
32
33 return new QGstreamerMediaCapture(videoOutput.value());
34}
35
36QGstreamerMediaCapture::QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput)
37 : capturePipeline(QGstPipeline::create("mediaCapturePipeline")), gstVideoOutput(videoOutput)
38{
39 gstVideoOutput->setParent(this);
40 gstVideoOutput->setIsPreview();
41 gstVideoOutput->setPipeline(capturePipeline);
42
43 // Use system clock to drive all elements in the pipeline. Otherwise,
44 // the clock is sourced from the elements (e.g. from an audio source).
45 // Since the elements are added and removed dynamically the clock would
46 // also change causing lost of synchronization in the pipeline.
47
48 QGstClockHandle systemClock{
49 gst_system_clock_obtain(),
50 };
51 gst_pipeline_use_clock(capturePipeline.pipeline(), systemClock.get());
52
53 // This is the recording pipeline with only live sources, thus the pipeline
54 // will be always in the playing state.
55 capturePipeline.setState(GST_STATE_PLAYING);
56 capturePipeline.setInStoppedState(false);
57
58 capturePipeline.dumpGraph("initial");
59}
60
62{
63 setMediaRecorder(nullptr);
64 setImageCapture(nullptr);
65 setCamera(nullptr);
66 capturePipeline.setStateSync(GST_STATE_NULL);
67}
68
70{
71 return gstCamera;
72}
73
75{
76 QGstreamerCamera *camera = static_cast<QGstreamerCamera *>(platformCamera);
77 if (gstCamera == camera)
78 return;
79
80 if (gstCamera) {
81 QObject::disconnect(gstCameraActiveConnection);
82 if (gstVideoTee)
83 setCameraActive(false);
84 }
85
86 gstCamera = camera;
87
88 if (gstCamera) {
89 gstCameraActiveConnection = QObject::connect(camera, &QGstreamerCamera::activeChanged, this,
90 &QGstreamerMediaCapture::setCameraActive);
91 if (gstCamera->isActive())
92 setCameraActive(true);
93 }
94
96}
97
98void QGstreamerMediaCapture::setCameraActive(bool activate)
99{
100 capturePipeline.modifyPipelineWhileNotRunning([&] {
101 if (activate) {
102 QGstElement cameraElement = gstCamera->gstElement();
103 gstVideoTee = QGstElement::createFromFactory("tee", "videotee");
104 gstVideoTee.set("allow-not-linked", true);
105
106 capturePipeline.add(gstVideoOutput->gstElement(), cameraElement, gstVideoTee);
107
108 linkTeeToPad(gstVideoTee, encoderVideoSink);
109 linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
110 linkTeeToPad(gstVideoTee, imageCaptureSink);
111
112 qLinkGstElements(cameraElement, gstVideoTee);
113
114 capturePipeline.syncChildrenState();
115 } else {
116 if (encoderVideoCapsFilter)
117 qUnlinkGstElements(gstVideoTee, encoderVideoCapsFilter);
118 if (m_imageCapture)
119 qUnlinkGstElements(gstVideoTee, m_imageCapture->gstElement());
120
121 auto camera = gstCamera->gstElement();
122
123 capturePipeline.stopAndRemoveElements(camera, gstVideoTee,
124 gstVideoOutput->gstElement());
125
126 gstVideoTee = {};
127 gstCamera->setCaptureSession(nullptr);
128 }
129 });
130
131 capturePipeline.dumpGraph("camera");
132}
133
135{
136 return m_imageCapture;
137}
138
140{
142 if (m_imageCapture == control)
143 return;
144
145 capturePipeline.modifyPipelineWhileNotRunning([&] {
146 if (m_imageCapture) {
147 qUnlinkGstElements(gstVideoTee, m_imageCapture->gstElement());
148 capturePipeline.stopAndRemoveElements(m_imageCapture->gstElement());
149 imageCaptureSink = {};
150 m_imageCapture->setCaptureSession(nullptr);
151 }
152
153 m_imageCapture = control;
154 if (m_imageCapture) {
155 imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
156 capturePipeline.add(m_imageCapture->gstElement());
157 m_imageCapture->gstElement().syncStateWithParent();
158 linkTeeToPad(gstVideoTee, imageCaptureSink);
159 m_imageCapture->setCaptureSession(this);
160 }
161 });
162
163 capturePipeline.dumpGraph("imageCapture");
164
166}
167
169{
170 QGstreamerMediaEncoder *control = static_cast<QGstreamerMediaEncoder *>(recorder);
171 if (m_mediaEncoder == control)
172 return;
173
174 if (m_mediaEncoder)
175 m_mediaEncoder->setCaptureSession(nullptr);
176 m_mediaEncoder = control;
177 if (m_mediaEncoder)
178 m_mediaEncoder->setCaptureSession(this);
179
181 capturePipeline.dumpGraph("encoder");
182}
183
185{
186 return m_mediaEncoder;
187}
188
190{
191 capturePipeline.modifyPipelineWhileNotRunning([&] {
192 if (!gstVideoTee.isNull() && !videoSink.isNull()) {
193 QGstCaps caps = gstVideoTee.sink().currentCaps();
194
195 encoderVideoCapsFilter =
196 QGstElement::createFromFactory("capsfilter", "encoderVideoCapsFilter");
197 Q_ASSERT(encoderVideoCapsFilter);
198 encoderVideoCapsFilter.set("caps", caps);
199
200 capturePipeline.add(encoderVideoCapsFilter);
201
202 encoderVideoCapsFilter.src().link(videoSink);
203 linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
204 encoderVideoSink = encoderVideoCapsFilter.sink();
205 }
206
207 if (!gstAudioTee.isNull() && !audioSink.isNull()) {
208 QGstCaps caps = gstAudioTee.sink().currentCaps();
209
210 encoderAudioCapsFilter =
211 QGstElement::createFromFactory("capsfilter", "encoderAudioCapsFilter");
212 Q_ASSERT(encoderAudioCapsFilter);
213 encoderAudioCapsFilter.set("caps", caps);
214
215 capturePipeline.add(encoderAudioCapsFilter);
216
217 encoderAudioCapsFilter.src().link(audioSink);
218 linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
219 encoderAudioSink = encoderAudioCapsFilter.sink();
220 }
221 });
222}
223
225{
226 capturePipeline.modifyPipelineWhileNotRunning([&] {
227 if (encoderVideoCapsFilter) {
228 qUnlinkGstElements(gstVideoTee, encoderVideoCapsFilter);
229 capturePipeline.stopAndRemoveElements(encoderVideoCapsFilter);
230 encoderVideoCapsFilter = {};
231 }
232
233 if (encoderAudioCapsFilter) {
234 qUnlinkGstElements(gstAudioTee, encoderAudioCapsFilter);
235 capturePipeline.stopAndRemoveElements(encoderAudioCapsFilter);
236 encoderAudioCapsFilter = {};
237 }
238
239 encoderAudioSink = {};
240 encoderVideoSink = {};
241 });
242}
243
245{
246 if (gstAudioInput == input)
247 return;
248
249 capturePipeline.modifyPipelineWhileNotRunning([&] {
250 if (gstAudioInput) {
251 if (encoderAudioCapsFilter)
252 qUnlinkGstElements(gstAudioTee, encoderAudioCapsFilter);
253
254 if (gstAudioOutput) {
255 qUnlinkGstElements(gstAudioTee, gstAudioOutput->gstElement());
256 capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
257 }
258
259 capturePipeline.stopAndRemoveElements(gstAudioInput->gstElement(), gstAudioTee);
260 gstAudioTee = {};
261 }
262
263 gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
264 if (gstAudioInput) {
265 Q_ASSERT(gstAudioTee.isNull());
266 gstAudioTee = QGstElement::createFromFactory("tee", "audiotee");
267 gstAudioTee.set("allow-not-linked", true);
268 capturePipeline.add(gstAudioInput->gstElement(), gstAudioTee);
269 qLinkGstElements(gstAudioInput->gstElement(), gstAudioTee);
270
271 if (gstAudioOutput) {
272 capturePipeline.add(gstAudioOutput->gstElement());
273 gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
274 linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
275 }
276
277 capturePipeline.syncChildrenState();
278
279 linkTeeToPad(gstAudioTee, encoderAudioSink);
280 }
281 });
282}
283
288
290{
291 if (gstAudioOutput == output)
292 return;
293
294 capturePipeline.modifyPipelineWhileNotRunning([&] {
295 if (gstAudioOutput && gstAudioInput) {
296 // If audio input is set, the output is in the pipeline
297 qUnlinkGstElements(gstAudioTee, gstAudioOutput->gstElement());
298 capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
299 }
300
301 gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
302 if (gstAudioOutput && gstAudioInput) {
303 capturePipeline.add(gstAudioOutput->gstElement());
304 capturePipeline.syncChildrenState();
305 linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
306 }
307 });
308}
309
311{
312 return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr;
313}
314
316{
317 return capturePipeline.pipeline();
318}
319
321
322#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:691
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void stopAndRemoveElements)(Ts... ts)
Definition qgst_p.h:710
bool syncChildrenState()
Definition qgst.cpp:1128
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 staticPad(const char *name) const
Definition qgst.cpp:898
static QGstElement createFromFactory(const char *factory, const char *name=nullptr)
Definition qgst.cpp:835
void set(const char *property, const char *str)
Definition qgst.cpp:538
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:644
std::enable_if_t<(std::is_base_of_v< QGstElement, Ts > &&...), void qUnlinkGstElements)(const Ts &...ts)
Definition qgst_p.h:663
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()