1// Copyright (C) 2022 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 <qgstreamerintegration_p.h>
5#include <qgstreamerformatinfo_p.h>
6#include <qgstreamervideodevices_p.h>
7#include <audio/qgstreameraudiodevice_p.h>
8#include <audio/qgstreameraudiodecoder_p.h>
9#include <common/qgstreameraudioinput_p.h>
10#include <common/qgstreameraudiooutput_p.h>
11#include <common/qgstreamermediaplayer_p.h>
12#include <common/qgstreamervideosink_p.h>
13#include <mediacapture/qgstreamercamera_p.h>
14#include <mediacapture/qgstreamerimagecapture_p.h>
15#include <mediacapture/qgstreamermediacapturesession_p.h>
16#include <mediacapture/qgstreamermediarecorder_p.h>
17#include <uri_handler/qgstreamer_qiodevice_handler_p.h>
18#include <uri_handler/qgstreamer_qrc_handler_p.h>
19
20#include <QtCore/qloggingcategory.h>
21#include <QtMultimedia/private/qmediaplayer_p.h>
22#include <QtMultimedia/private/qmediacapturesession_p.h>
23#include <QtMultimedia/private/qcameradevice_p.h>
24
25QT_BEGIN_NAMESPACE
26
27static_assert(GST_CHECK_VERSION(1, 20, 0), "Minimum required GStreamer version is 1.20");
28
29static thread_local bool inCustomCameraConstruction = false;
30static thread_local QGstElement pendingCameraElement{};
31
32QGStreamerPlatformSpecificInterfaceImplementation::
33 ~QGStreamerPlatformSpecificInterfaceImplementation() = default;
34
35QAudioDevice QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerAudioInput(
36 const QByteArray &gstreamerPipeline)
37{
38 return qMakeCustomGStreamerAudioInput(gstreamerPipeline);
39}
40
41QAudioDevice QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerAudioOutput(
42 const QByteArray &gstreamerPipeline)
43{
44 return qMakeCustomGStreamerAudioOutput(gstreamerPipeline);
45}
46
47QCamera *QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerCamera(
48 const QByteArray &gstreamerPipeline, QObject *parent)
49{
50 QCameraDevicePrivate *info = new QCameraDevicePrivate;
51 info->id = gstreamerPipeline;
52 QCameraDevice device = info->create();
53
54 inCustomCameraConstruction = true;
55 auto guard = qScopeGuard(f: [] {
56 inCustomCameraConstruction = false;
57 });
58
59 return new QCamera(device, parent);
60}
61
62QCamera *
63QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerCamera(GstElement *element,
64 QObject *parent)
65{
66 QCameraDevicePrivate *info = new QCameraDevicePrivate;
67 info->id = "Custom Camera from GstElement";
68 QCameraDevice device = info->create();
69
70 pendingCameraElement = QGstElement{
71 element,
72 QGstElement::NeedsRef,
73 };
74
75 inCustomCameraConstruction = true;
76 auto guard = qScopeGuard(f: [] {
77 inCustomCameraConstruction = false;
78 Q_ASSERT(!pendingCameraElement);
79 });
80
81 return new QCamera(device, parent);
82}
83
84GstPipeline *QGStreamerPlatformSpecificInterfaceImplementation::gstPipeline(QMediaPlayer *player)
85{
86 auto *priv = reinterpret_cast<QMediaPlayerPrivate *>(QMediaPlayerPrivate::get(session: player));
87 if (!priv)
88 return nullptr;
89
90 QGstreamerMediaPlayer *gstreamerPlayer = dynamic_cast<QGstreamerMediaPlayer *>(priv->control);
91 return gstreamerPlayer ? gstreamerPlayer->pipeline().pipeline() : nullptr;
92}
93
94GstPipeline *
95QGStreamerPlatformSpecificInterfaceImplementation::gstPipeline(QMediaCaptureSession *session)
96{
97 auto *priv = QMediaCaptureSessionPrivate::get(session);
98 if (!priv)
99 return nullptr;
100
101 QGstreamerMediaCaptureSession *gstreamerCapture =
102 dynamic_cast<QGstreamerMediaCaptureSession *>(priv->captureSession.get());
103 return gstreamerCapture ? gstreamerCapture->pipeline().pipeline() : nullptr;
104}
105
106Q_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer")
107
108namespace {
109
110void rankDownPlugin(GstRegistry *reg, const char *name)
111{
112 QGstPluginFeatureHandle pluginFeature{
113 gst_registry_lookup_feature(registry: reg, name),
114 QGstPluginFeatureHandle::HasRef,
115 };
116 if (pluginFeature)
117 gst_plugin_feature_set_rank(feature: pluginFeature.get(), rank: GST_RANK_PRIMARY - 1);
118}
119
120// https://gstreamer.freedesktop.org/documentation/vaapi/index.html
121constexpr auto vaapiPluginNames = {
122 "vaapidecodebin", "vaapih264dec", "vaapih264enc", "vaapih265dec",
123 "vaapijpegdec", "vaapijpegenc", "vaapimpeg2dec", "vaapipostproc",
124 "vaapisink", "vaapivp8dec", "vaapivp9dec",
125};
126
127// https://gstreamer.freedesktop.org/documentation/va/index.html
128constexpr auto vaPluginNames = {
129 "vaav1dec", "vacompositor", "vadeinterlace", "vah264dec", "vah264enc", "vah265dec",
130 "vajpegdec", "vampeg2dec", "vapostproc", "vavp8dec", "vavp9dec",
131};
132
133// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html
134constexpr auto nvcodecPluginNames = {
135 "cudaconvert", "cudaconvertscale", "cudadownload", "cudaipcsink", "cudaipcsrc",
136 "cudascale", "cudaupload", "nvautogpuh264enc", "nvautogpuh265enc", "nvav1dec",
137 "nvcudah264enc", "nvcudah265enc", "nvd3d11h264enc", "nvd3d11h265enc", "nvh264dec",
138 "nvh264enc", "nvh265dec", "nvh265enc", "nvjpegdec", "nvjpegenc",
139 "nvmpeg2videodec", "nvmpeg4videodec", "nvmpegvideodec", "nvvp8dec", "nvvp9dec",
140};
141
142} // namespace
143
144QGstreamerIntegration::QGstreamerIntegration()
145 : QPlatformMediaIntegration(QLatin1String("gstreamer"))
146{
147 gst_init(argc: nullptr, argv: nullptr);
148 qCDebug(lcGstreamer) << "Using gstreamer version: " << gst_version_string();
149
150 GstRegistry *reg = gst_registry_get();
151
152 if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
153 GstRegistry* reg = gst_registry_get();
154 for (const char *name : vaapiPluginNames)
155 rankDownPlugin(reg, name);
156 }
157
158 if (qEnvironmentVariableIsSet(varName: "QT_GSTREAMER_DISABLE_VA")) {
159 for (const char *name : vaPluginNames)
160 rankDownPlugin(reg, name);
161 }
162
163 if (qEnvironmentVariableIsSet(varName: "QT_GSTREAMER_DISABLE_NVCODEC")) {
164 for (const char *name : nvcodecPluginNames)
165 rankDownPlugin(reg, name);
166 }
167
168 qGstRegisterQRCHandler(plugin: nullptr);
169 qGstRegisterQIODeviceHandler(plugin: nullptr);
170}
171
172QGstreamerIntegration::~QGstreamerIntegration()
173{
174 // by default we don't deinit, as the application may have initialized gstreamer
175 // (gst_init/deinit is not refcounted).
176 // however it's useful to force deinitialization for leak detection in qt's unit tests.
177 if (qEnvironmentVariableIsSet(varName: "QT_GSTREAMER_DEINIT"))
178 gst_deinit();
179}
180
181QPlatformMediaFormatInfo *QGstreamerIntegration::createFormatInfo()
182{
183 return new QGstreamerFormatInfo();
184}
185
186QPlatformVideoDevices *QGstreamerIntegration::createVideoDevices()
187{
188 return new QGstreamerVideoDevices(this);
189}
190
191const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo()
192{
193 return static_cast<const QGstreamerFormatInfo *>(formatInfo());
194}
195
196QMaybe<QPlatformAudioDecoder *> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder)
197{
198 return QGstreamerAudioDecoder::create(parent: decoder);
199}
200
201QMaybe<QPlatformMediaCaptureSession *> QGstreamerIntegration::createCaptureSession()
202{
203 return QGstreamerMediaCaptureSession::create();
204}
205
206QMaybe<QPlatformMediaPlayer *> QGstreamerIntegration::createPlayer(QMediaPlayer *player)
207{
208 return QGstreamerMediaPlayer::create(parent: player);
209}
210
211QMaybe<QPlatformCamera *> QGstreamerIntegration::createCamera(QCamera *camera)
212{
213 if (inCustomCameraConstruction) {
214 QGstElement element = std::exchange(obj&: pendingCameraElement, new_val: {});
215 return element ? new QGstreamerCustomCamera{ camera, std::move(element) }
216 : new QGstreamerCustomCamera{ camera };
217 }
218
219 return QGstreamerCamera::create(camera);
220}
221
222QMaybe<QPlatformMediaRecorder *> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder)
223{
224 return new QGstreamerMediaRecorder(recorder);
225}
226
227QMaybe<QPlatformImageCapture *> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture)
228{
229 return QGstreamerImageCapture::create(parent: imageCapture);
230}
231
232QMaybe<QPlatformVideoSink *> QGstreamerIntegration::createVideoSink(QVideoSink *sink)
233{
234 return new QGstreamerVideoSink(sink);
235}
236
237QMaybe<QPlatformAudioInput *> QGstreamerIntegration::createAudioInput(QAudioInput *q)
238{
239 return QGstreamerAudioInput::create(parent: q);
240}
241
242QMaybe<QPlatformAudioOutput *> QGstreamerIntegration::createAudioOutput(QAudioOutput *q)
243{
244 return QGstreamerAudioOutput::create(parent: q);
245}
246
247GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id)
248{
249 const auto devices = videoDevices();
250 return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
251}
252
253QAbstractPlatformSpecificInterface *QGstreamerIntegration::platformSpecificInterface()
254{
255 return &m_platformSpecificImplementation;
256}
257
258QT_END_NAMESPACE
259

source code of qtmultimedia/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp