| 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 |  | 
|---|