mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2024-06-02 13:20:30 +00:00
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:
parent
51f4044a55
commit
a36dfd92bc
|
@ -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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue