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