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