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/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
33static thread_local bool inCustomCameraConstruction = false;
34static thread_local QGstElement pendingCameraElement{};
35
38
40 const QByteArray &gstreamerPipeline)
41{
42 return qMakeCustomGStreamerAudioInput(gstreamerPipeline);
43}
44
46 const QByteArray &gstreamerPipeline)
47{
48 return qMakeCustomGStreamerAudioOutput(gstreamerPipeline);
49}
50
52 const QByteArray &gstreamerPipeline, QObject *parent)
53{
54 QCameraDevicePrivate *info = new QCameraDevicePrivate;
55 info->id = gstreamerPipeline;
56 QCameraDevice device = info->create();
57
59 auto guard = qScopeGuard([] {
61 });
62
63 return new QCamera(device, parent);
64}
65
66QCamera *
68 QObject *parent)
69{
70 QCameraDevicePrivate *info = new QCameraDevicePrivate;
71 info->id = "Custom Camera from GstElement";
72 QCameraDevice device = info->create();
73
74 pendingCameraElement = QGstElement{
75 element,
76 QGstElement::NeedsRef,
77 };
78
80 auto guard = qScopeGuard([] {
82 Q_ASSERT(!pendingCameraElement);
83 });
84
85 return new QCamera(device, parent);
86}
87
89{
90 auto *priv = reinterpret_cast<QMediaPlayerPrivate *>(QMediaPlayerPrivate::get(player));
91 if (!priv)
92 return nullptr;
93
94 QGstreamerMediaPlayer *gstreamerPlayer = dynamic_cast<QGstreamerMediaPlayer *>(priv->control);
95 return gstreamerPlayer ? gstreamerPlayer->pipeline().pipeline() : nullptr;
96}
97
100{
101 auto *priv = QMediaCaptureSessionPrivate::get(session);
102 if (!priv)
103 return nullptr;
104
105 QGstreamerMediaCaptureSession *gstreamerCapture =
106 dynamic_cast<QGstreamerMediaCaptureSession *>(priv->captureSession.get());
107 return gstreamerCapture ? gstreamerCapture->pipeline().pipeline() : nullptr;
108}
109
111{
112 QHwVideoBuffer *hwBuffer = QVideoFramePrivate::hwBuffer(frame);
113 if (!hwBuffer)
114 return nullptr;
115 QGstVideoBuffer *gstBuffer = dynamic_cast<QGstVideoBuffer *>(hwBuffer);
116 return gstBuffer ? gstBuffer->gstBuffer() : nullptr;
117}
118
120 GstBuffer *buffer, const GstVideoInfo &videoInfo)
121{
122 if (!buffer)
123 return QVideoFrame();
124
125 QGstVideoInfo qtVideoInfo{ videoInfo, std::nullopt };
126
127 return qCreateFrameFromGstBuffer(QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
128 qtVideoInfo);
129}
130
132 GstBuffer *buffer, const GstVideoInfoDmaDrm &videoInfoDmaDrm)
133{
135 Q_UNUSED(buffer);
136 Q_UNUSED(videoInfoDmaDrm);
137 qWarning() << QStringLiteral("GstVideoInfoDmaDrm unsupported, minimum GStreamer version required: 1.24");
138 return QVideoFrame();
139#else
140 if (!buffer)
141 return QVideoFrame();
142
143 QGstVideoInfo qtVideoInfo{ { }, videoInfoDmaDrm.drm_modifier };
144 if (!gst_video_info_dma_drm_to_video_info(&videoInfoDmaDrm, &qtVideoInfo.gstVideoInfo))
145 qWarning() << "Failed to create QGstVideoInfo from GstVideoInfoDmaDrm";
146
147 return qCreateFrameFromGstBuffer(QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
148 qtVideoInfo);
149#endif
150}
151
152Q_STATIC_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer")
153
154namespace {
155
156void rankDownPlugin(GstRegistry *reg, const char *name)
157{
158 QGstPluginFeatureHandle pluginFeature{
159 gst_registry_lookup_feature(reg, name),
160 QGstPluginFeatureHandle::HasRef,
161 };
162 if (pluginFeature)
163 gst_plugin_feature_set_rank(pluginFeature.get(), GST_RANK_PRIMARY - 1);
164}
165
166// https://gstreamer.freedesktop.org/documentation/vaapi/index.html
167constexpr auto vaapiPluginNames = {
168 "vaapidecodebin", "vaapih264dec", "vaapih264enc", "vaapih265dec",
169 "vaapijpegdec", "vaapijpegenc", "vaapimpeg2dec", "vaapipostproc",
170 "vaapisink", "vaapivp8dec", "vaapivp9dec",
171};
172
173// https://gstreamer.freedesktop.org/documentation/va/index.html
174constexpr auto vaPluginNames = {
175 "vaav1dec", "vacompositor", "vadeinterlace", "vah264dec", "vah264enc", "vah265dec",
176 "vajpegdec", "vampeg2dec", "vapostproc", "vavp8dec", "vavp9dec",
177};
178
179// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html
180constexpr auto nvcodecPluginNames = {
181 "cudaconvert", "cudaconvertscale", "cudadownload", "cudaipcsink", "cudaipcsrc",
182 "cudascale", "cudaupload", "nvautogpuh264enc", "nvautogpuh265enc", "nvav1dec",
183 "nvcudah264enc", "nvcudah265enc", "nvd3d11h264enc", "nvd3d11h265enc", "nvh264dec",
184 "nvh264enc", "nvh265dec", "nvh265enc", "nvjpegdec", "nvjpegenc",
185 "nvmpeg2videodec", "nvmpeg4videodec", "nvmpegvideodec", "nvvp8dec", "nvvp9dec",
186};
187
188} // namespace
189
192{
193 gst_init(nullptr, nullptr);
194
195 const QGString version{ gst_version_string() };
196 qCInfo(lcGstreamer) << "Using Qt multimedia with GStreamer version:" << version.asStringView();
197
198 GstRegistry *reg = gst_registry_get();
199
200 if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
201 for (const char *name : vaapiPluginNames)
202 rankDownPlugin(reg, name);
203 }
204
205 if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_VA")) {
206 for (const char *name : vaPluginNames)
207 rankDownPlugin(reg, name);
208 }
209
210 if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_NVCODEC")) {
211 for (const char *name : nvcodecPluginNames)
212 rankDownPlugin(reg, name);
213 }
214
217}
218
220{
221 // by default we don't deinit, as the application may have initialized gstreamer
222 // (gst_init/deinit is not refcounted).
223 // however it's useful to force deinitialization for leak detection in qt's unit tests.
224 if (qEnvironmentVariableIsSet("QT_GSTREAMER_DEINIT"))
225 gst_deinit();
226}
227
232
234{
235 return new QGstreamerVideoDevices(this);
236}
237
239{
240 return static_cast<const QGstreamerFormatInfo *>(formatInfo());
241}
242
244{
245 return QGstreamerAudioDecoder::create(decoder);
246}
247
249{
250 return QGstreamerMediaCaptureSession::create();
251}
252
254{
255 return QGstreamerMediaPlayer::create(player);
256}
257
259{
261 QGstElement element = std::exchange(pendingCameraElement, {});
262 return element ? new QGstreamerCustomCamera{ camera, std::move(element) }
263 : new QGstreamerCustomCamera{ camera };
264 }
265
266 return QGstreamerCamera::create(camera);
267}
268
270{
271 return new QGstreamerMediaRecorder(recorder);
272}
273
275{
276 return QGstreamerImageCapture::create(imageCapture);
277}
278
283
285{
286 return QGstreamerAudioInput::create(q);
287}
288
290{
291 return QGstreamerAudioOutput::create(q);
292}
293
295QGstreamerIntegration::createGStreamerVideoSource(QGStreamerVideoSource *videoSource,
296 const GstElementOrDescription &elementOrDesc)
297{
298#if QT_CONFIG(gstreamer_qt_api)
299 using namespace Qt::Literals;
300 auto createImpl = [videoSource](const auto &arg) -> q23::expected<QPlatformCamera *, QString> {
301 QGstElement element;
302 if constexpr (std::is_same_v<decltype(arg), const QString &>) {
303 if (arg.isEmpty())
304 return q23::unexpected{ u"GstBin description is empty"_s };
305 element = QGstBin::createFromPipelineDescription(arg.toUtf8(), /*name=*/nullptr,
306 /* ghostUnlinkedPads=*/true);
307 if (!element)
308 return q23::unexpected{ u"Failed to create GstBin from description"_s };
309 } else {
310 if (!arg)
311 return q23::unexpected{ u"GstElement is null"_s };
312
313 element = QGstElement{ arg, QGstElement::NeedsRef };
314 }
315
316 return new QGstreamerCustomCamera(videoSource, std::move(element));
317 };
318 return std::visit(createImpl, elementOrDesc);
319#else
320 return QPlatformMediaIntegration::createGStreamerVideoSource(videoSource, elementOrDesc);
321#endif
322}
323
325{
326 const auto devices = videoDevices();
327 return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
328}
329
334
335QT_END_NAMESPACE
QCamera * makeCustomGStreamerCamera(GstElement *, QObject *parent) override
GstPipeline * gstPipeline(QMediaPlayer *) override
QAudioDevice makeCustomGStreamerAudioInput(const QByteArray &gstreamerPipeline) override
QCamera * makeCustomGStreamerCamera(const QByteArray &gstreamerPipeline, QObject *parent) override
QAudioDevice makeCustomGStreamerAudioOutput(const QByteArray &gstreamerPipeline) override
QVideoFrame createFrameFromGstBuffer(GstBuffer *buffer, const GstVideoInfo &videoInfo) override
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
QGStreamerPlatformSpecificInterfaceImplementation m_platformSpecificImplementation
QAbstractPlatformSpecificInterface * platformSpecificInterface() 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
q23::expected< QPlatformCamera *, QString > createGStreamerVideoSource(QGStreamerVideoSource *, const GstElementOrDescription &) override
GstDevice * videoDevice(const QByteArray &id)
q23::expected< QPlatformAudioInput *, QString > createAudioInput(QAudioInput *) override
const QGstPipeline & pipeline() const
Combined button and popup list for selecting options.
#define QT_GSTREAMER_SUPPORTS_GST_VIDEO_FORMAT_DMA_DRM
Definition qgst_p.h:38
void qGstRegisterQIODeviceHandler(GstPlugin *plugin)
QT_BEGIN_NAMESPACE void qGstRegisterQRCHandler(GstPlugin *plugin)
static bool inCustomCameraConstruction
static QGstElement pendingCameraElement
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)