QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GStreamerLogging.cc
Go to the documentation of this file.
1#include "GStreamerLogging.h"
2
3#include <QtCore/QSettings>
4#include <QtCore/QString>
5#include <algorithm>
6#include <atomic>
7#include <memory>
8
9#include "AppSettings.h"
10#include "GStreamer.h"
11#include "GStreamerHelpers.h"
12#include "QGCLoggingCategory.h"
13
14QGC_LOGGING_CATEGORY(GStreamerLoggingLog, "Video.GStreamer.GStreamerLogging")
15QGC_LOGGING_CATEGORY_ON(GStreamerAPILog, "Video.GStreamer.GStreamerAPI")
16QGC_LOGGING_CATEGORY(GStreamerDecoderRanksLog, "Video.GStreamer.DecoderRanks")
17
18namespace {
19
20std::atomic_bool g_externalPluginLoaderFailed = false;
21
22void glib_print_handler(const gchar* string)
23{
24 qCInfo(GStreamerLoggingLog) << string;
25}
26
27void glib_printerr_handler(const gchar* string)
28{
29 qCWarning(GStreamerLoggingLog) << string;
30}
31
32void glib_log_handler(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data)
33{
34 Q_UNUSED(user_data);
35 const QString domain = log_domain ? QString::fromUtf8(log_domain) : QStringLiteral("GLib");
36 const QString msg = QString::fromUtf8(message);
37
38 if (msg.contains(QStringLiteral("External plugin loader failed"), Qt::CaseInsensitive)) {
39 g_externalPluginLoaderFailed.store(true);
40 }
41
42 if (msg.contains(QStringLiteral("pygobject initialization failed"), Qt::CaseInsensitive)) {
43 qCDebug(GStreamerLoggingLog) << domain << msg;
44 return;
45 }
46
47 switch (log_level & G_LOG_LEVEL_MASK) {
48 case G_LOG_LEVEL_ERROR:
49 case G_LOG_LEVEL_CRITICAL:
50 qCCritical(GStreamerLoggingLog) << domain << msg;
51 break;
52 case G_LOG_LEVEL_WARNING:
53 qCWarning(GStreamerLoggingLog) << domain << msg;
54 break;
55 case G_LOG_LEVEL_MESSAGE:
56 case G_LOG_LEVEL_INFO:
57 qCInfo(GStreamerLoggingLog) << domain << msg;
58 break;
59 case G_LOG_LEVEL_DEBUG:
60 default:
61 qCDebug(GStreamerLoggingLog) << domain << msg;
62 break;
63 }
64}
65
66} // anonymous namespace
67
68namespace GStreamer {
69
71{
72 g_externalPluginLoaderFailed.store(false);
73}
74
76{
77 return g_externalPluginLoaderFailed.load();
78}
79
81{
82 g_set_print_handler(glib_print_handler);
83 g_set_printerr_handler(glib_printerr_handler);
84 g_log_set_default_handler(glib_log_handler, nullptr);
85}
86
87void qtGstLog(GstDebugCategory* category, GstDebugLevel level, const gchar* file, const gchar* function, gint line,
88 GObject* object, GstDebugMessage* message, gpointer data)
89{
90 Q_UNUSED(data);
91
92 if (level > gst_debug_category_get_threshold(category)) {
93 return;
94 }
95
96 QMessageLogger log(file, line, function);
97
98 struct GFree
99 {
100 void operator()(gchar* p) const { g_free(p); }
101 };
102
103 const std::unique_ptr<gchar, GFree> object_info(gst_info_strdup_printf("%" GST_PTR_FORMAT, object));
104
105 switch (level) {
106 case GST_LEVEL_ERROR:
107 log.critical(GStreamerAPILog, "%s %s", object_info.get(), gst_debug_message_get(message));
108 break;
109 case GST_LEVEL_WARNING:
110 log.warning(GStreamerAPILog, "%s %s", object_info.get(), gst_debug_message_get(message));
111 break;
112 case GST_LEVEL_FIXME:
113 case GST_LEVEL_INFO:
114 log.info(GStreamerAPILog, "%s %s", object_info.get(), gst_debug_message_get(message));
115 break;
116 case GST_LEVEL_DEBUG:
117#ifdef QT_DEBUG
118 // In release builds LOG/TRACE/MEMDUMP are intentionally dropped to reduce
119 // noise. Only debug builds route these verbose levels through Qt logging.
120 case GST_LEVEL_LOG:
121 case GST_LEVEL_TRACE:
122 case GST_LEVEL_MEMDUMP:
123#endif
124 log.debug(GStreamerAPILog, "%s %s", object_info.get(), gst_debug_message_get(message));
125 break;
126 default:
127 break;
128 }
129}
130
132{
133 gst_debug_remove_log_function(gst_debug_log_default);
134 gst_debug_remove_log_function(GStreamer::qtGstLog);
135 gst_debug_add_log_function(GStreamer::qtGstLog, nullptr, nullptr);
136
137 if (!qEnvironmentVariableIsEmpty("GST_DEBUG")) {
138 return;
139 }
140
141 QSettings settings;
142 if (settings.contains(AppSettings::gstDebugLevelName)) {
143 const int level =
144 std::clamp(settings.value(AppSettings::gstDebugLevelName).toInt(), 0, static_cast<int>(GST_LEVEL_MEMDUMP));
145 gst_debug_set_default_threshold(static_cast<GstDebugLevel>(level));
146 }
147}
148
149void setDebugLevel(int level)
150{
151 if (!gst_is_initialized()) {
152 return;
153 }
154 const int clamped = std::clamp(level, 0, static_cast<int>(GST_LEVEL_MEMDUMP));
155 gst_debug_set_default_threshold(static_cast<GstDebugLevel>(clamped));
156 qCDebug(GStreamerLoggingLog) << "GStreamer debug threshold set to" << clamped;
157}
158
160{
161 GList* factories = gst_element_factory_list_get_elements(
162 static_cast<GstElementFactoryListType>(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO),
163 GST_RANK_NONE);
164
165 if (!factories) {
166 qCDebug(GStreamerDecoderRanksLog) << "No video decoder factories found";
167 return;
168 }
169
170 factories = g_list_sort(factories, [](gconstpointer lhs, gconstpointer rhs) -> gint {
171 const guint lhsRank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(lhs));
172 const guint rhsRank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(rhs));
173 if (lhsRank != rhsRank) {
174 return (lhsRank > rhsRank) ? -1 : 1;
175 }
176 return g_strcmp0(gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(lhs)),
177 gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(rhs)));
178 });
179
180 qCDebug(GStreamerDecoderRanksLog) << "Video decoder ranks:";
181 for (GList* node = factories; node != nullptr; node = node->next) {
182 GstElementFactory* factory = GST_ELEMENT_FACTORY(node->data);
183 GstPluginFeature* feature = GST_PLUGIN_FEATURE(factory);
184 const gchar* featureName = gst_plugin_feature_get_name(feature);
185 const guint rank = gst_plugin_feature_get_rank(feature);
186 const gchar* klass = gst_element_factory_get_klass(factory);
187 const bool isHw = GStreamer::isHardwareDecoderFactory(factory);
188
189 GstPlugin* plugin = gst_plugin_feature_get_plugin(feature);
190 const gchar* pluginName = plugin ? gst_plugin_get_name(plugin) : "?";
191
192 qCDebug(GStreamerDecoderRanksLog).noquote()
193 << QStringLiteral(" [%1] %2/%3 rank=%4 (%5)")
194 .arg(isHw ? QStringLiteral("HW") : QStringLiteral("SW"), QString::fromUtf8(pluginName),
195 QString::fromUtf8(featureName))
196 .arg(rank)
197 .arg(QString::fromUtf8(klass));
198
199 if (plugin) {
200 gst_object_unref(plugin);
201 }
202 }
203
204 gst_plugin_feature_list_free(factories);
205}
206
207} // namespace GStreamer
#define QGC_LOGGING_CATEGORY_ON(name, categoryStr)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void configureDebugLogging()
void qtGstLog(GstDebugCategory *category, GstDebugLevel level, const gchar *file, const gchar *function, gint line, GObject *object, GstDebugMessage *message, gpointer data)
void setDebugLevel(int level)
bool didExternalPluginLoaderFail()
void redirectGLibLogging()
bool isHardwareDecoderFactory(GstElementFactory *factory)
void resetExternalPluginLoaderFailure()
void logDecoderRanks()