3#include <QtCore/QByteArray>
4#include <QtCore/QDateTime>
7#include <QtCore/QFileInfo>
8#include <QtCore/QLatin1String>
9#include <QtCore/QStandardPaths>
10#include <QtCore/QString>
11#include <QtCore/QStringList>
12#include <QtQuick/QQuickWindow>
13#include <QtQuick/QSGRendererInterface>
15#include <gst/rtsp/gstrtspurl.h>
20#if defined(QGC_HAS_ANY_GPU_PATH)
41 if (!gst_uri_is_valid(uri_str)) {
45 GstRTSPUrl* url =
nullptr;
46 const GstRTSPResult res = gst_rtsp_url_parse(uri_str, &url);
47 if ((res != GST_RTSP_OK) || !url) {
49 gst_rtsp_url_free(url);
54 const bool hasHost = (url->host && url->host[0] !=
'\0');
55 gst_rtsp_url_free(url);
62 constexpr int kMaxDotFiles = 10;
66 if (!qgetenv(
"GST_DEBUG_DUMP_DOT_DIR").isEmpty()) {
69 const QString cacheRoot = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
70 if (cacheRoot.isEmpty())
72 QDir dir(cacheRoot + QStringLiteral(
"/qgc-pipeline-dot"));
73 if (!dir.exists() && !dir.mkpath(QStringLiteral(
"."))) {
74 qCWarning(GStreamerHelpersLog) <<
"Failed to create" << dir.absolutePath();
79 QFileInfoList existing = dir.entryInfoList(QStringList{QStringLiteral(
"*.dot")},
80 QDir::Files, QDir::Time | QDir::Reversed);
81 while (existing.size() >= kMaxDotFiles) {
82 QFile::remove(existing.takeFirst().absoluteFilePath());
85 gchar* data = gst_debug_bin_to_dot_data(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL);
88 const QString fileName = QStringLiteral(
"%1-%2.dot")
89 .arg(QString::fromLatin1(tag),
90 QDateTime::currentDateTimeUtc().toString(QStringLiteral(
"yyyyMMdd-HHmmss-zzz")));
91 const QString fullPath = dir.absoluteFilePath(fileName);
92 const QByteArray dotData = QByteArray::fromRawData(data,
static_cast<qsizetype
>(qstrlen(data)));
95 if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
96 wrote = (out.write(dotData) ==
static_cast<qint64
>(dotData.size()));
101 if (out.error() != QFileDevice::NoError) {
106 return wrote ? fullPath : QString{};
109void forEachPlugin(GstRegistry* registry,
const std::function<
void(GstPlugin*)>& visitor)
111 if (!registry || !visitor)
113 GList* plugins = gst_registry_get_plugin_list(registry);
114 for (GList* node = plugins; node !=
nullptr; node = node->next) {
115 GstPlugin* plugin =
static_cast<GstPlugin*
>(node->data);
119 gst_plugin_list_free(plugins);
128 const gchar* factoryName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
133 const QByteArray nameLower = QByteArray::fromRawData(factoryName, qstrlen(factoryName)).toLower();
136 if (nameLower.startsWith(
"amcviddec-omxgoogle") || nameLower.startsWith(
"amcviddec-c2android")) {
139 if (nameLower.startsWith(
"amcviddec-")) {
143 const auto containsHardware = [](
const gchar* value) {
146 gchar* lower = g_ascii_strdown(value, -1);
147 bool found = (g_strrstr(lower,
"hardware") !=
nullptr);
152 if (containsHardware(gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_KLASS))) {
156 if (containsHardware(gst_element_factory_get_metadata(factory, GST_ELEMENT_METADATA_DESCRIPTION))) {
160 static constexpr const char* kHardwareTags[] = {
161 "va",
"nv",
"qsv",
"msdk",
"vulkan",
"d3d",
"dxva",
"vtdec",
"metal",
164 for (
const auto& tag : kHardwareTags) {
165 if (nameLower.contains(tag)) {
175 if (!registry || !featureName) {
184 qCDebug(GStreamerHelpersLog) <<
" Changing feature (" << featureName <<
") to use rank:" << rank;
185 gst_plugin_feature_set_rank(feature.get(), rank);
191void applyRanks(GstRegistry* registry, std::span<const char* const> features, uint16_t rank)
193 for (
const char* name : features) {
194 changeFeatureRank(registry, name, rank);
199constexpr std::array<const char* const, 7> kVaDecoders = {
"vaav1dec",
"vah264dec",
"vah265dec",
"vajpegdec",
200 "vampeg2dec",
"vavp8dec",
"vavp9dec"};
201constexpr std::array<const char* const, 9> kNvidiaDecoders = {
"nvav1dec",
"nvh264dec",
"nvh265dec",
202 "nvjpegdec",
"nvmpeg2videodec",
"nvmpeg4videodec",
203 "nvmpegvideodec",
"nvvp8dec",
"nvvp9dec"};
204constexpr std::array<const char* const, 18> kDirectX3DDecoders = {
205 "d3d11av1dec",
"d3d11h264dec",
"d3d11h265dec",
"d3d11mpeg2dec",
"d3d11vp8dec",
"d3d11vp9dec",
206 "d3d12av1dec",
"d3d12h264dec",
"d3d12h265dec",
"d3d12mpeg2dec",
"d3d12vp8dec",
"d3d12vp9dec",
207 "dxvaav1decoder",
"dxvah264decoder",
"dxvah265decoder",
"dxvampeg2decoder",
"dxvavp8decoder",
"dxvavp9decoder"};
208constexpr std::array<const char* const, 2> kVideoToolboxDecoders = {
"vtdec_hw",
"vtdec"};
209constexpr std::array<const char* const, 12> kIntelDecoders = {
210 "qsvh264dec",
"qsvh265dec",
"qsvjpegdec",
"qsvvp9dec",
"msdkav1dec",
"msdkh264dec",
211 "msdkh265dec",
"msdkmjpegdec",
"msdkmpeg2dec",
"msdkvc1dec",
"msdkvp8dec",
"msdkvp9dec"};
212constexpr std::array<const char* const, 2> kVulkanDecoders = {
"vulkanh264dec",
"vulkanh265dec"};
214void lowerDecoderRanksByClass(GstRegistry* registry,
bool lowerHardware)
216 static constexpr uint16_t NewRank = GST_RANK_NONE;
218 qCCritical(GStreamerHelpersLog) <<
"Invalid registry!";
222 GList* decoderFactories = gst_element_factory_list_get_elements(
223 static_cast<GstElementFactoryListType
>(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO),
226 for (GList* node = decoderFactories; node !=
nullptr; node = node->next) {
227 GstElementFactory* factory = GST_ELEMENT_FACTORY(node->data);
236 const gchar* name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
241 qCDebug(GStreamerHelpersLog) <<
"Lowering" << (lowerHardware ?
"hardware" :
"software")
242 <<
"decoder rank:" << name;
243 gst_plugin_feature_set_rank(GST_PLUGIN_FEATURE(factory), NewRank);
246 gst_plugin_feature_list_free(decoderFactories);
249void prioritizeByHardwareClass(GstRegistry* registry, uint16_t prioritizedRank,
bool requireHardware)
252 qCCritical(GStreamerHelpersLog) <<
"Failed to get gstreamer registry.";
256 GList* decoderFactories = gst_element_factory_list_get_elements(
257 static_cast<GstElementFactoryListType
>(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO),
260 if (!decoderFactories) {
261 qCDebug(GStreamerHelpersLog) <<
"No decoder factories available while prioritizing"
262 << (requireHardware ?
"hardware" :
"software") <<
"decoders";
266 qCDebug(GStreamerHelpersLog) <<
"Prioritizing" << (requireHardware ?
"hardware" :
"software")
267 <<
"video decoders with rank:" << prioritizedRank;
268 int matchedFactories = 0;
269 for (GList* node = decoderFactories; node !=
nullptr; node = node->next) {
270 GstElementFactory* factory = GST_ELEMENT_FACTORY(node->data);
279 const gchar* featureName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
288 if (matchedFactories == 0) {
289 qCWarning(GStreamerHelpersLog) <<
"No" << (requireHardware ?
"hardware" :
"software")
290 <<
"video decoder factories found to reprioritize.";
293 qCDebug(GStreamerHelpersLog) <<
"Lowering" << (requireHardware ?
"software" :
"hardware") <<
"decoder ranks.";
294 lowerDecoderRanksByClass(registry, !requireHardware);
296 gst_plugin_feature_list_free(decoderFactories);
301void alignD3DDecoderRanksToRhi(GstRegistry* registry,
bool promoteMatchedFamily)
307 bool wantD3D12 =
false;
308 bool resolved =
false;
309#if defined(QGC_HAS_ANY_GPU_PATH)
311 wantD3D12 = (rhi->backend() == QRhi::D3D12);
316 switch (QQuickWindow::graphicsApi()) {
317 case QSGRendererInterface::Direct3D12:
320 case QSGRendererInterface::Direct3D11:
325 qEnvironmentVariable(
"QSG_RHI_BACKEND").compare(QLatin1String(
"d3d12"), Qt::CaseInsensitive) == 0;
329 static constexpr const char* kD3D11Decoders[] = {
"d3d11av1dec",
"d3d11h264dec",
"d3d11h265dec",
330 "d3d11mpeg2dec",
"d3d11vp8dec",
"d3d11vp9dec"};
331 static constexpr const char* kD3D12Decoders[] = {
"d3d12av1dec",
"d3d12h264dec",
"d3d12h265dec",
332 "d3d12mpeg2dec",
"d3d12vp8dec",
"d3d12vp9dec"};
333 const char*
const* matched = wantD3D12 ? kD3D12Decoders : kD3D11Decoders;
334 const char*
const* mismatched = wantD3D12 ? kD3D11Decoders : kD3D12Decoders;
335 qCDebug(GStreamerHelpersLog) <<
"Aligning D3D decoder ranks to" << (wantD3D12 ?
"D3D12" :
"D3D11")
336 <<
"RHI - demoting the mismatched decoder family";
337 static_assert(std::size(kD3D11Decoders) == std::size(kD3D12Decoders));
338 if (promoteMatchedFamily) {
339 static constexpr uint16_t ZeroCopyRank = GST_RANK_PRIMARY + 2;
340 for (
size_t i = 0; i < std::size(kD3D11Decoders); ++i) {
344 for (
size_t i = 0; i < std::size(kD3D11Decoders); ++i) {
351constexpr std::array<const char* const, 8> kLegacyVaapiDecoders = {
"vaapiav1dec",
"vaapih264dec",
"vaapih265dec",
352 "vaapijpegdec",
"vaapimpeg2dec",
"vaapivp8dec",
353 "vaapivp9dec",
"vaapidecodebin"};
354constexpr std::array<const char* const, 6> kV4l2StatelessDecoders = {
355 "v4l2slh264dec",
"v4l2slh265dec",
"v4l2slvp8dec",
"v4l2slvp9dec",
"v4l2slav1dec",
"v4l2slmpeg2dec"};
359void preferZeroCopyDecoders(GstRegistry* registry)
361 static constexpr uint16_t ZeroCopyRank = GST_RANK_PRIMARY + 2;
365 applyRanks(registry, kLegacyVaapiDecoders, GST_RANK_NONE);
369 applyRanks(registry, kVaDecoders, ZeroCopyRank);
370 applyRanks(registry, kV4l2StatelessDecoders, ZeroCopyRank);
380 qCWarning(GStreamerHelpersLog) <<
"Ignoring invalid decode option:" << rawOption;
388 GstRegistry* registry = gst_registry_get();
391 qCCritical(GStreamerHelpersLog) <<
"Failed to get gstreamer registry.";
395 static constexpr uint16_t PrioritizedRank = GST_RANK_PRIMARY + 1;
401 preferZeroCopyDecoders(registry);
405 prioritizeByHardwareClass(registry, PrioritizedRank,
false);
408 prioritizeByHardwareClass(registry, PrioritizedRank,
true);
411 applyRanks(registry, kVaDecoders, PrioritizedRank);
414 applyRanks(registry, kNvidiaDecoders, PrioritizedRank);
417 applyRanks(registry, kDirectX3DDecoders, PrioritizedRank);
420 applyRanks(registry, kVideoToolboxDecoders, PrioritizedRank);
423 applyRanks(registry, kIntelDecoders, PrioritizedRank);
427 qCWarning(GStreamerHelpersLog) <<
"Forcing Vulkan video decoders: zero-copy import is dormant "
428 "(foreign VkDevice → CPU fallback), so decode will not be zero-copy.";
429 applyRanks(registry, kVulkanDecoders, PrioritizedRank);
438 alignD3DDecoderRanksToRhi(registry, promoteMatchingD3D);
struct _GstElement GstElement
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool changeFeatureRank(GstRegistry *registry, const char *featureName, uint16_t rank)
void setCodecPriorities(int rawOption)
Overload taking the raw forceVideoDecoder setting value; the cast/range-check lives in the impl.
void forEachPlugin(GstRegistry *registry, const std::function< void(GstPlugin *)> &visitor)
QString writePipelineDot(GstElement *pipeline, const char *tag)
bool isHardwareDecoderFactory(GstElementFactory *factory)
GstFeaturePtr adoptFeature(GstPluginFeature *feature) noexcept
bool isValidRtspUri(const gchar *uri_str)
@ ForceVideoDecoderDefault
@ ForceVideoDecoderVulkan
@ ForceVideoDecoderSoftware
@ ForceVideoDecoderNVIDIA
@ ForceVideoDecoderVideoToolbox
@ ForceVideoDecoderHardware
@ ForceVideoDecoderDirectX3D
std::unique_ptr< GstPluginFeature, GstObjectDeleter > GstFeaturePtr
QRhi * cachedRhi() noexcept
Cached QRhi* maintained by sceneGraph signals; safe from any thread via acquire ordering.