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_STATIC_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
149 const QGString version{ gst_version_string() };
150 qCInfo(lcGstreamer) << "Using Qt multimedia with GStreamer version:" << version.asStringView();
151
152 GstRegistry *reg = gst_registry_get();
153
154 if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
155 for (const char *name : vaapiPluginNames)
156 rankDownPlugin(reg, name);
157 }
158
159 if (qEnvironmentVariableIsSet(varName: "QT_GSTREAMER_DISABLE_VA")) {
160 for (const char *name : vaPluginNames)
161 rankDownPlugin(reg, name);
162 }
163
164 if (qEnvironmentVariableIsSet(varName: "QT_GSTREAMER_DISABLE_NVCODEC")) {
165 for (const char *name : nvcodecPluginNames)
166 rankDownPlugin(reg, name);
167 }
168
169 qGstRegisterQRCHandler(plugin: nullptr);
170 qGstRegisterQIODeviceHandler(plugin: nullptr);
171}
172
173QGstreamerIntegration::~QGstreamerIntegration()
174{
175 // by default we don't deinit, as the application may have initialized gstreamer
176 // (gst_init/deinit is not refcounted).
177 // however it's useful to force deinitialization for leak detection in qt's unit tests.
178 if (qEnvironmentVariableIsSet(varName: "QT_GSTREAMER_DEINIT"))
179 gst_deinit();
180}
181
182QPlatformMediaFormatInfo *QGstreamerIntegration::createFormatInfo()
183{
184 return new QGstreamerFormatInfo();
185}
186
187QPlatformVideoDevices *QGstreamerIntegration::createVideoDevices()
188{
189 return new QGstreamerVideoDevices(this);
190}
191
192const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo()
193{
194 return static_cast<const QGstreamerFormatInfo *>(formatInfo());
195}
196
197q23::expected<QPlatformAudioDecoder *, QString> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder)
198{
199 return QGstreamerAudioDecoder::create(parent: decoder);
200}
201
202q23::expected<QPlatformMediaCaptureSession *, QString> QGstreamerIntegration::createCaptureSession()
203{
204 return QGstreamerMediaCaptureSession::create();
205}
206
207q23::expected<QPlatformMediaPlayer *, QString> QGstreamerIntegration::createPlayer(QMediaPlayer *player)
208{
209 return QGstreamerMediaPlayer::create(parent: player);
210}
211
212q23::expected<QPlatformCamera *, QString> QGstreamerIntegration::createCamera(QCamera *camera)
213{
214 if (inCustomCameraConstruction) {
215 QGstElement element = std::exchange(obj&: pendingCameraElement, new_val: {});
216 return element ? new QGstreamerCustomCamera{ camera, std::move(element) }
217 : new QGstreamerCustomCamera{ camera };
218 }
219
220 return QGstreamerCamera::create(camera);
221}
222
223q23::expected<QPlatformMediaRecorder *, QString> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder)
224{
225 return new QGstreamerMediaRecorder(recorder);
226}
227
228q23::expected<QPlatformImageCapture *, QString> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture)
229{
230 return QGstreamerImageCapture::create(parent: imageCapture);
231}
232
233q23::expected<QPlatformVideoSink *, QString> QGstreamerIntegration::createVideoSink(QVideoSink *sink)
234{
235 return new QGstreamerVideoSink(sink);
236}
237
238q23::expected<QPlatformAudioInput *, QString> QGstreamerIntegration::createAudioInput(QAudioInput *q)
239{
240 return QGstreamerAudioInput::create(parent: q);
241}
242
243q23::expected<QPlatformAudioOutput *, QString> QGstreamerIntegration::createAudioOutput(QAudioOutput *q)
244{
245 return QGstreamerAudioOutput::create(parent: q);
246}
247
248GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id)
249{
250 const auto devices = videoDevices();
251 return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
252}
253
254QAbstractPlatformSpecificInterface *QGstreamerIntegration::platformSpecificInterface()
255{
256 return &m_platformSpecificImplementation;
257}
258
259QT_END_NAMESPACE
260

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