summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp')
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
new file mode 100644
index 000000000..7ecbb07d7
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
@@ -0,0 +1,326 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <mediacapture/qgstreamermediaencoder_p.h>
+#include <mediacapture/qgstreamerimagecapture_p.h>
+#include <mediacapture/qgstreamercamera_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreameraudioinput_p.h>
+#include <common/qgstreameraudiooutput_p.h>
+#include <common/qgstreamervideooutput_p.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/quniquehandle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static void linkTeeToPad(QGstElement tee, QGstPad sink)
+{
+ if (tee.isNull() || sink.isNull())
+ return;
+
+ auto source = tee.getRequestPad("src_%u");
+ source.link(sink);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QGstreamerMediaCapture::create()
+{
+ auto videoOutput = QGstreamerVideoOutput::create();
+ if (!videoOutput)
+ return videoOutput.error();
+
+ static const auto error = qGstErrorMessageIfElementsNotAvailable("tee", "capsfilter");
+ if (error)
+ return *error;
+
+ return new QGstreamerMediaCapture(videoOutput.value());
+}
+
+QGstreamerMediaCapture::QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput)
+ : capturePipeline(QGstPipeline::create("mediaCapturePipeline")), gstVideoOutput(videoOutput)
+{
+ gstVideoOutput->setParent(this);
+ gstVideoOutput->setIsPreview();
+ gstVideoOutput->setPipeline(capturePipeline);
+
+ // Use system clock to drive all elements in the pipeline. Otherwise,
+ // the clock is sourced from the elements (e.g. from an audio source).
+ // Since the elements are added and removed dynamically the clock would
+ // also change causing lost of synchronization in the pipeline.
+
+ QGstClockHandle systemClock{
+ gst_system_clock_obtain(),
+ };
+ gst_pipeline_use_clock(capturePipeline.pipeline(), systemClock.get());
+
+ // This is the recording pipeline with only live sources, thus the pipeline
+ // will be always in the playing state.
+ capturePipeline.setState(GST_STATE_PLAYING);
+ capturePipeline.setInStoppedState(false);
+
+ capturePipeline.dumpGraph("initial");
+}
+
+QGstreamerMediaCapture::~QGstreamerMediaCapture()
+{
+ setMediaRecorder(nullptr);
+ setImageCapture(nullptr);
+ setCamera(nullptr);
+ capturePipeline.setStateSync(GST_STATE_NULL);
+}
+
+QPlatformCamera *QGstreamerMediaCapture::camera()
+{
+ return gstCamera;
+}
+
+void QGstreamerMediaCapture::setCamera(QPlatformCamera *platformCamera)
+{
+ auto *camera = static_cast<QGstreamerCameraBase *>(platformCamera);
+ if (gstCamera == camera)
+ return;
+
+ if (gstCamera) {
+ QObject::disconnect(gstCameraActiveConnection);
+ if (gstVideoTee)
+ setCameraActive(false);
+ }
+
+ gstCamera = camera;
+
+ if (gstCamera) {
+ gstCameraActiveConnection = QObject::connect(camera, &QPlatformCamera::activeChanged, this,
+ &QGstreamerMediaCapture::setCameraActive);
+ if (gstCamera->isActive())
+ setCameraActive(true);
+ }
+
+ emit cameraChanged();
+}
+
+void QGstreamerMediaCapture::setCameraActive(bool activate)
+{
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (activate) {
+ QGstElement cameraElement = gstCamera->gstElement();
+ gstVideoTee = QGstElement::createFromFactory("tee", "videotee");
+ gstVideoTee.set("allow-not-linked", true);
+
+ capturePipeline.add(gstVideoOutput->gstElement(), cameraElement, gstVideoTee);
+
+ linkTeeToPad(gstVideoTee, encoderVideoSink);
+ linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
+ linkTeeToPad(gstVideoTee, imageCaptureSink);
+
+ qLinkGstElements(cameraElement, gstVideoTee);
+
+ capturePipeline.syncChildrenState();
+ } else {
+ if (encoderVideoCapsFilter)
+ qUnlinkGstElements(gstVideoTee, encoderVideoCapsFilter);
+ if (m_imageCapture)
+ qUnlinkGstElements(gstVideoTee, m_imageCapture->gstElement());
+
+ auto camera = gstCamera->gstElement();
+
+ capturePipeline.stopAndRemoveElements(camera, gstVideoTee,
+ gstVideoOutput->gstElement());
+
+ gstVideoTee = {};
+ gstCamera->setCaptureSession(nullptr);
+ }
+ });
+
+ capturePipeline.dumpGraph("camera");
+}
+
+QPlatformImageCapture *QGstreamerMediaCapture::imageCapture()
+{
+ return m_imageCapture;
+}
+
+void QGstreamerMediaCapture::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ QGstreamerImageCapture *control = static_cast<QGstreamerImageCapture *>(imageCapture);
+ if (m_imageCapture == control)
+ return;
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (m_imageCapture) {
+ qUnlinkGstElements(gstVideoTee, m_imageCapture->gstElement());
+ capturePipeline.stopAndRemoveElements(m_imageCapture->gstElement());
+ imageCaptureSink = {};
+ m_imageCapture->setCaptureSession(nullptr);
+ }
+
+ m_imageCapture = control;
+ if (m_imageCapture) {
+ imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
+ capturePipeline.add(m_imageCapture->gstElement());
+ m_imageCapture->gstElement().syncStateWithParent();
+ linkTeeToPad(gstVideoTee, imageCaptureSink);
+ m_imageCapture->setCaptureSession(this);
+ }
+ });
+
+ capturePipeline.dumpGraph("imageCapture");
+
+ emit imageCaptureChanged();
+}
+
+void QGstreamerMediaCapture::setMediaRecorder(QPlatformMediaRecorder *recorder)
+{
+ QGstreamerMediaEncoder *control = static_cast<QGstreamerMediaEncoder *>(recorder);
+ if (m_mediaEncoder == control)
+ return;
+
+ if (m_mediaEncoder)
+ m_mediaEncoder->setCaptureSession(nullptr);
+ m_mediaEncoder = control;
+ if (m_mediaEncoder)
+ m_mediaEncoder->setCaptureSession(this);
+
+ emit encoderChanged();
+ capturePipeline.dumpGraph("encoder");
+}
+
+QPlatformMediaRecorder *QGstreamerMediaCapture::mediaRecorder()
+{
+ return m_mediaEncoder;
+}
+
+void QGstreamerMediaCapture::linkEncoder(QGstPad audioSink, QGstPad videoSink)
+{
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (!gstVideoTee.isNull() && !videoSink.isNull()) {
+ QGstCaps caps = gstVideoTee.sink().currentCaps();
+
+ encoderVideoCapsFilter =
+ QGstElement::createFromFactory("capsfilter", "encoderVideoCapsFilter");
+ Q_ASSERT(encoderVideoCapsFilter);
+ encoderVideoCapsFilter.set("caps", caps);
+
+ capturePipeline.add(encoderVideoCapsFilter);
+
+ encoderVideoCapsFilter.src().link(videoSink);
+ linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
+ encoderVideoSink = encoderVideoCapsFilter.sink();
+ }
+
+ if (!gstAudioTee.isNull() && !audioSink.isNull()) {
+ QGstCaps caps = gstAudioTee.sink().currentCaps();
+
+ encoderAudioCapsFilter =
+ QGstElement::createFromFactory("capsfilter", "encoderAudioCapsFilter");
+ Q_ASSERT(encoderAudioCapsFilter);
+ encoderAudioCapsFilter.set("caps", caps);
+
+ capturePipeline.add(encoderAudioCapsFilter);
+
+ encoderAudioCapsFilter.src().link(audioSink);
+ linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
+ encoderAudioSink = encoderAudioCapsFilter.sink();
+ }
+ });
+}
+
+void QGstreamerMediaCapture::unlinkEncoder()
+{
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (encoderVideoCapsFilter) {
+ qUnlinkGstElements(gstVideoTee, encoderVideoCapsFilter);
+ capturePipeline.stopAndRemoveElements(encoderVideoCapsFilter);
+ encoderVideoCapsFilter = {};
+ }
+
+ if (encoderAudioCapsFilter) {
+ qUnlinkGstElements(gstAudioTee, encoderAudioCapsFilter);
+ capturePipeline.stopAndRemoveElements(encoderAudioCapsFilter);
+ encoderAudioCapsFilter = {};
+ }
+
+ encoderAudioSink = {};
+ encoderVideoSink = {};
+ });
+}
+
+const QGstPipeline &QGstreamerMediaCapture::pipeline() const
+{
+ return capturePipeline;
+}
+
+void QGstreamerMediaCapture::setAudioInput(QPlatformAudioInput *input)
+{
+ if (gstAudioInput == input)
+ return;
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioInput) {
+ if (encoderAudioCapsFilter)
+ qUnlinkGstElements(gstAudioTee, encoderAudioCapsFilter);
+
+ if (gstAudioOutput) {
+ qUnlinkGstElements(gstAudioTee, gstAudioOutput->gstElement());
+ capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
+ }
+
+ capturePipeline.stopAndRemoveElements(gstAudioInput->gstElement(), gstAudioTee);
+ gstAudioTee = {};
+ }
+
+ gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
+ if (gstAudioInput) {
+ Q_ASSERT(gstAudioTee.isNull());
+ gstAudioTee = QGstElement::createFromFactory("tee", "audiotee");
+ gstAudioTee.set("allow-not-linked", true);
+ capturePipeline.add(gstAudioInput->gstElement(), gstAudioTee);
+ qLinkGstElements(gstAudioInput->gstElement(), gstAudioTee);
+
+ if (gstAudioOutput) {
+ capturePipeline.add(gstAudioOutput->gstElement());
+ gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
+ linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ }
+
+ capturePipeline.syncChildrenState();
+
+ linkTeeToPad(gstAudioTee, encoderAudioSink);
+ }
+ });
+}
+
+void QGstreamerMediaCapture::setVideoPreview(QVideoSink *sink)
+{
+ gstVideoOutput->setVideoSink(sink);
+}
+
+void QGstreamerMediaCapture::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (gstAudioOutput == output)
+ return;
+
+ capturePipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioOutput && gstAudioInput) {
+ // If audio input is set, the output is in the pipeline
+ qUnlinkGstElements(gstAudioTee, gstAudioOutput->gstElement());
+ capturePipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
+ }
+
+ gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
+ if (gstAudioOutput && gstAudioInput) {
+ capturePipeline.add(gstAudioOutput->gstElement());
+ capturePipeline.syncChildrenState();
+ linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ }
+ });
+}
+
+QGstreamerVideoSink *QGstreamerMediaCapture::gstreamerVideoSink() const
+{
+ return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgstreamermediacapture_p.cpp"