* -------------------------------------------------------------------------
* This file is part of the Vision SDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* Vision SDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: Collect information about plug-ins available in the environment.
* Author: MindX SDK
* Create: 2020
* History: NA
*/
#include "MxTools/PluginInspector/PluginInspector.h"
#include <sys/stat.h>
#include <gst/gst.h>
#include <fstream>
#include <iostream>
#include <map>
#include <regex>
#include <cstdarg>
#include <vector>
#include <iomanip>
#include <utility>
#include <dvpp/securec.h>
#include "nlohmann/json.hpp"
#define G_PARAM_SPEC_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), g_param_spec_types[18], GParamSpecValueArray))
namespace {
using namespace MxBase;
using ustring = std::string;
const ustring EL_NAME = "name";
const ustring EL_FACTORY = "Factory Details";
const ustring EL_FACTORY_RANK = "Rank";
const ustring EL_FACTORY_GRAPH_AGENT = "GraphAgentElement";
const ustring EL_FACTORY_CLASSIFICATION = "Classification";
const ustring EL_FACTORY_KLASS = "Klass";
const ustring EL_PLUGIN = "Plugin Details";
const ustring EL_PLUGIN_NAME = "Name";
const ustring EL_PLUGIN_DESCRIPTION = "Description";
const ustring EL_PLUGIN_FILENAME = "Filename";
const ustring EL_PLUGIN_VERSION = "Version";
const ustring EL_PLUGIN_LICSENCE = "License";
const ustring EL_PLUGIN_SRCMODULE = "Source module";
const ustring EL_PLUGIN_SRCRELEASEDATA = "Source release date";
const ustring EL_PLUGIN_BINPACKAGE = "Binary package";
const ustring EL_PLUGIN_OURL = "Origin URL";
const ustring EL_PADTEMPLATES = "Pad Templates";
const ustring EL_PADTEMPLATES_SINK = "SINK template";
const ustring EL_PADTEMPLATES_SRC = "SRC template";
const ustring EL_PADTEMPLATES_NAME = "padname";
const ustring EL_PADTEMPLATES_AVAIL = "Availability";
const ustring EL_PADTEMPLATES_CAP = "Capabilities";
const ustring EL_PAD = "Pads";
const ustring EL_PAD_NAME = "name";
const ustring EL_PAD_SRC = "SRC";
const ustring EL_PAD_SINK = "SINK";
const ustring EL_PAD_TEMPLATE = "Pad Template";
const ustring EL_PROPS = "Element Properties";
const ustring EL_PROPS_BLURB = "blurb";
const ustring EL_PROPS_FLAGS = "flags";
const ustring EL_PROPS_VALUE_TYPE = "type";
const ustring EL_PROPS_VALUE_DEFAULT = "default";
const ustring EL_PROPS_VALUE_MAX = "range_max";
const ustring EL_PROPS_VALUE_MIN = "range_min";
const ustring EL_PROPS_VALUE_LIST = "choice";
std::map<ustring, int> supportedPlugins;
const int MAXIMUM_NUMBER_PLUGINS = 500;
const int NUM_RANK_LEVELS = 4;
const int LEN_RANK_NAME = 50;
const int MAX_FILE_LINES = 100000;
std::map<ustring, ustring> pluginKlassMap;
const std::vector<std::string> NOT_SHOW_PLUGINS = {
"rtpbin", "rtpjitterbuffer", "rtpptdemux", "rtpsession", "rtprtxqueue", "rtprtxreceive", "rtprtxsend",
"rtpssrcdemux", "rtpmux", "rtpdtmfmux", "rtpfunnel", "rtpac3depay", "rtpac3pay", "rtpbvdepay", "rtpbvpay",
"rtpceltdepay", "rtpceltpay", "rtpdvdepay", "rtpdvpay", "rtpgstdepay", "rtpgstpay", "rtpilbcpay", "rtpilbcdepay",
"rtpg722depay", "rtpg722pay", "rtpg723depay", "rtpg723pay", "rtpg726depay", "rtpg726pay", "rtpg729depay",
"rtpg729pay", "rtpgsmdepay", "rtpgsmpay", "rtpamrdepay", "rtpamrpay", "rtppcmadepay", "rtppcmudepay",
"rtppcmupay", "rtppcmapay", "rtpmpadepay", "rtpmpapay", "rtpmparobustdepay", "rtpmpvdepay", "rtpmpvpay",
"rtpopusdepay", "rtpopuspay", "rtph261pay", "rtph261depay", "rtph263ppay", "rtph263pdepay", "rtph263depay",
"rtph263pay", "rtph264depay", "rtph264pay", "rtph265depay", "rtph265pay", "rtpj2kdepay", "rtpj2kpay",
"rtpjpegdepay", "rtpjpegpay", "rtpklvdepay", "rtpklvpay", "rtpL8pay", "rtpL8depay", "rtpL16pay", "rtpL16depay",
"rtpL24pay", "rtpL24depay", "asteriskh263", "rtpmp1sdepay", "rtpmp2tdepay", "rtpmp2tpay", "rtpmp4vpay",
"rtpmp4vdepay", "rtpmp4apay", "rtpmp4adepay", "rtpmp4gdepay", "rtpmp4gpay", "rtpqcelpdepay", "rtpqdm2depay",
"rtpsbcdepay", "rtpsbcpay", "rtpsirenpay", "rtpsirendepay", "rtpspeexpay", "rtpspeexdepay", "rtpsv3vdepay",
"rtptheoradepay", "rtptheorapay", "rtpvorbisdepay", "rtpvorbispay", "rtpvp8depay", "rtpvp8pay", "rtpvp9depay",
"rtpvp9pay", "rtpvrawdepay", "rtpvrawpay", "rtpstreampay", "rtpstreamdepay", "rtpredenc", "rtpreddec",
"rtpulpfecdec", "rtpulpfecenc", "rtpstorage", "rtspsrc", "rtpsrc", "rtpsink", "rtpdec", "h263parse", "h264parse",
"diracparse", "mpegvideoparse", "mpeg4videoparse", "pngparse", "jpeg2000parse", "h265parse", "vc1parse", "shmsrc",
"shmsink", "capsfilter", "concat", "dataurisrc", "downloadbuffer", "fakesrc",
"fdsrc", "fdsink", "filesrc", "funnel", "identity", "input-selector", "output-selector",
"queue2", "typefind", "multiqueue", "valve", "streamiddemux", "v4l2src", "v4l2sink",
"srtpenc", "srtpdec"
};
const std::map<std::string, std::vector<std::string>> GSTREAMER_SHOW_PROPS = {
{"test_appsrc", {"name", "blocksize"}},
{"test_appsink", {"name", "blocksize"}},
{"test_queue", {"name", "max-size-buffers"}},
{"test_fakesink", {"name"}},
{"test_tee", {"name"}},
{"test_filesink", {"name", "append", "buffer-mode", "buffer-size", "location", "max-transient-error-timeout",
"o-sync"}},
};
std::vector<std::pair<ustring, ustring>> klassNamePairs {
std::pair<ustring, ustring>("infer", "Inference"),
std::pair<ustring, ustring>("Infer", "Inference"),
std::pair<ustring, ustring>("Codec/Encoder", "Encoder"),
std::pair<ustring, ustring>("Codec/Decoder", "Decoder"),
std::pair<ustring, ustring>("parse", "Parser"),
std::pair<ustring, ustring>("Parse", "Parser"),
std::pair<ustring, ustring>("demux", "Demux"),
std::pair<ustring, ustring>("Demux", "Demux"),
std::pair<ustring, ustring>("mux", "Mux"),
std::pair<ustring, ustring>("Mux", "Mux")
};
const std::vector<std::string> SCREEN_DISPLAY_PLUGINS = {
"mxpi_opencvosd", "mxpi_object2osdinstances", "mxpi_class2osdinstances", "mxpi_osdinstancemerger",
"mxpi_channelselector", "mxpi_channelimagesstitcher", "mxpi_channelosdcoordsconverter", "mxpi_bufferstablizer",
};
const std::vector<std::string> DEBUG_PLUGINS = {
"mxpi_dumpdata", "mxpi_loaddata",
};
const std::vector<std::string> INTELLIGENT_VIDEO_PLUGINS = {
"mxpi_motsimplesort", "mxpi_motsimplesortV2", "mxpi_facealignment", "mxpi_qualitydetection",
};
const std::vector<std::string> MODEL_PROCESS_PLUGINS = {
"mxpi_objectpostprocessor", "mxpi_classpostprocessor", "mxpi_semanticsegpostprocessor",
"mxpi_textgenerationpostprocessor", "mxpi_keypointpostprocessor",
};
const std::vector<std::string> MODEL_PLUGINS = {
"mxpi_modelinfer", "mxpi_tensorinfer",
};
const std::vector<std::string> MEDIA_DATA_HANDLE_PLUGINS = {
"mxpi_imagedecoder", "mxpi_imageresize", "mxpi_imagecrop", "mxpi_videodecoder", "mxpi_videoencoder",
"mxpi_imagedecoder", "mxpi_imageencoder", "mxpi_imagenormalize", "mxpi_opencvcentercrop", "mxpi_warpperspective",
"mxpi_rotation",
};
const std::vector<std::string> STREAMING_PLUGINS = {
"mxpi_parallel2serial", "mxpi_distributor", "mxpi_synchronize", "queue", "tee", "mxpi_datatransfer",
"mxpi_nmsoverlapedroi", "mxpi_nmsoverlapedroiv2", "mxpi_roigenerator", "mxpi_semanticsegstitcher",
"mxpi_objectselector", "mxpi_skipframe",
};
const std::vector<std::string> OUTPUT_PLUGINS = {
"mxpi_dataserialize", "appsink", "fakesink", "filesink",
};
const std::vector<std::string> INPUT_PLUGINS = {
"appsrc", "mxpi_rtspsrc",
};
const std::map<std::string, std::vector<std::string>> PLUGINS_CLASSIFICATION = {
{"Input", INPUT_PLUGINS},
{"Output", OUTPUT_PLUGINS},
{"Streaming", STREAMING_PLUGINS},
{"Media Data Process", MEDIA_DATA_HANDLE_PLUGINS},
{"Model", MODEL_PLUGINS},
{"Model Postprocess", MODEL_PROCESS_PLUGINS},
{"Intelligent Video", INTELLIGENT_VIDEO_PLUGINS},
{"Debug", DEBUG_PLUGINS},
{"Screen Display", SCREEN_DISPLAY_PLUGINS},
};
ustring GetFactoryClassification(GstElementFactory *factory, GstElement *element, ustring& klass)
{
ustring factoryName(GST_OBJECT_NAME(factory));
for (auto iter = PLUGINS_CLASSIFICATION.begin(); iter != PLUGINS_CLASSIFICATION.end(); ++iter) {
if (std::find(iter->second.begin(), iter->second.end(), factoryName) != iter->second.end()) {
return iter->first;
}
}
if (pluginKlassMap.find(factoryName) != pluginKlassMap.end()) {
return pluginKlassMap[factoryName];
}
if (GST_OBJECT_FLAG_IS_SET(element, GST_ELEMENT_FLAG_SOURCE)) {
return "Input";
}
if (GST_OBJECT_FLAG_IS_SET(element, GST_ELEMENT_FLAG_SINK)) {
return "Output";
}
for (auto pair : klassNamePairs) {
if (klass.find(pair.first) != ustring::npos) {
return pair.second;
}
}
return "Other";
}
inline int UpdateBestRank(int rank, int currentRank, int bestRank, int idx, int bestIdx)
{
bestIdx = (abs(rank - currentRank) < abs(rank - bestRank)) ? idx : bestIdx;
return bestIdx;
}
void GetRankName(char s[], const int size, gint rank)
{
static const int ranks[NUM_RANK_LEVELS] = {
GST_RANK_NONE, GST_RANK_MARGINAL, GST_RANK_SECONDARY, GST_RANK_PRIMARY
};
static const char *rankNames[NUM_RANK_LEVELS] = {"none", "marginal", "secondary", "primary"};
int len = 0;
int bestIdx = 0;
for (int i = 0; i < NUM_RANK_LEVELS; i++) {
if (rank == ranks[i]) {
len = sprintf_s(s, size, "%s (%d)", rankNames[i], rank);
if (rank != len) {
std::cout << "[WARNING] " << "PluginInspector.cpp" << ":" << __LINE__
<< "use sprintf_s secure function write " << len
<< " characters, but expect write " << rank << " characters. " << std::endl;
}
return;
}
bestIdx = UpdateBestRank(rank, ranks[i], ranks[bestIdx], i, bestIdx);
}
len = sprintf_s(s, size, "%s %c %d (%d)", rankNames[bestIdx],
(rank - ranks[bestIdx] > 0) ? '+' : '-', abs(ranks[bestIdx] - rank), rank);
if (rank != len) {
std::cout << "[WARNING] " << "PluginInspector.cpp" << ":" << __LINE__
<< " use sprintf_s secure function write " << len
<< " characters, but expect write " << rank << " characters. " << std::endl;
}
return;
}
nlohmann::json GetFactoryInfoJson(GstElementFactory *factory, GstElement *element)
{
nlohmann::json jsonFactory = nlohmann::json::object();
gchar **keys, **k;
guint rank;
char rankName[LEN_RANK_NAME];
if (memset_s(rankName, sizeof(rankName), 0, sizeof(rankName)) != EOK) {
LogError << "Call memset_s failed." << GetErrorInfo(APP_ERR_COMM_ALLOC_MEM);
return {};
}
rank = gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory));
GetRankName(rankName, LEN_RANK_NAME, rank);
jsonFactory[EL_FACTORY_RANK] = rankName;
jsonFactory[EL_FACTORY_GRAPH_AGENT] = false;
keys = gst_element_factory_get_metadata_keys(factory);
if (keys != nullptr) {
for (k = keys; *k != nullptr; ++k) {
const gchar *val;
gchar *key = *k;
val = gst_element_factory_get_metadata(factory, key);
key[0] = g_ascii_toupper(key[0]);
jsonFactory[key] = val;
}
g_strfreev(keys);
}
ustring klass;
if (jsonFactory.find(EL_FACTORY_KLASS) != jsonFactory.end()) {
klass = jsonFactory[EL_FACTORY_KLASS];
}
ustring classfication = GetFactoryClassification(factory, element, klass);
jsonFactory[EL_FACTORY_CLASSIFICATION] = classfication;
return jsonFactory;
}
void GetFormattedDate(const gchar *releaseDate, nlohmann::json& jsonPlugin)
{
if (releaseDate != nullptr) {
const gchar *tz = "(UTC)";
gchar *str = nullptr;
gchar *sep = nullptr;
str = g_strdup(releaseDate);
if (str != nullptr) {
sep = strstr(str, "T");
if (sep == nullptr) {
tz = "";
}
ustring data = str;
data.append(tz);
jsonPlugin[EL_PLUGIN_SRCRELEASEDATA] = data;
g_free(str);
}
}
}
nlohmann::json GetPluginInfoJson(GstElementFactory *factory)
{
nlohmann::json jsonPlugin = nlohmann::json::object();
GstPlugin *plugin = nullptr;
plugin = gst_plugin_feature_get_plugin(GST_PLUGIN_FEATURE(factory));
if (!plugin) {
return {};
}
const gchar *releaseDate = gst_plugin_get_release_date_string(plugin);
const gchar *filename = gst_plugin_get_filename(plugin);
jsonPlugin[EL_PLUGIN_NAME] = gst_plugin_get_name(plugin);
jsonPlugin[EL_PLUGIN_DESCRIPTION] = gst_plugin_get_description(plugin);
jsonPlugin[EL_PLUGIN_FILENAME] = (filename != nullptr) ? filename : "(null)";
jsonPlugin[EL_PLUGIN_VERSION] = gst_plugin_get_version(plugin);
jsonPlugin[EL_PLUGIN_LICSENCE] = gst_plugin_get_license(plugin);
jsonPlugin[EL_PLUGIN_SRCMODULE] = gst_plugin_get_source(plugin);
GetFormattedDate(releaseDate, jsonPlugin);
jsonPlugin[EL_PLUGIN_BINPACKAGE] = gst_plugin_get_package(plugin);
jsonPlugin[EL_PLUGIN_OURL] = gst_plugin_get_origin(plugin);
gst_object_unref(plugin);
return jsonPlugin;
}
void SetCapsInfoJson(nlohmann::json& result, const ustring& field, const nlohmann::json& structJson)
{
if (result.find(field) != result.end()) {
if (!result[field].is_array()) {
nlohmann::json jsontmp = result[field];
result[field] = nlohmann::json::array();
result[field][result[field].size()] = jsontmp;
}
result[field][result[field].size()] = structJson;
} else {
result[field] = structJson;
}
}
ustring GetStructureStr(const GstStructure* structure, const GstCapsFeatures* features)
{
ustring str = gst_structure_get_name(structure);
if (features && (gst_caps_features_is_any(features) ||
!gst_caps_features_is_equal(features, GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))) {
gchar *featureString = gst_caps_features_to_string(features);
str.append("(");
str.append(featureString);
str.append(")");
g_free(featureString);
}
return str;
}
inline bool IsCapsAnyOrEmpty(const GstCaps *caps)
{
return gst_caps_is_any(caps) || gst_caps_is_empty(caps);
}
inline nlohmann::json CapsInfoAnyOrEmpty(const GstCaps *caps)
{
if (gst_caps_is_any(caps)) {
return "ANY";
}
return "EMPTY";
}
nlohmann::json GetCapsInfoJson(GstCaps *caps)
{
nlohmann::json result = nlohmann::json::object();
guint i;
if (IsCapsAnyOrEmpty(caps)) {
return CapsInfoAnyOrEmpty(caps);
}
for (i = 0; i < gst_caps_get_size(caps); i++) {
nlohmann::json structJson = nlohmann::json::object();
GstStructure *structure = gst_caps_get_structure(caps, i);
GstCapsFeatures *features = gst_caps_get_features(caps, i);
ustring str = GetStructureStr(structure, features);
guint len, j;
len = (guint)gst_structure_n_fields(structure);
for (j = 0; j < len; j++) {
const gchar *filed = gst_structure_nth_field_name(structure, j);
const GValue *value = gst_structure_get_value(structure, filed);
gchar *value_str = gst_value_serialize(value);
if (value_str != nullptr) {
structJson[filed] = value_str;
g_free(value_str);
}
}
SetCapsInfoJson(result, str, structJson);
}
return result;
}
inline void GatherPadTemplates(nlohmann::json& allPadTemplates,
const nlohmann::json& srcPadTemplate, const nlohmann::json& sinkPadTemplate)
{
if (srcPadTemplate.size()) {
allPadTemplates[EL_PADTEMPLATES_SRC] = srcPadTemplate;
}
if (sinkPadTemplate.size()) {
allPadTemplates[EL_PADTEMPLATES_SINK] = sinkPadTemplate;
}
}
struct Index {
size_t srcIdx;
size_t sinkIdx;
};
inline void AddSubPadTemplate(nlohmann::json& srcPadTemplate,
nlohmann::json& sinkPadTemplate, const GstStaticPadTemplate* padTemplate,
struct Index& index, const nlohmann::json& jsonPad)
{
if (padTemplate->direction == GST_PAD_SRC) {
srcPadTemplate[index.srcIdx++] = jsonPad;
} else {
sinkPadTemplate[index.sinkIdx++] = jsonPad;
}
}
nlohmann::json GetPadsTemplatesInfoJson(GstElementFactory* factory)
{
nlohmann::json allPadTemplates = nlohmann::json::object();
nlohmann::json srcPadTemplate = nlohmann::json::array();
nlohmann::json sinkPadTemplate = nlohmann::json::array();
Index index = {0, 0};
GstStaticPadTemplate* padTemplate = nullptr;
const GList *pads = gst_element_factory_get_static_pad_templates(factory);
while (pads) {
nlohmann::json jsonPad = nlohmann::json::object();
padTemplate = (GstStaticPadTemplate*)(pads->data);
pads = g_list_next(pads);
jsonPad[EL_PADTEMPLATES_NAME] = padTemplate->name_template;
if (padTemplate->presence == GST_PAD_ALWAYS) {
jsonPad[EL_PADTEMPLATES_AVAIL] = "Always";
} else if (padTemplate->presence == GST_PAD_SOMETIMES) {
jsonPad[EL_PADTEMPLATES_AVAIL] = "Sometimes";
} else if (padTemplate->presence == GST_PAD_REQUEST) {
jsonPad[EL_PADTEMPLATES_AVAIL] = "On request";
} else {
jsonPad[EL_PADTEMPLATES_AVAIL] = "UNKNOWN";
}
if (padTemplate->static_caps.string) {
GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps);
jsonPad[EL_PADTEMPLATES_CAP] = GetCapsInfoJson(caps);
gst_caps_unref(caps);
}
AddSubPadTemplate(srcPadTemplate, sinkPadTemplate, padTemplate, index, jsonPad);
}
GatherPadTemplates(allPadTemplates, srcPadTemplate, sinkPadTemplate);
return allPadTemplates;
}
void SetPadTemplates(const GstPad *pad, nlohmann::json& jsonPads, const gchar *name)
{
if (pad->padtemplate) {
jsonPads[EL_PAD_TEMPLATE] = pad->padtemplate->name_template;
} else {
jsonPads[EL_PAD_TEMPLATE] = name;
}
}
void FillPadJsonByType(nlohmann::json& jsonPads, const nlohmann::json& jsonEachPad,
const ustring& sinkSrcPadName)
{
if (jsonPads.find(sinkSrcPadName) != jsonPads.end()) {
if (!jsonPads[sinkSrcPadName].is_array()) {
nlohmann::json jsontmp = jsonPads[sinkSrcPadName];
jsonPads[sinkSrcPadName] = nlohmann::json::array();
jsonPads[sinkSrcPadName][jsonPads[sinkSrcPadName].size()] = jsontmp;
}
jsonPads[sinkSrcPadName][jsonPads[sinkSrcPadName].size()] = jsonEachPad;
} else {
jsonPads[sinkSrcPadName] = jsonEachPad;
}
}
void FillPadsJson(GstPad* pad, nlohmann::json& jsonPads, const nlohmann::json& jsonEachPad)
{
if (gst_pad_get_direction(pad) == GST_PAD_SRC) {
FillPadJsonByType(jsonPads, jsonEachPad, EL_PAD_SRC);
} else {
FillPadJsonByType(jsonPads, jsonEachPad, EL_PAD_SINK);
}
}
nlohmann::json GetPadsInfoJson(GstElement *element)
{
nlohmann::json jsonPads = nlohmann::json::object();
GList *pads = nullptr;
GstPad *pad = nullptr;
if (!element->numpads) {
return {};
}
pads = element->pads;
while (pads) {
nlohmann::json jsonEachPad = nlohmann::json::object();
gchar *name = nullptr;
pad = GST_PAD(pads->data);
pads = g_list_next(pads);
name = gst_pad_get_name(pad);
jsonEachPad[EL_PAD_NAME] = name;
SetPadTemplates(pad, jsonEachPad, name);
g_free(name);
FillPadsJson(pad, jsonPads, jsonEachPad);
}
return jsonPads;
}
ustring UtilityStringFormat(const char *fmt, ...)
{
va_list ap;
int count = 0;
int limit = 10;
unsigned int len1 = 0;
unsigned int bufSize = 1024;
while ((++count) <= limit) {
bufSize = bufSize << 1;
ustring tmpBuf(bufSize, '\0');
va_start(ap, fmt);
len1 = (unsigned int)vsnprintf_s(&tmpBuf[0], bufSize, bufSize - 1, fmt, ap);
va_end(ap);
if (len1 < bufSize) {
break;
}
}
if (count > limit) {
std::cout << "[ERROR] " << "PluginInspector.cpp" << ":" << __LINE__
<< " use vsnprintf_s secure function write characters failed. return value: " << len1 << std::endl;
return ustring();
}
ustring buf(len1 + static_cast<unsigned int>(1), '\0');
va_start(ap, fmt);
unsigned int len2 = (unsigned int)vsnprintf_s(&buf[0], buf.size(), buf.size() - 1, fmt, ap);
va_end(ap);
if (len1 != len2) {
std::cout << "[WARNING] " << "PluginInspector.cpp" << ":" << __LINE__
<< " use vsnprintf_s secure function write " << len2
<< " characters, but expect write " << len1 << " characters. " << std::endl;
}
buf.pop_back();
return buf;
}
void GetJsonFromParamPointer(const GParamSpec *param, nlohmann::json &jsonProp)
{
if (param->value_type != G_TYPE_POINTER) {
ustring type = UtilityStringFormat("Pointer of type %s",
g_type_name(param->value_type));
jsonProp[EL_PROPS_VALUE_TYPE] = type;
} else {
jsonProp[EL_PROPS_VALUE_TYPE] = "Pointer";
}
}
void GetJsonFromValueArray(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecValueArray *pvarray = G_PARAM_SPEC_ARRAY(param);
if (pvarray->element_spec) {
ustring type = UtilityStringFormat("Array of GValues of type %s",
g_type_name(pvarray->element_spec->value_type));
gchar *str = gst_value_serialize(&value);
jsonProp[EL_PROPS_VALUE_TYPE] = type;
jsonProp[EL_PROPS_VALUE_DEFAULT] = str;
g_free(str);
} else {
gchar *str = gst_value_serialize(&value);
jsonProp[EL_PROPS_VALUE_TYPE] = "Array of GValues";
jsonProp[EL_PROPS_VALUE_DEFAULT] = str;
g_free(str);
}
}
void GetJsonFromGstTypeArray(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GstParamSpecArray *parray = GST_PARAM_SPEC_ARRAY_LIST(param);
if (parray->element_spec) {
ustring type = UtilityStringFormat(
"GstValueArray of GValues of type %s", g_type_name(parray->element_spec->value_type));
gchar *str = gst_value_serialize(&value);
jsonProp[EL_PROPS_VALUE_TYPE] = type;
jsonProp[EL_PROPS_VALUE_DEFAULT] = str;
g_free(str);
} else {
gchar *str = gst_value_serialize(&value);
jsonProp[EL_PROPS_VALUE_TYPE] = "GstValueArray of GValues";
jsonProp[EL_PROPS_VALUE_DEFAULT] = str;
g_free(str);
}
}
void GetJsonFromParamEnum(const GParamSpec* param, nlohmann::json& jsonProp, const GValue& value)
{
nlohmann::json jsonVal = nlohmann::json::object();
GEnumValue* values;
guint j = 0;
ustring enumValue;
GTypeClass* typeClass = (GTypeClass*)g_type_class_ref(param->value_type);
values = G_ENUM_CLASS(typeClass)->values;
enumValue = UtilityStringFormat("%d", g_value_get_enum(&value));
jsonProp[EL_PROPS_VALUE_TYPE] = "Enum";
jsonProp[EL_PROPS_VALUE_DEFAULT] = enumValue;
while (values[j].value_name) {
ustring key = UtilityStringFormat("%d", values[j].value);
ustring value = UtilityStringFormat("%-16s - %s", values[j].value_nick, values[j].value_name);
jsonVal[key] = value;
j++;
}
jsonProp[EL_PROPS_VALUE_LIST] = jsonVal;
g_type_class_unref(typeClass);
}
void GetJsonFromParamFlags(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
nlohmann::json jsonVal = nlohmann::json::object();
GParamSpecFlags *pflags = G_PARAM_SPEC_FLAGS(param);
GFlagsValue *vals;
ustring defaultVal = UtilityStringFormat("0x%08x", g_value_get_flags(&value));
vals = pflags->flags_class->values;
jsonProp[EL_PROPS_VALUE_TYPE] = "Flags";
jsonProp[EL_PROPS_VALUE_DEFAULT] = defaultVal;
while (vals[0].value_name) {
ustring key = UtilityStringFormat("0x%08x", vals[0].value);
ustring value = UtilityStringFormat("%-16s - %s", vals[0].value_nick,
vals[0].value_name);
jsonVal[key] = value;
++vals;
}
jsonProp[EL_PROPS_VALUE_LIST] = jsonVal;
}
void PropsValueTypeDefault(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
if (G_IS_PARAM_SPEC_ENUM(param)) {
GetJsonFromParamEnum(param, jsonProp, value);
} else if (G_IS_PARAM_SPEC_FLAGS(param)) {
GetJsonFromParamFlags(param, jsonProp, value);
} else if (G_IS_PARAM_SPEC_OBJECT(param)) {
ustring type = UtilityStringFormat("Object of type %s",
g_type_name(param->value_type));
jsonProp[EL_PROPS_VALUE_TYPE] = type;
} else if (G_IS_PARAM_SPEC_BOXED(param)) {
ustring type = UtilityStringFormat("Boxed pointer of type %s",
g_type_name(param->value_type));
jsonProp[EL_PROPS_VALUE_TYPE] = type;
} else if (G_IS_PARAM_SPEC_POINTER(param)) {
GetJsonFromParamPointer(param, jsonProp);
} else if (param->value_type == g_value_array_get_type()) {
GetJsonFromValueArray(param, jsonProp, value);
} else if (GST_IS_PARAM_SPEC_FRACTION(param)) {
GstParamSpecFraction *pfraction = GST_PARAM_SPEC_FRACTION(param);
ustring defaultString = UtilityStringFormat(
"%d/%d", gst_value_get_fraction_numerator(&value), gst_value_get_fraction_denominator(&value)
);
ustring minString = UtilityStringFormat("%d/%d", pfraction->min_num,
pfraction->min_den);
ustring maxString = UtilityStringFormat("%d/%d", pfraction->max_num,
pfraction->max_den);
jsonProp[EL_PROPS_VALUE_TYPE] = "Fraction";
jsonProp[EL_PROPS_VALUE_DEFAULT] = defaultString;
jsonProp[EL_PROPS_VALUE_MAX] = maxString;
jsonProp[EL_PROPS_VALUE_MIN] = minString;
} else if (param->value_type == GST_TYPE_ARRAY) {
GetJsonFromGstTypeArray(param, jsonProp, value);
} else {
jsonProp[EL_PROPS_VALUE_TYPE] = "Unknown";
}
}
void GetJsonFromValueTypeString(nlohmann::json &jsonProp, const GValue &value)
{
const char *stringVal = g_value_get_string(&value);
jsonProp[EL_PROPS_VALUE_TYPE] = "String";
if (stringVal == nullptr) {
jsonProp[EL_PROPS_VALUE_DEFAULT] = "null";
} else {
jsonProp[EL_PROPS_VALUE_DEFAULT] = stringVal;
}
}
void GetJsonFromValueTypeUlong(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecULong *pulong = G_PARAM_SPEC_ULONG(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Unsigned Long";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%lu", g_value_get_ulong(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%lu", pulong->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%lu", pulong->minimum);
}
void GetJsonFromValueTypeLong(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecLong *plong = G_PARAM_SPEC_LONG(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Long";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%ld", g_value_get_long(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%ld", plong->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%ld", plong->minimum);
}
void GetJsonFromValueTypeUint(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecUInt *puint = G_PARAM_SPEC_UINT(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Unsigned Integer";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%u", g_value_get_uint(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%u", puint->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%u", puint->minimum);
}
void GetJsonFromValueTypeInt(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecInt *pint = G_PARAM_SPEC_INT(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Integer";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%d", g_value_get_int(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%d", pint->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%d", pint->minimum);
}
void GetJsonFromValueTypeUint64(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecUInt64 *puint64 = G_PARAM_SPEC_UINT64(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Unsigned Integer64";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%llu", g_value_get_uint64(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%llu", puint64->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%llu", puint64->minimum);
}
void GetJsonFromValueTyeInt64(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecInt64 *pint64 = G_PARAM_SPEC_INT64(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Integer64";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%lld", g_value_get_int64(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%lld", pint64->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%lld", pint64->minimum);
}
void GetJsonFromValueTypeFloat(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecFloat *pfloat = G_PARAM_SPEC_FLOAT(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Float";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%g", g_value_get_float(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%g", pfloat->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%g", pfloat->minimum);
}
void GetJsonFromValueTypeDouble(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
GParamSpecDouble *pdouble = G_PARAM_SPEC_DOUBLE(param);
jsonProp[EL_PROPS_VALUE_TYPE] = "Double";
jsonProp[EL_PROPS_VALUE_DEFAULT] = UtilityStringFormat("%g", g_value_get_double(&value));
jsonProp[EL_PROPS_VALUE_MAX] = UtilityStringFormat("%g", pdouble->maximum);
jsonProp[EL_PROPS_VALUE_MIN] = UtilityStringFormat("%g", pdouble->minimum);
}
void GetJsonFromValueTypeBoolean(nlohmann::json &jsonProp, const GValue &value)
{
gboolean boolVal = g_value_get_boolean(&value);
jsonProp[EL_PROPS_VALUE_TYPE] = "Boolean";
jsonProp[EL_PROPS_VALUE_DEFAULT] = boolVal ? "true" : "false";
}
void GetPropJsonFromTypes(const GParamSpec *param, nlohmann::json &jsonProp, const GValue &value)
{
switch (G_VALUE_TYPE(&value)) {
case G_TYPE_STRING: {
GetJsonFromValueTypeString(jsonProp, value);
break;
}
case G_TYPE_BOOLEAN: {
GetJsonFromValueTypeBoolean(jsonProp, value);
break;
}
case G_TYPE_ULONG: {
GetJsonFromValueTypeUlong(param, jsonProp, value);
break;
}
case G_TYPE_LONG: {
GetJsonFromValueTypeLong(param, jsonProp, value);
break;
}
case G_TYPE_UINT: {
GetJsonFromValueTypeUint(param, jsonProp, value);
break;
}
case G_TYPE_INT: {
GetJsonFromValueTypeInt(param, jsonProp, value);
break;
}
case G_TYPE_UINT64: {
GetJsonFromValueTypeUint64(param, jsonProp, value);
break;
}
case G_TYPE_INT64: {
GetJsonFromValueTyeInt64(param, jsonProp, value);
break;
}
case G_TYPE_FLOAT: {
GetJsonFromValueTypeFloat(param, jsonProp, value);
break;
}
case G_TYPE_DOUBLE: {
GetJsonFromValueTypeDouble(param, jsonProp, value);
break;
}
case G_TYPE_CHAR:
case G_TYPE_UCHAR:
default:
PropsValueTypeDefault(param, jsonProp, value);
break;
}
}
void SetElementProps(GObject *obj, nlohmann::json &jsonPropsOut, GParamSpec *param, nlohmann::json& jsonProp)
{
if (obj == nullptr) {
LogError << "Failed to set element props, object ptr is nullptr." << GetErrorInfo(APP_ERR_COMM_INVALID_POINTER);
return;
}
std::string elementName = GST_OBJECT_NAME(obj);
auto iter = GSTREAMER_SHOW_PROPS.find(elementName);
if (iter != GSTREAMER_SHOW_PROPS.end()) {
if (std::find(iter->second.begin(), iter->second.end(), g_param_spec_get_name(param)) != iter->second.end()) {
jsonPropsOut[g_param_spec_get_name(param)] = jsonProp;
}
return;
}
jsonPropsOut[g_param_spec_get_name(param)] = jsonProp;
}
void GetPropsInfoJsonJsonIteration(GObject *obj, nlohmann::json &jsonPropsOut, gboolean readable,
gboolean firstFlag, GParamSpec *param)
{
nlohmann::json jsonProp = nlohmann::json::object();
ustring flags = "";
GValue value = {};
jsonProp[EL_PROPS_BLURB] = g_param_spec_get_blurb(param);
g_value_init(&value, param->value_type);
firstFlag = TRUE;
readable = FALSE;
readable = !!(param->flags & G_PARAM_READABLE);
if (readable && obj != nullptr) {
g_object_get_property(obj, param->name, &value);
} else {
g_param_value_set_default(param, &value);
}
if (readable) {
flags.append((firstFlag) ? "readable" : ", readable");
firstFlag = FALSE;
}
if (param->flags & G_PARAM_WRITABLE) {
flags.append((firstFlag) ? "writable" : ", writable");
firstFlag = FALSE;
}
if (param->flags & G_PARAM_DEPRECATED) {
flags.append((firstFlag) ? "deprecated" : ", deprecated");
firstFlag = FALSE;
}
if (param->flags & GST_PARAM_CONTROLLABLE) {
flags.append((firstFlag) ? "controllable" : ", controllable");
firstFlag = FALSE;
}
if (param->flags & GST_PARAM_MUTABLE_PLAYING) {
flags.append(", changeable in NULL, READY, PAUSED or PLAYING state");
} else if (param->flags & GST_PARAM_MUTABLE_PAUSED) {
flags.append(", changeable only in NULL, READY or PAUSED state");
} else if (param->flags & GST_PARAM_MUTABLE_READY) {
flags.append(", changeable only in NULL or READY state");
}
jsonProp[EL_PROPS_FLAGS] = flags;
GetPropJsonFromTypes(param, jsonProp, value);
g_value_reset(&value);
SetElementProps(obj, jsonPropsOut, param, jsonProp);
}
nlohmann::json GetPropsInfoJson(GObjectClass *obj_class, GObject *obj = nullptr)
{
nlohmann::json jsonPropsOut = nlohmann::json::object();
GParamSpec **propertySpecs;
guint numProperties, i;
gboolean readable = false;
gboolean firstFlag = false;
propertySpecs = g_object_class_list_properties(obj_class, &numProperties);
for (i = 0; i < numProperties; i++) {
GParamSpec *param = propertySpecs[i];
GType owner_type = param->owner_type;
if (obj == nullptr && (owner_type == G_TYPE_OBJECT
|| owner_type == GST_TYPE_OBJECT || owner_type == GST_TYPE_PAD)) {
continue;
}
GetPropsInfoJsonJsonIteration(obj, jsonPropsOut, readable, firstFlag, param);
}
g_free(propertySpecs);
return jsonPropsOut;
}
nlohmann::json GetElementAllInfo(GstPluginFeature *feature)
{
nlohmann::json jsonResult = nlohmann::json::object();
ustring elementName = "test_";
nlohmann::json jsonProps = nlohmann::json::array();
GstElementFactory *factory = nullptr;
GstElement *element = nullptr;
factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature));
if (!factory) {
LogError << "[GetElementAllInfo] element plugin couldn't be loaded." << GetErrorInfo(APP_ERR_COMM_FAILURE);
return {};
}
elementName.append(GST_OBJECT_NAME(feature));
element = gst_element_factory_create(factory, elementName.c_str());
if (!element) {
gst_object_unref(factory);
LogError << "[GetElementAllInfo] couldn't construct element for some reason."
<< GetErrorInfo(APP_ERR_COMM_FAILURE);
return {};
}
jsonResult[EL_NAME] = GST_OBJECT_NAME(feature);
jsonResult[EL_FACTORY] = GetFactoryInfoJson(factory, element);
jsonResult[EL_PLUGIN] = GetPluginInfoJson(factory);
jsonResult[EL_PADTEMPLATES] = GetPadsTemplatesInfoJson(factory);
jsonResult[EL_PAD] = GetPadsInfoJson(element);
jsonResult[EL_PROPS] = GetPropsInfoJson(G_OBJECT_GET_CLASS(G_OBJECT(element)), G_OBJECT(element));
gst_object_unref(element);
gst_object_unref(factory);
return jsonResult;
}
void GetAllElementsInfoJson(GstPluginFeature *feature, nlohmann::json &result, size_t &idx)
{
std::string factoryName = GST_OBJECT_NAME(feature);
if (std::find(NOT_SHOW_PLUGINS.begin(), NOT_SHOW_PLUGINS.end(), factoryName) != NOT_SHOW_PLUGINS.end()) {
return;
}
nlohmann::json tmp = GetElementAllInfo(feature);
if (tmp.empty()) {
return;
}
result[idx++] = tmp;
}
void GetElementListProcess(nlohmann::json &result, size_t &idx)
{
GList* plugins = nullptr;
GList* origPlugins = nullptr;
origPlugins = plugins = gst_registry_get_plugin_list(gst_registry_get());
while (plugins) {
LogDebug << "Plugin index: " << idx << " .";
GList* features = nullptr;
GList* orig_features = nullptr;
GstPlugin* plugin = nullptr;
plugin = (GstPlugin*)(plugins->data);
plugins = g_list_next(plugins);
if (GST_OBJECT_FLAG_IS_SET(plugin, GST_PLUGIN_FLAG_BLACKLISTED)) {
continue;
}
orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get(),
gst_plugin_get_name(plugin));
while (features) {
GstPluginFeature* feature = nullptr;
GstElementFactory* factory = nullptr;
if (G_UNLIKELY(features->data == nullptr))
goto next;
feature = GST_PLUGIN_FEATURE(features->data);
if (!GST_IS_ELEMENT_FACTORY(feature)) {
goto next;
}
factory = GST_ELEMENT_FACTORY(feature);
if (gst_element_factory_get_num_pad_templates(factory) == 0) {
goto next;
}
GetAllElementsInfoJson(feature, result, idx);
next:
features = g_list_next(features);
}
gst_plugin_feature_list_free(orig_features);
}
gst_plugin_list_free(origPlugins);
}
nlohmann::json GetAllElementsInfo(void)
{
nlohmann::json result = nlohmann::json::array();
size_t idx = 0;
GetElementListProcess(result, idx);
LogDebug << "Total result index:" << idx;
return result;
}
void PluginInfoMapClear()
{
pluginKlassMap.clear();
supportedPlugins.clear();
}
}
namespace MxTools {
PluginInspector::PluginInspector()
{
}
void PluginInspector::ExportAllPluginInfos(std::string outputJsonFile)
{
LogInfo << "Exporting all plugins...";
PluginInfoMapClear();
nlohmann::json result = GetAllElementsInfo();
std::ofstream outStream(outputJsonFile);
chmod(outputJsonFile.c_str(), S_IRUSR | S_IWUSR | S_IRGRP);
outStream << result << std::endl;
outStream.close();
PluginInfoMapClear();
LogInfo << "Finish plugins inspection. Json file was output";
}
PluginInspector::~PluginInspector()
{
}
}