inspect: Print element properties

Example of String: "location" of "videotestsrc"
Example of Boolean: "sync" property of "fakesink" element.
Example of Long: n.a.
Example of Unsigned Long: n.a.
Example of Unsigned Integer: "min-percent" property of "fakesink" element
Example of Integer: "blocksize" property of "alsasink" element
Example of Unsigned Integer64: "max-buffers" property of "appsrc" element
Example of Integer64: "max-lateness" property of "fakeaudiosink" element
Example of Float: "max-zoom" property of "camerabin" element
Example of Double: "peak-falloff" property of "level" element*
Example of Enum: "pattern" property of "videotestsrc" element
Example of Flags: "output-multiview-flags" property of "glimagesink" element
Example of GValueArray: "mix-matrix" property of "audioconvert" element
Example of GstCaps: "caps" property of "uridecodebin" element**
Example of Boxed: "converter-config" property of "compositor" element***
Example of Pointer: "factories" property of "autoconvert" element
Example of Fraction: "scale-pixel-aspect-ratio" property of "clockoverlay" element
Example of GstValueArray: "render-rectangle" property of "xvimagesink" element

TODO/Bugs
- "audio-capture-supported-caps:" property of "camerabin" element**
- "peak-falloff" property of "level" element*
- "converter-config" property of "compositor" element ***
This commit is contained in:
Fabian Orccon 2023-06-18 12:01:20 +02:00
parent 51f4044a55
commit a36dfd92bc

View file

