Revision control
Copy as Markdown
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qgstreamerintegration_p.h>
#include <qgstreamerformatinfo_p.h>
#include <qgstreamervideodevices_p.h>
#include <audio/qgstreameraudiodevice_p.h>
#include <audio/qgstreameraudiodecoder_p.h>
#include <common/qgstreameraudioinput_p.h>
#include <common/qgstreameraudiooutput_p.h>
#include <common/qgstbufferprovider_p.h>
#include <common/qgstreamermediaplayer_p.h>
#include <common/qgstreamervideosink_p.h>
#include <mediacapture/qgstreamercamera_p.h>
#include <mediacapture/qgstreamerimagecapture_p.h>
#include <mediacapture/qgstreamermediacapturesession_p.h>
#include <mediacapture/qgstreamermediarecorder_p.h>
#include <uri_handler/qgstreamer_qiodevice_handler_p.h>
#include <uri_handler/qgstreamer_qrc_handler_p.h>
#if QT_CONFIG(gstreamer_qt_api)
# include <QtMultimedia/spi/qgstreamervideosource.h>
#endif
#include <QtCore/qloggingcategory.h>
#include <QtMultimedia/private/qmediaplayer_p.h>
#include <QtMultimedia/private/qmediacapturesession_p.h>
#include <QtMultimedia/private/qcameradevice_p.h>
#include <QtMultimedia/private/qvideoframe_p.h>
QT_BEGIN_NAMESPACE
static_assert(GST_CHECK_VERSION(1, 20, 0), "Minimum required GStreamer version is 1.20");
#if QT_CONFIG(gstreamer_qt_api)
namespace {
thread_local std::optional<GstElementOrDescription> customCameraContructionInput = { };
QCamera *makeCustomGStreamerCameraImpl(QByteArray id, GstElementOrDescription elementOrDesc,
QObject *parent)
{
QCameraDevicePrivate *info = new QCameraDevicePrivate;
info->id = std::move(id);
QCameraDevice device = info->create();
Q_ASSERT(!customCameraContructionInput);
customCameraContructionInput = std::move(elementOrDesc);
auto resetCustomCameraInput = qScopeGuard([] {
customCameraContructionInput.reset();
});
return new QCamera(device, parent);
}
q23::expected<QPlatformCamera *, QString>
createGStreamerVideoSourceImpl(QObject *videoSource, const GstElementOrDescription &elementOrDesc)
{
using namespace Qt::Literals;
auto createImpl = [videoSource](const auto &arg) -> q23::expected<QPlatformCamera *, QString> {
QGstElement element;
if constexpr (std::is_same_v<decltype(arg), const QString &>) {
if (arg.isEmpty())
return q23::unexpected{ u"GstBin description is empty"_s };
element = QGstBin::createFromPipelineDescription(arg.toUtf8(), /*name=*/nullptr,
/* ghostUnlinkedPads=*/true);
if (!element)
return q23::unexpected{ u"Failed to create GstBin from description"_s };
} else {
if (!arg)
return q23::unexpected{ u"GstElement is null"_s };
element = QGstElement{ arg, QGstElement::NeedsRef };
}
return new QGstreamerCustomCamera(videoSource, std::move(element));
};
return std::visit(createImpl, elementOrDesc);
}
} // namespace
QGStreamerInterfaceImplementation::~QGStreamerInterfaceImplementation() = default;
QAudioDevice QGStreamerInterfaceImplementation::makeCustomGStreamerAudioInput(
const QByteArray &gstreamerPipeline)
{
return qMakeCustomGStreamerAudioInput(gstreamerPipeline);
}
QAudioDevice QGStreamerInterfaceImplementation::makeCustomGStreamerAudioOutput(
const QByteArray &gstreamerPipeline)
{
return qMakeCustomGStreamerAudioOutput(gstreamerPipeline);
}
QCamera *
QGStreamerInterfaceImplementation::makeCustomGStreamerCamera(const QByteArray &gstBinDescription,
QObject *parent)
{
return makeCustomGStreamerCameraImpl(gstBinDescription, QString::fromUtf8(gstBinDescription),
parent);
}
QCamera *QGStreamerInterfaceImplementation::makeCustomGStreamerCamera(
GstElement *element, QObject *parent)
{
return makeCustomGStreamerCameraImpl("Custom Camera from GstElement", element, parent);
}
GstPipeline *QGStreamerInterfaceImplementation::gstPipeline(QMediaPlayer *player)
{
auto *priv = reinterpret_cast<QMediaPlayerPrivate *>(QMediaPlayerPrivate::get(player));
if (!priv)
return nullptr;
QGstreamerMediaPlayer *gstreamerPlayer = dynamic_cast<QGstreamerMediaPlayer *>(priv->control);
return gstreamerPlayer ? gstreamerPlayer->pipeline().pipeline() : nullptr;
}
GstPipeline *
QGStreamerInterfaceImplementation::gstPipeline(QMediaCaptureSession *session)
{
auto *priv = QMediaCaptureSessionPrivate::get(session);
if (!priv)
return nullptr;
QGstreamerMediaCaptureSession *gstreamerCapture =
dynamic_cast<QGstreamerMediaCaptureSession *>(priv->captureSession.get());
return gstreamerCapture ? gstreamerCapture->pipeline().pipeline() : nullptr;
}
GstBuffer *QGStreamerInterfaceImplementation::gstBuffer(const QVideoFrame &frame)
{
QHwVideoBuffer *hwBuffer = QVideoFramePrivate::hwBuffer(frame);
if (!hwBuffer)
return nullptr;
QGstBufferProvider *provider = dynamic_cast<QGstBufferProvider *>(hwBuffer);
return provider ? provider->gstBuffer() : nullptr;
}
QVideoFrame QGStreamerInterfaceImplementation::createFrameFromGstBuffer(
GstBuffer *buffer, const GstVideoInfo &videoInfo)
{
if (!buffer)
return QVideoFrame();
QGstVideoInfo qtVideoInfo{ videoInfo, std::nullopt };
return qCreateFrameFromGstBuffer(QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
qtVideoInfo);
}
QVideoFrame QGStreamerInterfaceImplementation::createFrameFromGstBuffer(
GstBuffer *buffer, const GstVideoInfoDmaDrm &videoInfoDmaDrm)
{
#if !QT_GSTREAMER_SUPPORTS_GST_VIDEO_FORMAT_DMA_DRM
Q_UNUSED(buffer);
Q_UNUSED(videoInfoDmaDrm);
qWarning() << QStringLiteral("GstVideoInfoDmaDrm unsupported, minimum GStreamer version required: 1.24");
return QVideoFrame();
#else
if (!buffer)
return QVideoFrame();
QGstVideoInfo qtVideoInfo{ { }, videoInfoDmaDrm.drm_modifier };
if (!gst_video_info_dma_drm_to_video_info(&videoInfoDmaDrm, &qtVideoInfo.gstVideoInfo))
qWarning() << "Failed to create QGstVideoInfo from GstVideoInfoDmaDrm";
return qCreateFrameFromGstBuffer(QGstBufferHandle{ buffer, QGstBufferHandle::NeedsRef },
qtVideoInfo);
#endif
}
#endif
Q_STATIC_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer")
namespace {
void rankDownPlugin(GstRegistry *reg, const char *name)
{
QGstPluginFeatureHandle pluginFeature{
gst_registry_lookup_feature(reg, name),
QGstPluginFeatureHandle::HasRef,
};
if (pluginFeature)
gst_plugin_feature_set_rank(pluginFeature.get(), GST_RANK_PRIMARY - 1);
}
constexpr auto vaapiPluginNames = {
"vaapidecodebin", "vaapih264dec", "vaapih264enc", "vaapih265dec",
"vaapijpegdec", "vaapijpegenc", "vaapimpeg2dec", "vaapipostproc",
"vaapisink", "vaapivp8dec", "vaapivp9dec",
};
constexpr auto vaPluginNames = {
"vaav1dec", "vacompositor", "vadeinterlace", "vah264dec", "vah264enc", "vah265dec",
"vajpegdec", "vampeg2dec", "vapostproc", "vavp8dec", "vavp9dec",
};
constexpr auto nvcodecPluginNames = {
"cudaconvert", "cudaconvertscale", "cudadownload", "cudaipcsink", "cudaipcsrc",
"cudascale", "cudaupload", "nvautogpuh264enc", "nvautogpuh265enc", "nvav1dec",
"nvcudah264enc", "nvcudah265enc", "nvd3d11h264enc", "nvd3d11h265enc", "nvh264dec",
"nvh264enc", "nvh265dec", "nvh265enc", "nvjpegdec", "nvjpegenc",
"nvmpeg2videodec", "nvmpeg4videodec", "nvmpegvideodec", "nvvp8dec", "nvvp9dec",
};
} // namespace
QGstreamerIntegration::QGstreamerIntegration()
: QPlatformMediaIntegration(QLatin1String("gstreamer"))
{
gst_init(nullptr, nullptr);
const QGString version{ gst_version_string() };
qCInfo(lcGstreamer) << "Using Qt multimedia with GStreamer version:" << version.asStringView();
GstRegistry *reg = gst_registry_get();
if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
for (const char *name : vaapiPluginNames)
rankDownPlugin(reg, name);
}
if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_VA")) {
for (const char *name : vaPluginNames)
rankDownPlugin(reg, name);
}
if (qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_NVCODEC")) {
for (const char *name : nvcodecPluginNames)
rankDownPlugin(reg, name);
}
qGstRegisterQRCHandler(nullptr);
qGstRegisterQIODeviceHandler(nullptr);
}
QGstreamerIntegration::~QGstreamerIntegration()
{
// by default we don't deinit, as the application may have initialized gstreamer
// (gst_init/deinit is not refcounted).
// however it's useful to force deinitialization for leak detection in qt's unit tests.
if (qEnvironmentVariableIsSet("QT_GSTREAMER_DEINIT"))
gst_deinit();
}
QPlatformMediaFormatInfo *QGstreamerIntegration::createFormatInfo()
{
return new QGstreamerFormatInfo();
}
QPlatformVideoDevices *QGstreamerIntegration::createVideoDevices()
{
return new QGstreamerVideoDevices(this);
}
const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo()
{
return static_cast<const QGstreamerFormatInfo *>(formatInfo());
}
q23::expected<QPlatformAudioDecoder *, QString> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
return QGstreamerAudioDecoder::create(decoder);
}
q23::expected<QPlatformMediaCaptureSession *, QString> QGstreamerIntegration::createCaptureSession()
{
return QGstreamerMediaCaptureSession::create();
}
q23::expected<QPlatformMediaPlayer *, QString> QGstreamerIntegration::createPlayer(QMediaPlayer *player)
{
return QGstreamerMediaPlayer::create(player);
}
q23::expected<QPlatformCamera *, QString> QGstreamerIntegration::createCamera(QCamera *camera)
{
#if QT_CONFIG(gstreamer_qt_api)
if (customCameraContructionInput)
return createGStreamerVideoSourceImpl(camera, *customCameraContructionInput);
#endif
return QGstreamerCamera::create(camera);
}
q23::expected<QPlatformMediaRecorder *, QString> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder)
{
return new QGstreamerMediaRecorder(recorder);
}
q23::expected<QPlatformImageCapture *, QString> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture)
{
return QGstreamerImageCapture::create(imageCapture);
}
q23::expected<QPlatformVideoSink *, QString> QGstreamerIntegration::createVideoSink(QVideoSink *sink)
{
return new QGstreamerPluggableVideoSink(sink);
}
q23::expected<QPlatformAudioInput *, QString> QGstreamerIntegration::createAudioInput(QAudioInput *q)
{
return QGstreamerAudioInput::create(q);
}
q23::expected<QPlatformAudioOutput *, QString> QGstreamerIntegration::createAudioOutput(QAudioOutput *q)
{
return QGstreamerAudioOutput::create(q);
}
GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id)
{
const auto devices = videoDevices();
return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
}
#if QT_CONFIG(gstreamer_qt_api)
q23::expected<QPlatformCamera *, QString>
QGstreamerIntegration::createGStreamerVideoSource(QGStreamerVideoSource *videoSource,
const GstElementOrDescription &elementOrDesc)
{
return createGStreamerVideoSourceImpl(videoSource, elementOrDesc);
}
QGStreamerInterface *QGstreamerIntegration::gstreamerInterface()
{
return &m_gstreamerInterface;
}
#endif
QT_END_NAMESPACE