1
0
Fork 0
forked from wry/wry
wry/src/pipewire/pw_pod/pw_debug.rs
2026-02-13 11:13:37 +01:00

464 lines
18 KiB
Rust

use {
crate::{
pipewire::{
pw_parser::PwParser,
pw_pod::{
PW_COMMAND_Node, PW_OBJECT_Format, PW_OBJECT_ParamBuffers, PW_OBJECT_ParamIO,
PW_OBJECT_ParamLatency, PW_OBJECT_ParamMeta, PW_OBJECT_ParamPortConfig,
PW_OBJECT_ParamProcessLatency, PW_OBJECT_ParamProfile, PW_OBJECT_ParamRoute,
PW_OBJECT_Profiler, PW_OBJECT_PropInfo, PW_OBJECT_Props, PW_TYPE_Id, PwPod,
PwPodArray, PwPodObject, PwPodObjectType, PwPodSequence, PwPodStruct, PwPodType,
PwProp, SPA_FORMAT_AUDIO_bitorder, SPA_FORMAT_AUDIO_format,
SPA_FORMAT_AUDIO_iec958Codec, SPA_FORMAT_AUDIO_position,
SPA_FORMAT_VIDEO_H264_alignment, SPA_FORMAT_VIDEO_H264_streamFormat,
SPA_FORMAT_VIDEO_chromaSite, SPA_FORMAT_VIDEO_colorMatrix,
SPA_FORMAT_VIDEO_colorPrimaries, SPA_FORMAT_VIDEO_colorRange,
SPA_FORMAT_VIDEO_format, SPA_FORMAT_VIDEO_interlaceMode,
SPA_FORMAT_VIDEO_multiviewFlags, SPA_FORMAT_VIDEO_multiviewMode,
SPA_FORMAT_VIDEO_transferFunction, SPA_FORMAT_mediaSubtype, SPA_FORMAT_mediaType,
SPA_PARAM_BUFFERS_dataType, SPA_PARAM_IO_id, SPA_PARAM_META_type,
SPA_PARAM_PORT_CONFIG_direction, SPA_PARAM_PORT_CONFIG_mode,
SPA_PARAM_PROFILE_available, SPA_PARAM_ROUTE_available, SPA_PARAM_ROUTE_direction,
SPA_PROP_channelMap, SPA_PROP_iec958Codecs, SpaAudioChannel, SpaAudioFormat,
SpaAudioIec958Codec, SpaDataTypes, SpaDirection, SpaFormat, SpaH264Alignment,
SpaH264StreamFormat, SpaIoType, SpaMediaSubtype, SpaMediaType, SpaMetaType,
SpaNodeCommand, SpaParamAvailability, SpaParamBitorder, SpaParamBuffers,
SpaParamIo, SpaParamLatency, SpaParamMeta, SpaParamPortConfig,
SpaParamPortConfigMode, SpaParamProcessLatency, SpaParamProfile, SpaParamRoute,
SpaParamType, SpaProfiler, SpaProp, SpaPropInfo, SpaVideoChromaSite,
SpaVideoColorMatrix, SpaVideoColorPrimaries, SpaVideoColorRange, SpaVideoFormat,
SpaVideoInterlaceMode, SpaVideoMultiviewFlags, SpaVideoMultiviewMode,
SpaVideoTransferFunction,
},
},
utils::errorfmt::ErrorFmt,
},
std::{
fmt,
fmt::{Debug, DebugList, Formatter, Write},
},
};
trait PwPodObjectDebugger: Sync {
fn debug_property(&self, fmt: &mut Formatter<'_>, value: PwProp<'_>) -> std::fmt::Result;
fn id_name(&self, id: u32) -> Option<&'static str>;
}
struct PwPodObjectDebuggerSimple<F, G, H> {
key_name: F,
debug_pod: G,
id_name: H,
}
impl<F, G, H> PwPodObjectDebugger for PwPodObjectDebuggerSimple<F, G, H>
where
F: Fn(u32) -> Option<&'static str> + Sync,
G: Fn(u32, &mut Formatter<'_>, PwPod<'_>) -> std::fmt::Result + Sync,
H: Fn(u32) -> Option<&'static str> + Sync,
{
fn debug_property(&self, fmt: &mut Formatter<'_>, value: PwProp<'_>) -> std::fmt::Result {
let mut s = fmt.debug_struct("PwProp");
match (self.key_name)(value.key) {
Some(n) => s.field("key", &n),
_ => s.field("key", &value.key),
};
s.field("flags", &value.flags)
.field(
"pod",
&fmt::from_fn(|f| (self.debug_pod)(value.key, f, value.pod)),
)
.finish()
}
fn id_name(&self, id: u32) -> Option<&'static str> {
(self.id_name)(id)
}
}
fn choice_debug<F>(fmt: &mut Formatter<'_>, p: PwPod<'_>, ty: PwPodType, f: F) -> std::fmt::Result
where
F: Fn(&mut Formatter<'_>, PwPod<'_>) -> std::fmt::Result,
{
match p {
PwPod::Choice(c) if c.elements.ty == ty => fmt
.debug_struct("choice")
.field("ty", &c.ty)
.field("flags", &c.flags)
.field(
"elements",
&fmt::from_fn(|fmt| {
array_body_debug(fmt, c.elements, |l, p| {
match p.read_pod_body_packed(ty, c.elements.child_len) {
Ok(p) => {
l.entry(&fmt::from_fn(|fmt| f(fmt, p)));
true
}
Err(e) => {
let e = ErrorFmt(e);
l.entry(&fmt::from_fn(|fmt| {
write!(fmt, "Could not read choice element: {}", e)
}));
false
}
}
})
}),
)
.finish(),
_ if p.ty() == ty => f(fmt, p),
_ => p.fmt(fmt),
}
}
fn id_debug<F>(fmt: &mut Formatter<'_>, p: PwPod<'_>, f: F) -> std::fmt::Result
where
F: Fn(&mut Formatter<'_>, u32) -> std::fmt::Result,
{
choice_debug(fmt, p, PW_TYPE_Id, |fmt, p| match p {
PwPod::Id(id) => f(fmt, id),
_ => p.fmt(fmt),
})
}
fn array_body_debug<F>(fmt: &mut Formatter<'_>, mut a: PwPodArray<'_>, f: F) -> std::fmt::Result
where
F: Fn(&mut DebugList, &mut PwParser<'_>) -> bool,
{
let mut l = fmt.debug_list();
for _ in 0..a.n_elements {
if !f(&mut l, &mut a.elements) {
break;
}
}
l.finish()
}
fn array_debug<F>(fmt: &mut Formatter<'_>, p: PwPod<'_>, ty: PwPodType, f: F) -> std::fmt::Result
where
F: Fn(&mut DebugList, &mut PwParser<'_>) -> bool,
{
match p {
PwPod::Array(a) if a.ty == ty => array_body_debug(fmt, a, f),
_ => p.fmt(fmt),
}
}
fn array_id_debug<F, T>(fmt: &mut Formatter<'_>, p: PwPod<'_>, f: F) -> std::fmt::Result
where
F: Fn(&mut DebugList, u32) -> T,
{
array_debug(fmt, p, PW_TYPE_Id, |l, p| match p.read_id() {
Ok(a) => {
f(l, a);
true
}
Err(e) => {
let e = ErrorFmt(e);
l.entry(&fmt::from_fn(|f| write!(f, "Could not read id: {}", e)));
false
}
})
}
fn object_id_name(id: u32) -> Option<&'static str> {
SpaParamType(id).name()
}
fn command_id_name(id: u32) -> Option<&'static str> {
SpaNodeCommand(id).name()
}
static PROP_INFO_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaPropInfo(key).name(),
debug_pod: |_, f: &mut Formatter<'_>, p: PwPod<'_>| p.fmt(f),
id_name: object_id_name,
};
static PROPS_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaProp(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaProp(key) {
SPA_PROP_channelMap => array_id_debug(f, p, |l, a| {
l.entry(&SpaAudioChannel(a));
}),
SPA_PROP_iec958Codecs => array_id_debug(f, p, |l, a| {
l.entry(&SpaAudioIec958Codec(a));
}),
_ => p.fmt(f),
},
};
static FORMAT_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaFormat(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaFormat(key) {
SPA_FORMAT_mediaType => id_debug(f, p, |f, a| SpaMediaType(a).fmt(f)),
SPA_FORMAT_mediaSubtype => id_debug(f, p, |f, a| SpaMediaSubtype(a).fmt(f)),
SPA_FORMAT_AUDIO_format => id_debug(f, p, |f, a| SpaAudioFormat(a).fmt(f)),
SPA_FORMAT_AUDIO_position => array_id_debug(f, p, |l, a| {
l.entry(&SpaAudioChannel(a));
}),
SPA_FORMAT_AUDIO_iec958Codec => id_debug(f, p, |f, a| SpaAudioIec958Codec(a).fmt(f)),
SPA_FORMAT_AUDIO_bitorder => id_debug(f, p, |f, a| SpaParamBitorder(a).fmt(f)),
SPA_FORMAT_VIDEO_format => id_debug(f, p, |f, a| SpaVideoFormat(a).fmt(f)),
SPA_FORMAT_VIDEO_interlaceMode => id_debug(f, p, |f, a| SpaVideoInterlaceMode(a).fmt(f)),
SPA_FORMAT_VIDEO_multiviewMode => id_debug(f, p, |f, a| SpaVideoMultiviewMode(a).fmt(f)),
SPA_FORMAT_VIDEO_multiviewFlags => id_debug(f, p, |f, a| SpaVideoMultiviewFlags(a).fmt(f)),
SPA_FORMAT_VIDEO_chromaSite => id_debug(f, p, |f, a| SpaVideoChromaSite(a).fmt(f)),
SPA_FORMAT_VIDEO_colorRange => id_debug(f, p, |f, a| SpaVideoColorRange(a).fmt(f)),
SPA_FORMAT_VIDEO_colorMatrix => id_debug(f, p, |f, a| SpaVideoColorMatrix(a).fmt(f)),
SPA_FORMAT_VIDEO_transferFunction => {
id_debug(f, p, |f, a| SpaVideoTransferFunction(a).fmt(f))
}
SPA_FORMAT_VIDEO_colorPrimaries => id_debug(f, p, |f, a| SpaVideoColorPrimaries(a).fmt(f)),
SPA_FORMAT_VIDEO_H264_streamFormat => id_debug(f, p, |f, a| SpaH264StreamFormat(a).fmt(f)),
SPA_FORMAT_VIDEO_H264_alignment => id_debug(f, p, |f, a| SpaH264Alignment(a).fmt(f)),
_ => p.fmt(f),
},
};
static PARAM_BUFFERS_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamBuffers(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaParamBuffers(key) {
SPA_PARAM_BUFFERS_dataType => match p {
PwPod::Int(v) => SpaDataTypes(v as _).fmt(f),
_ => p.fmt(f),
},
_ => p.fmt(f),
},
};
static PARAM_META_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamMeta(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaParamMeta(key) {
SPA_PARAM_META_type => id_debug(f, p, |f, b| SpaMetaType(b).fmt(f)),
_ => p.fmt(f),
},
};
static PARAM_IO_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamIo(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaParamIo(key) {
SPA_PARAM_IO_id => id_debug(f, p, |f, b| SpaIoType(b).fmt(f)),
_ => p.fmt(f),
},
};
static PARAM_PROFILE_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamProfile(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaParamProfile(key) {
SPA_PARAM_PROFILE_available => id_debug(f, p, |f, b| SpaParamAvailability(b).fmt(f)),
_ => p.fmt(f),
},
};
static PARAM_PORT_CONFIG_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamPortConfig(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaParamPortConfig(key) {
SPA_PARAM_PORT_CONFIG_direction => id_debug(f, p, |f, b| SpaDirection(b).fmt(f)),
SPA_PARAM_PORT_CONFIG_mode => id_debug(f, p, |f, b| SpaParamPortConfigMode(b).fmt(f)),
_ => p.fmt(f),
},
};
static PARAM_ROUTE_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamRoute(key).name(),
id_name: object_id_name,
debug_pod: |key, f: &mut Formatter<'_>, p: PwPod<'_>| match SpaParamRoute(key) {
SPA_PARAM_ROUTE_direction => id_debug(f, p, |f, b| SpaDirection(b).fmt(f)),
SPA_PARAM_ROUTE_available => id_debug(f, p, |f, b| SpaParamAvailability(b).fmt(f)),
_ => p.fmt(f),
},
};
static PROFILER_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaProfiler(key).name(),
id_name: object_id_name,
debug_pod: |_, f: &mut Formatter<'_>, p: PwPod<'_>| p.fmt(f),
};
static PARAM_LATENCY_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaParamLatency(key).name(),
id_name: object_id_name,
debug_pod: |_, f: &mut Formatter<'_>, p: PwPod<'_>| p.fmt(f),
};
static PARAM_PROCESS_LATENCY_DEBUGGER: &'static dyn PwPodObjectDebugger =
&PwPodObjectDebuggerSimple {
key_name: |key| SpaParamProcessLatency(key).name(),
id_name: object_id_name,
debug_pod: |_, f: &mut Formatter<'_>, p: PwPod<'_>| p.fmt(f),
};
static COMMAND_NODE_DEBUGGER: &'static dyn PwPodObjectDebugger = &PwPodObjectDebuggerSimple {
key_name: |key| SpaNodeCommand(key).name(),
id_name: command_id_name,
debug_pod: |_, f: &mut Formatter<'_>, p: PwPod<'_>| p.fmt(f),
};
fn object_debugger(obj: PwPodObjectType) -> Option<&'static dyn PwPodObjectDebugger> {
let res: &dyn PwPodObjectDebugger = match obj {
PW_OBJECT_PropInfo => PROP_INFO_DEBUGGER,
PW_OBJECT_Props => PROPS_DEBUGGER,
PW_OBJECT_Format => FORMAT_DEBUGGER,
PW_OBJECT_ParamBuffers => PARAM_BUFFERS_DEBUGGER,
PW_OBJECT_ParamMeta => PARAM_META_DEBUGGER,
PW_OBJECT_ParamIO => PARAM_IO_DEBUGGER,
PW_OBJECT_ParamProfile => PARAM_PROFILE_DEBUGGER,
PW_OBJECT_ParamPortConfig => PARAM_PORT_CONFIG_DEBUGGER,
PW_OBJECT_ParamRoute => PARAM_ROUTE_DEBUGGER,
PW_OBJECT_Profiler => PROFILER_DEBUGGER,
PW_OBJECT_ParamLatency => PARAM_LATENCY_DEBUGGER,
PW_OBJECT_ParamProcessLatency => PARAM_PROCESS_LATENCY_DEBUGGER,
PW_COMMAND_Node => COMMAND_NODE_DEBUGGER,
_ => return None,
};
Some(res)
}
impl<'a> Debug for PwPodObject<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let debugger = object_debugger(self.ty);
let mut s = f.debug_struct("object");
s.field("type", &self.ty);
let name;
let mut id: &dyn Debug = &self.id;
if let Some(d) = debugger
&& let Some(n) = d.id_name(self.id)
{
name = n;
id = &name;
}
s.field("id", id);
s.field(
"props",
&fmt::from_fn(|f| {
let mut l = f.debug_list();
let mut parser = self.probs;
while parser.len() > 0 {
match parser.read_prop() {
Ok(p) => match debugger {
Some(d) => l.entry(&fmt::from_fn(|fmt| d.debug_property(fmt, p))),
_ => l.entry(&p),
},
Err(e) => {
let e = ErrorFmt(e);
l.entry(&fmt::from_fn(|f| {
write!(f, "Could not read object property: {}", &e)
}));
break;
}
};
}
l.finish()
}),
);
s.finish()
}
}
impl<'a> Debug for PwPodSequence<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("sequence");
s.field("unit", &self.unit);
s.field(
"controls",
&fmt::from_fn(|f| {
let mut l = f.debug_list();
let mut parser = self.controls;
while parser.len() > 0 {
match parser.read_control() {
Ok(c) => l.entry(&c),
Err(e) => {
let e = ErrorFmt(e);
l.entry(&fmt::from_fn(|f| {
write!(f, "Could not read control element: {}", &e)
}));
break;
}
};
}
l.finish()
}),
);
s.finish()
}
}
impl<'a> Debug for PwPodStruct<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut parser = self.fields;
let mut s = f.debug_struct("struct");
let mut field = String::new();
for i in 0.. {
if parser.len() == 0 {
break;
}
field.clear();
let _ = write!(&mut field, "\"{}\"", i);
match parser.read_pod() {
Ok(p) => s.field(&field, &p),
Err(e) => {
let e = ErrorFmt(e);
s.field(
&field,
&fmt::from_fn(|f| write!(f, "Could not parse struct field: {}", &e)),
);
break;
}
};
}
s.finish()
}
}
impl<'a> Debug for PwPodArray<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut list = f.debug_list();
let mut parser = self.elements;
for _ in 0..self.n_elements {
match parser.read_pod_body_packed(self.ty, self.child_len) {
Ok(e) => list.entry(&e),
Err(e) => {
let e = ErrorFmt(e);
list.entry(&fmt::from_fn(|f| {
write!(f, "Could not parse array element: {}", &e)
}));
break;
}
};
}
list.finish()
}
}
impl<'a> Debug for PwPod<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PwPod::None => write!(f, "None"),
PwPod::Bool(b) => write!(f, "{}", b),
PwPod::Id(id) => write!(f, "id({})", id),
PwPod::Int(i) => write!(f, "int({})", i),
PwPod::Long(l) => write!(f, "long({})", l),
PwPod::Float(v) => write!(f, "float({})", v),
PwPod::Double(d) => write!(f, "double({})", d),
PwPod::String(s) => write!(f, "string({:?})", s),
PwPod::Bytes(b) => write!(f, "bytes(len = {})", b.len()),
PwPod::Rectangle(r) => write!(f, "rectangle({}x{})", r.width, r.height),
PwPod::Fraction(v) => write!(f, "fraction({}/{})", v.num, v.denom),
PwPod::Bitmap(b) => write!(f, "bitmap(len = {})", b.len()),
PwPod::Array(a) => a.fmt(f),
PwPod::Struct(s) => s.fmt(f),
PwPod::Object(o) => o.fmt(f),
PwPod::Sequence(s) => s.fmt(f),
PwPod::Pointer(p) => p.fmt(f),
PwPod::Fd(v) => write!(f, "fd({})", v),
PwPod::Choice(c) => c.fmt(f),
}
}
}