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 | |
25 | QT_BEGIN_NAMESPACE |
26 | |
27 | static_assert(GST_CHECK_VERSION(1, 20, 0), "Minimum required GStreamer version is 1.20" ); |
28 | |
29 | static thread_local bool inCustomCameraConstruction = false; |
30 | static thread_local QGstElement pendingCameraElement{}; |
31 | |
32 | QGStreamerPlatformSpecificInterfaceImplementation:: |
33 | ~QGStreamerPlatformSpecificInterfaceImplementation() = default; |
34 | |
35 | QAudioDevice QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerAudioInput( |
36 | const QByteArray &gstreamerPipeline) |
37 | { |
38 | return qMakeCustomGStreamerAudioInput(gstreamerPipeline); |
39 | } |
40 | |
41 | QAudioDevice QGStreamerPlatformSpecificInterfaceImplementation::makeCustomGStreamerAudioOutput( |
42 | const QByteArray &gstreamerPipeline) |
43 | { |
44 | return qMakeCustomGStreamerAudioOutput(gstreamerPipeline); |
45 | } |
46 | |
47 | QCamera *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 | |
62 | QCamera * |
63 | QGStreamerPlatformSpecificInterfaceImplementation::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 | |
84 | GstPipeline *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 | |
94 | GstPipeline * |
95 | QGStreamerPlatformSpecificInterfaceImplementation::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 | |
106 | Q_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer" ) |
107 | |
108 | namespace { |
109 | |
110 | void 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 |
121 | constexpr 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 |
128 | constexpr 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 |
134 | constexpr 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 | |
144 | QGstreamerIntegration::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 | |
172 | QGstreamerIntegration::~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 | |
181 | QPlatformMediaFormatInfo *QGstreamerIntegration::createFormatInfo() |
182 | { |
183 | return new QGstreamerFormatInfo(); |
184 | } |
185 | |
186 | QPlatformVideoDevices *QGstreamerIntegration::createVideoDevices() |
187 | { |
188 | return new QGstreamerVideoDevices(this); |
189 | } |
190 | |
191 | const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo() |
192 | { |
193 | return static_cast<const QGstreamerFormatInfo *>(formatInfo()); |
194 | } |
195 | |
196 | QMaybe<QPlatformAudioDecoder *> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder) |
197 | { |
198 | return QGstreamerAudioDecoder::create(parent: decoder); |
199 | } |
200 | |
201 | QMaybe<QPlatformMediaCaptureSession *> QGstreamerIntegration::createCaptureSession() |
202 | { |
203 | return QGstreamerMediaCaptureSession::create(); |
204 | } |
205 | |
206 | QMaybe<QPlatformMediaPlayer *> QGstreamerIntegration::createPlayer(QMediaPlayer *player) |
207 | { |
208 | return QGstreamerMediaPlayer::create(parent: player); |
209 | } |
210 | |
211 | QMaybe<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 | |
222 | QMaybe<QPlatformMediaRecorder *> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder) |
223 | { |
224 | return new QGstreamerMediaRecorder(recorder); |
225 | } |
226 | |
227 | QMaybe<QPlatformImageCapture *> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture) |
228 | { |
229 | return QGstreamerImageCapture::create(parent: imageCapture); |
230 | } |
231 | |
232 | QMaybe<QPlatformVideoSink *> QGstreamerIntegration::createVideoSink(QVideoSink *sink) |
233 | { |
234 | return new QGstreamerVideoSink(sink); |
235 | } |
236 | |
237 | QMaybe<QPlatformAudioInput *> QGstreamerIntegration::createAudioInput(QAudioInput *q) |
238 | { |
239 | return QGstreamerAudioInput::create(parent: q); |
240 | } |
241 | |
242 | QMaybe<QPlatformAudioOutput *> QGstreamerIntegration::createAudioOutput(QAudioOutput *q) |
243 | { |
244 | return QGstreamerAudioOutput::create(parent: q); |
245 | } |
246 | |
247 | GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id) |
248 | { |
249 | const auto devices = videoDevices(); |
250 | return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr; |
251 | } |
252 | |
253 | QAbstractPlatformSpecificInterface *QGstreamerIntegration::platformSpecificInterface() |
254 | { |
255 | return &m_platformSpecificImplementation; |
256 | } |
257 | |
258 | QT_END_NAMESPACE |
259 | |