Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgstreamerintegration.cpp
Go to the documentation of this file.
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#if QT_CONFIG(gstreamer_qt_api)
21# include <QtMultimedia/spi/qgstreamervideosource.h>
22#endif
23#include <QtCore/qloggingcategory.h>
24#include <QtMultimedia/private/qmediaplayer_p.h>
25#include <QtMultimedia/private/qmediacapturesession_p.h>
26#include <QtMultimedia/private/qcameradevice_p.h>
27#include <QtMultimedia/private/qvideoframe_p.h>
28
30
31static_assert(GST_CHECK_VERSION(1, 20, 0), "Minimum required GStreamer version is 1.20");
32
33#if QT_CONFIG(gstreamer_qt_api)
34
35namespace {
36
37thread_local std::optional<GstElementOrDescription> customCameraContructionInput = { };
38
39QCamera *makeCustomGStreamerCameraImpl(QByteArray id, GstElementOrDescription elementOrDesc,
40 QObject *parent)
41{
42 QCameraDevicePrivate *info = new QCameraDevicePrivate;
43 info->id = std::move(id);
44 QCameraDevice device = info->create();
45
46 Q_ASSERT(!customCameraContructionInput);
47 customCameraContructionInput = std::move(elementOrDesc);
48
49 auto resetCustomCameraInput = qScopeGuard([] {
50 customCameraContructionInput.reset();
51 });
52
53 return new QCamera(device, parent);
54}
55
56q23::expected<QPlatformCamera *, QString>
57createGStreamerVideoSourceImpl(QObject *videoSource, const GstElementOrDescription &elementOrDesc)
58{
59 using namespace Qt::Literals;
60 auto createImpl = [videoSource](const auto &arg) -> q23::expected<QPlatformCamera *, QString> {
61 QGstElement element;
62 if constexpr (std::is_same_v<decltype(arg), const QString &>) {
63 if (arg.isEmpty())
64 return q23::unexpected{ u"GstBin description is empty"_s };
65 element = QGstBin::createFromPipelineDescription(arg.toUtf8(), /*name=*/nullptr,
66 /* ghostUnlinkedPads=*/true);
67 if (!element)
68 return q23::unexpected{ u"Failed to create GstBin from description"_s };
69 } else {
70 if (!arg)
71 return q23::unexpected{ u"GstElement is null"_s };
72
73 element = QGstElement{ arg, QGstElement::NeedsRef };
74 }
75
76 return new QGstreamerCustomCamera(videoSource, std::move(element));
77 };
78 return std::visit(createImpl, elementOrDesc);
79}
80
81} // namespace
82
83QGStreamerInterfaceImplementation::~QGStreamerInterfaceImplementation() = default;
84
85QAudioDevice QGStreamerInterfaceImplementation::makeCustomGStreamerAudioInput(
86 const QByteArray &gstreamerPipeline)
87{
88 return qMakeCustomGStreamerAudioInput(gstreamerPipeline);
89}
90
91QAudioDevice QGStreamerInterfaceImplementation::makeCustomGStreamerAudioOutput(
92 const QByteArray &gstreamerPipeline)
93{
94 return qMakeCustomGStreamerAudioOutput(gstreamerPipeline);
95}
96
97QCamera *
98QGStreamerInterfaceImplementation::makeCustomGStreamerCamera(const QByteArray &gstBinDescription,
99 QObject *parent)
100{
101 return makeCustomGStreamerCameraImpl(gstBinDescription, QString::fromUtf8(gstBinDescription),
102 parent);
103}
104
105QCamera *QGStreamerInterfaceImplementation::makeCustomGStreamerCamera(
106 GstElement *element, QObject *parent)
107{
108 return makeCustomGStreamerCameraImpl("Custom Camera from GstElement", element, parent);
109}
110
111GstPipeline *QGStreamerInterfaceImplementation::gstPipeline(QMediaPlayer *player)
112{
113 auto *priv = reinterpret_cast<QMediaPlayerPrivate *>(QMediaPlayerPrivate::get(player));
114 if (!priv)
115 return nullptr;
116
117 QGstreamerMediaPlayer *gstreamerPlayer = dynamic_cast<QGstreamerMediaPlayer *>(priv->control);
118 return gstreamerPlayer ? gstreamerPlayer->pipeline().pipeline() : nullptr;
119}
120
121GstPipeline *
122QGStreamerInterfaceImplementation::gstPipeline(QMediaCaptureSession *session)
123{
124 auto *priv = QMediaCaptureSessionPrivate::get(session);
125 if (!priv)
126 return nullptr;
127
128 QGstreamerMediaCaptureSession *gstreamerCapture =
129 dynamic_cast<QGstreamerMediaCaptureSession *>(priv->captureSession.get());
130 return gstreamerCapture ? gstreamerCapture->pipeline().pipeline() : nullptr;
131}
132
133GstBuffer *QGStreamerInterfaceImplementation::gstBuffer(const QVideoFrame &frame)
134{
135 QHwVideoBuffer *hwBuffer = QVideoFramePrivate::hwBuffer(frame);
136 if (!hwBuffer)
137 return nullptr;
138 QGstVideoBuffer *gstBuffer = dynamic_cast<QGstVideoBuffer *>(hwBuffer);
139 return gstBuffer ? gstBuffer->gstBuffer() : nullptr;
140}
141
142QVideoFrame QGStreamerInterfaceImplementation::createFrameFromGstBuffer(
143 GstBuffer *buffer, const GstVideoInfo &videoInfo)
144{
145 if (!buffer)
146 return QVideoFrame();
147
148 QGstVideoInfo qtVideoInfo{ videoInfo, std::nullopt };
149
150 return qCreateFrameFromGstBuffer(QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
151 qtVideoInfo);
152}
153
154QVideoFrame QGStreamerInterfaceImplementation::createFrameFromGstBuffer(
155 GstBuffer *buffer, const GstVideoInfoDmaDrm &videoInfoDmaDrm)
156{
157#if !QT_GSTREAMER_SUPPORTS_GST_VIDEO_FORMAT_DMA_DRM
158 Q_UNUSED(buffer);
159 Q_UNUSED(videoInfoDmaDrm);
160 qWarning() << QStringLiteral("GstVideoInfoDmaDrm unsupported, minimum GStreamer version required: 1.24");
161 return QVideoFrame();
162#else
163 if (!buffer)
164 return QVideoFrame();
165
166 QGstVideoInfo qtVideoInfo{ { }, videoInfoDmaDrm.drm_modifier };
167 if (!gst_video_info_dma_drm_to_video_info(&videoInfoDmaDrm, &qtVideoInfo.gstVideoInfo))
168 qWarning() << "Failed to create QGstVideoInfo from GstVideoInfoDmaDrm";
169
170 return qCreateFrameFromGstBuffer(QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
171 qtVideoInfo);
172#endif
173}
174
175#endif
176
177Q_STATIC_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer")
178
179namespace {
180
181void rankDownPlugin(GstRegistry *reg, const char *name)
182{
183 QGstPluginFeatureHandle pluginFeature{
184 gst_registry_lookup_feature(reg, name),
185 QGstPluginFeatureHandle::HasRef,
186 };
187 if (pluginFeature)
188 gst_plugin_feature_set_rank(pluginFeature.get(), GST_RANK_PRIMARY - 1);
189}
190
191// https://gstreamer.freedesktop.org/documentation/vaapi/index.html
192constexpr auto vaapiPluginNames = {
193 "vaapidecodebin", "vaapih264dec", "vaapih264enc", "vaapih265dec",
194 "vaapijpegdec", "vaapijpegenc", "vaapimpeg2dec", "vaapipostproc",
195 "vaapisink", "vaapivp8dec", "vaapivp9dec",
196};
197
198// https://gstreamer.freedesktop.org/documentation/va/index.html
199constexpr auto vaPluginNames = {
200 "vaav1dec", "vacompositor", "vadeinterlace", "vah264dec", "vah264enc", "vah265dec",
201 "vajpegdec", "vampeg2dec", "vapostproc", "vavp8dec", "vavp9dec",
202};
203
204// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html
205constexpr auto nvcodecPluginNames = {
206 "cudaconvert", "cudaconvertscale", "cudadownload", "cudaipcsink", "cudaipcsrc",
207 "cudascale", "cudaupload", "nvautogpuh264enc", "nvautogpuh265enc", "nvav1dec",
208 "nvcudah264enc", "nvcudah265enc", "nvd3d11h264enc", "nvd3d11h265enc", "nvh264dec",
209 "nvh264enc", "nvh265dec", "nvh265enc", "nvjpegdec", "nvjpegenc",
210 "nvmpeg2videodec", "nvmpeg4videodec", "nvmpegvideodec", "nvvp8dec", "nvvp9dec",
211};
212
213} // namespace
214
217{
218 gst_init(nullptr, nullptr);
219
220 const QGString version{ gst_version_string() };
221 qCInfo(lcGstreamer) << "Using Qt multimedia with GStreamer version:" << version.asStringView();
222
223 GstRegistry *reg = gst_registry_get();
224
225 if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
226 for (const char *name : vaapiPluginNames)
227 rankDownPlugin(reg, name);
228 }
229
230 if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_VA")) {
231 for (const char *name : vaPluginNames)
232 rankDownPlugin(reg, name);
233 }
234
235 if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_NVCODEC")) {
236 for (const char *name : nvcodecPluginNames)
237 rankDownPlugin(reg, name);
238 }
239
242}
243
245{
246 // by default we don't deinit, as the application may have initialized gstreamer
247 // (gst_init/deinit is not refcounted).
248 // however it's useful to force deinitialization for leak detection in qt's unit tests.
249 if (qEnvironmentVariableIsSet("QT_GSTREAMER_DEINIT"))
250 gst_deinit();
251}
252
257
259{
260 return new QGstreamerVideoDevices(this);
261}
262
264{
265 return static_cast<const QGstreamerFormatInfo *>(formatInfo());
266}
267
269{
270 return QGstreamerAudioDecoder::create(decoder);
271}
272
274{
275 return QGstreamerMediaCaptureSession::create();
276}
277
279{
280 return QGstreamerMediaPlayer::create(player);
281}
282
284{
285#if QT_CONFIG(gstreamer_qt_api)
286 if (customCameraContructionInput)
287 return createGStreamerVideoSourceImpl(camera, *customCameraContructionInput);
288#endif
289
290 return QGstreamerCamera::create(camera);
291}
292
294{
295 return new QGstreamerMediaRecorder(recorder);
296}
297
299{
300 return QGstreamerImageCapture::create(imageCapture);
301}
302
307
309{
310 return QGstreamerAudioInput::create(q);
311}
312
314{
315 return QGstreamerAudioOutput::create(q);
316}
317
319{
320 const auto devices = videoDevices();
321 return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
322}
323
324#if QT_CONFIG(gstreamer_qt_api)
325q23::expected<QPlatformCamera *, QString>
326QGstreamerIntegration::createGStreamerVideoSource(QGStreamerVideoSource *videoSource,
327 const GstElementOrDescription &elementOrDesc)
328{
329 return createGStreamerVideoSourceImpl(videoSource, elementOrDesc);
330}
331
332QGStreamerInterface *QGstreamerIntegration::gstreamerInterface()
333{
334 return &m_gstreamerInterface;
335}
336#endif
337
338QT_END_NAMESPACE
q23::expected< QPlatformCamera *, QString > createCamera(QCamera *) override
QPlatformVideoDevices * createVideoDevices() override
QPlatformMediaFormatInfo * createFormatInfo() override
q23::expected< QPlatformAudioOutput *, QString > createAudioOutput(QAudioOutput *) override
q23::expected< QPlatformVideoSink *, QString > createVideoSink(QVideoSink *sink) override
q23::expected< QPlatformMediaCaptureSession *, QString > createCaptureSession() override
q23::expected< QPlatformMediaPlayer *, QString > createPlayer(QMediaPlayer *player) override
q23::expected< QPlatformMediaRecorder *, QString > createRecorder(QMediaRecorder *) override
const QGstreamerFormatInfo * gstFormatsInfo()
q23::expected< QPlatformAudioDecoder *, QString > createAudioDecoder(QAudioDecoder *decoder) override
q23::expected< QPlatformImageCapture *, QString > createImageCapture(QImageCapture *) override
GstDevice * videoDevice(const QByteArray &id)
q23::expected< QPlatformAudioInput *, QString > createAudioInput(QAudioInput *) override
Combined button and popup list for selecting options.
void qGstRegisterQIODeviceHandler(GstPlugin *plugin)
QT_BEGIN_NAMESPACE void qGstRegisterQRCHandler(GstPlugin *plugin)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)