@ -20,6 +20,8 @@
// DEALINGS IN THE SOFTWARE.
use ansi_term::Color;
use clap::{Arg, Command};
use gst::glib;
use gst::glib::translate::IntoGlib;
use gst::prelude::*;
const BRBLUE: Color = Color::RGB(97, 127, 166);
@ -35,7 +37,9 @@ const STRUCT_NAME_COLOR: Color = Color::Yellow;
const CAPS_FEATURE_COLOR: Color = Color::Green;
const FIELD_VALUE_COLOR: Color = BRBLUE;
const FIELD_NAME_COLOR: Color = Color::Cyan;
const PROP_ATTR_NAME_COLOR: Color = Color::Yellow;
const PROP_ATTR_VALUE_COLOR: Color = Color::Cyan;
const DATATYPE_COLOR: Color = Color::Green;
fn print_element_list() {
let registry = gst::Registry::get();
@ -107,9 +111,9 @@ fn print_plugin_info(plugin: &gst::Plugin) {
println!();
}
fn hierarchy_foreach<F>(type_: gst::glib::Type, foreach_func: &mut F)
fn hierarchy_foreach<F>(type_: glib::Type, foreach_func: &mut F)
where
F: FnMut(gst::glib::Type),
F: FnMut(glib::Type),
{
if let Some(parent) = type_.parent() {
hierarchy_foreach(parent, foreach_func);
@ -118,9 +122,9 @@ where
foreach_func(type_);
}
fn print_hierarchy(type_: gst::glib::Type) {
fn print_hierarchy(type_: glib::Type) {
let mut level = 0;
let mut func = |cur_type: gst::glib::Type| {
let mut func = |cur_type: glib::Type| {
if level > 0 {
print!("{}", " ".repeat(level - 1));
print!(" {}", CHILD_LINK_COLOR.paint("+----"));
@ -133,7 +137,7 @@ fn print_hierarchy(type_: gst::glib::Type) {
println!();
}
fn print_interfaces(type_: gst::glib::Type) {
fn print_interfaces(type_: glib::Type) {
let interfaces = type_.interfaces();
if interfaces.is_empty() {
return;
@ -146,8 +150,22 @@ fn print_interfaces(type_: gst::glib::Type) {
println!();
}
fn print_caps(caps: &gst::Caps) {
let indent = " ".repeat(6);
fn print_fields(structure: &gst::StructureRef, indent: usize) {
let width = 15;
for (field, value) in structure.iter() {
if let Ok(val) = value.serialize() {
println!(
"{}{}: {}",
" ".repeat(indent),
FIELD_NAME_COLOR.paint(format!("{:>width$}", field.as_str())),
FIELD_VALUE_COLOR.paint(val.as_str())
);
}
}
}
fn print_caps(caps: &gst::Caps, indent: usize) {
let indent = " ".repeat(indent);
if caps.is_any() {
println!("{}{}", indent, CAPS_TYPE_COLOR.paint("ANY"));
@ -173,16 +191,7 @@ fn print_caps(caps: &gst::Caps) {
STRUCT_NAME_COLOR.paint(structure.name().as_str())
);
};
for (field, value) in structure {
let width = 23;
if let Ok(val) = value.serialize() {
println!(
"{}: {}",
FIELD_NAME_COLOR.paint(format!("{:>width$}", field.as_str())),
FIELD_VALUE_COLOR.paint(val.as_str())
);
}
}
print_fields(structure, 8);
}
}
@ -222,7 +231,7 @@ fn print_pad_templates_info(factory: &gst::ElementFactory) {
);
print_property("Availability", availability, 0, indent * 2, true);
print_property("Capabilities", "", 0, indent * 2, true);
print_caps(&pad_tmpl.caps());
print_caps(&pad_tmpl.caps(), indent * 3);
println!();
}
}
@ -316,6 +325,363 @@ fn print_pad_info(element: &gst::Element) {
}
}
fn print_pspec_flags(pspec: &glib::ParamSpec, indent: usize) {
let flags_to_string = [
(glib::ParamFlags::READABLE, "writable"),
(glib::ParamFlags::WRITABLE, "readable"),
(glib::ParamFlags::DEPRECATED, "deprecated"),
(gst::PARAM_FLAG_CONTROLLABLE, "controllable"),
(
gst::PARAM_FLAG_CONDITIONALLY_AVAILABLE,
"conditionally available",
),
(
gst::PARAM_FLAG_MUTABLE_PLAYING,
"changeable in NULL, READY, PAUSED or PLAYING state",
),
(
gst::PARAM_FLAG_MUTABLE_PAUSED,
"changeable only in NULL, READY or PAUSED state",
),
(
gst::PARAM_FLAG_MUTABLE_READY,
"changeable only in NULL or READY state",
),
// TODO: ~KNOWN_PARAM_FLAGS
];
let flags = pspec.flags();
print!(
"{:indent$}{}: ",
"",
PROP_ATTR_NAME_COLOR.paint("flags"),
indent = indent
);
let mut first_flag = true;
for (flag, string) in flags_to_string.iter() {
if !flags.contains(*flag) {
continue;
}
if !first_flag {
print!(", ")
}
print!("{}", PROP_ATTR_VALUE_COLOR.paint(*string));
first_flag = false;
}
println!();
}
fn flags_to_string(pspec: &glib::ParamSpecFlags, flags: u32) -> String {
let pspec = pspec.flags_class().to_owned();
let pspec_vals = pspec.values();
pspec_vals
.iter()
.find_map(|flags_val| {
let same_flags = flags_val.value() == flags;
same_flags.then_some(flags_val.nick().to_string())
})
.unwrap_or_else(|| {
let mut res = "".to_string();
let mut flags_left = flags;
for pspec_val in pspec_vals.iter().rev() {
let pspec_value = pspec_val.value();
if pspec_value != 0 && (flags_left & pspec_value) == pspec_value {
if !res.is_empty() {
res += "+";
}
res += pspec_val.nick();
flags_left -= pspec_value;
if flags_left == 0 {
break;
}
}
}
if res.is_empty() {
res = "(none)".to_string()
}
res
})
}
fn print_default_prop(title: &str, range: Option<(String, String)>, default: &str) {
print!("{}", DATATYPE_COLOR.paint(title));
if let Some((min, max)) = range {
print!(
". {}: {} - {}",
PROP_ATTR_NAME_COLOR.paint("Range"),
PROP_ATTR_VALUE_COLOR.paint(min.to_string()),
PROP_ATTR_VALUE_COLOR.paint(max.to_string()),
);
}
print!(" ");
print!(
"{}: {}",
PROP_ATTR_NAME_COLOR.paint("Default"),
PROP_ATTR_VALUE_COLOR.paint(default.to_string())
);
}
fn print_enum_flag(i: u32, nick: &str, name: &str, hex_fmt: bool, indent: usize) {
println!();
print!("{:indent$}", "", indent = indent);
print!(
"{}: {} - {}",
if hex_fmt {
PROP_ATTR_NAME_COLOR.paint(format!("({:#08x})", i))
} else {
PROP_ATTR_NAME_COLOR.paint(format!("({})", i))
},
PROP_ATTR_VALUE_COLOR.paint(&format!("{:<16}", nick)),
name
);
}
fn print_default_property_value(
element: &gst::Element,
pspec: &glib::ParamSpec,
readable: bool,
base_indent: usize,
indent: usize,
) {
let caps_indent = 53;
let flags_indent = base_indent + indent + 1;
let value: glib::Value = if readable {
element.property::<glib::Value>(&pspec.name())
} else {
pspec.default_value().clone()
};
// Keep caps properties aligned.
if !value.type_().is_a(gst::Caps::static_type()) || value.get::<Option<&gst::Caps>>().is_err() {
print!("{:indent$}", "", indent = base_indent);
}
match value.type_() {
glib::types::Type::STRING => {
let default = match value.get::<Option<&str>>() {
Ok(Some(val)) => format!("\"{}\"", val),
_ => "null".to_string(),
};
print_default_prop("String", None, &default);
}
glib::types::Type::BOOL => {
let default = value.get::<bool>().unwrap().to_string();
print_default_prop("Boolean", None, &default);
}
glib::types::Type::I_LONG => {
let default = value.get::<i64>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecLong>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Long", range, &default);
}
glib::types::Type::U_LONG => {
let default = value.get::<u64>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecULong>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Unsigned Long", range, &default);
}
glib::types::Type::U32 => {
let default = value.get::<u32>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecUInt>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Unsigned Integer", range, &default);
}
glib::types::Type::I32 => {
let default = value.get::<i32>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecInt>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Integer", range, &default);
}
glib::types::Type::U64 => {
let default = value.get::<u64>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecUInt64>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Unsigned Integer64", range, &default);
}
glib::types::Type::I64 => {
let default = value.get::<i64>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecInt64>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Integer64", range, &default);
}
glib::types::Type::F32 => {
let default = value.get::<f32>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecFloat>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Float", range, &default);
}
glib::types::Type::F64 => {
let default = value.get::<f64>().unwrap().to_string();
let pspec = pspec.downcast_ref::<glib::ParamSpecDouble>().unwrap();
let range = Some((pspec.minimum().to_string(), pspec.maximum().to_string()));
print_default_prop("Double", range, &default);
}
typ if typ.is_a(glib::types::Type::ENUM) => {
let val = value.get::<&glib::EnumValue>().unwrap();
let pspec = pspec.downcast_ref::<glib::ParamSpecEnum>().unwrap();
let default = format!("{}, \"{}\"", val.value(), val.nick());
let title = format!("Enum \"{}\"", value.type_().name());
print_default_prop(&title, None, &default);
for (i, enum_val) in pspec.enum_class().to_owned().values().iter().enumerate() {
print_enum_flag(
i as u32,
enum_val.nick(),
enum_val.name(),
false,
flags_indent,
);
}
}
typ if typ.is_a(glib::types::Type::FLAGS) => {
let val = value.get::<Vec<&glib::FlagsValue>>().unwrap();
let pspec = pspec.downcast_ref::<glib::ParamSpecFlags>().unwrap();
let title = format!("Flags \"{}\"", value.type_().name());
let flags = val
.to_owned()
.into_iter()
.fold(0, |acc, val| acc | val.value());
let default = format!("{:#08x}, \"{}\"", flags, flags_to_string(pspec, flags));
print_default_prop(&title, None, &default);
for flags_val in pspec.flags_class().to_owned().values().iter() {
print_enum_flag(
flags_val.value(),
flags_val.nick(),
flags_val.name(),
true,
flags_indent,
);
}
}
typ if typ.is_a(glib::types::Type::OBJECT) => {
print!(
"Object of type {}",
DATATYPE_COLOR.paint(&format!("\"{}\"", pspec.name()))
);
}
typ if typ.is_a(glib::ValueArray::static_type()) => {
let pvarray = pspec.downcast_ref::<glib::ParamSpecValueArray>().unwrap();
if let Some(espec) = pvarray.element_spec() {
print!(
"Array of GValues of type {}",
DATATYPE_COLOR.paint(&format!("\"{}\"", espec.value_type()))
);
} else {
print!("Array of GValues");
}
}
typ if typ.is_a(gst::Caps::static_type()) => match value.get::<Option<&gst::Caps>>() {
Ok(Some(val)) => print_caps(val, caps_indent),
_ => println!("Caps (NULL)"),
},
typ if typ.is_a(glib::types::Type::BOXED) => {
let val = value.get::<glib::Value>().unwrap();
print!(
"Boxed pointer of type {}",
DATATYPE_COLOR.paint(&format!("\"{}\"", val.type_()))
);
if val.type_() == gst::Structure::static_type() {
if let Ok(Some(st)) = value.get::<Option<gst::Structure>>() {
println!();
print_fields(&st, caps_indent);
}
}
}
typ if typ.is_a(glib::types::Type::POINTER) => {
if pspec.value_type() != glib::types::Type::POINTER {
print!(
"Pointer of type {}",
DATATYPE_COLOR.paint(&format!("\"{}\"", pspec.value_type()))
);
} else {
print!("Pointer.");
}
}
typ if typ.is_a(gst::Fraction::static_type()) => {
let pfraction = pspec
.downcast_ref::<gst::param_spec::ParamSpecFraction>()
.unwrap();
let val = value.get::<gst::Fraction>().unwrap();
let default = format!("{}/{}", val.numer(), val.denom());
let min = format!(
"{}/{}",
pfraction.minimum().numer(),
pfraction.minimum().denom()
);
let max = format!(
"{}/{}",
pfraction.maximum().numer(),
pfraction.maximum().denom()
);
print_default_prop("Fraction", Some((min, max)), &default);
}
typ if typ.is_a(gst::Array::static_type()) => {
let parray = pspec
.downcast_ref::<gst::param_spec::ParamSpecArray>()
.unwrap();
let val = value.get::<gst::Array>().unwrap();
if !val.is_empty() {
if let Ok(def) = value.serialize() {
println!("Default: {}", DATATYPE_COLOR.paint(&format!("\"{}\"", def)));
}
}
if let Some(espec) = parray.element_spec() {
print!(
"GstValueArray of GValues of type {}",
DATATYPE_COLOR.paint(&format!("\"{}\"", espec.value_type()))
);
} else {
print!("GstValueArray of GValues");
}
}
_ => {
print!(
"Unknown type {} {}",
pspec.value_type().into_glib(),
DATATYPE_COLOR.paint(&format!("\"{}\"", pspec.value_type().name()))
);
}
}
if !readable {
println!(" Write only");
} else {
println!();
}
}
fn print_element_properties(element: &gst::Element) {
let (indent, width) = (2, 20);
let mut property_specs = element.list_properties();
property_specs.sort_by_key(|pspec| pspec.name());
println!("{}:", HEADING_COLOR.paint("Element Properties"));
for pspec in &property_specs {
print_property(pspec.name(), &pspec.blurb().unwrap(), width, indent, true);
print_pspec_flags(&pspec, width + indent * 2);
let readable = pspec.flags().contains(glib::ParamFlags::READABLE);
print_default_property_value(element, &pspec, readable, width + indent * 2, indent);
}
if property_specs.is_empty() {
println!(" none");
}
}
fn print_element_info(feature: &gst::PluginFeature) -> Result<(), String> {
let Ok(factory) = feature.load() else {
return Err(format!("element factory '{}' couldn't be loaded", feature.name()));
@ -341,6 +707,7 @@ fn print_element_info(feature: &gst::PluginFeature) -> Result<(), String> {
print_clocking_info(&element);
print_uri_handler_info(&element);
print_pad_info(&element);
print_element_properties(&element);
Ok(())
}