use std::fmt::{Debug, Formatter};
use std::io::{Seek, Write};
use std::marker::PhantomData;
use std::mem::size_of;
use std::ptr::addr_of;
use bitflags::{bitflags, Flags};
use spa_sys::spa_pod_object_body;
use pipewire_wrapper_proc_macro::RawWrapper;
use prop::ObjectPropType;
use prop_info::ObjectPropInfoType;
use crate::spa::param::ParamType;
use crate::spa::pod::id::{PodIdRef, PodIdType};
use crate::spa::pod::iterator::PodIterator;
use crate::spa::pod::object::enum_format::ObjectEnumFormatType;
use crate::spa::pod::object::format::{MediaSubType, MediaType, ObjectFormatType};
use crate::spa::pod::object::param_buffers::ParamBuffersType;
use crate::spa::pod::object::param_io::ParamIOType;
use crate::spa::pod::object::param_latency::ParamLatencyType;
use crate::spa::pod::object::param_meta::ParamMetaType;
use crate::spa::pod::object::param_port_config::ParamPortConfigType;
use crate::spa::pod::object::param_process_latency::ParamProcessLatencyType;
use crate::spa::pod::object::param_profile::ParamProfileType;
use crate::spa::pod::object::param_route::ParamRouteType;
use crate::spa::pod::object::profiler::ProfilerType;
use crate::spa::pod::pod_buf::{AllocPod, PodBuf};
use crate::spa::pod::restricted::{CloneTo, PodHeader, PodRawValue};
use crate::spa::pod::{
BasicTypePod, FromValue, PodError, PodRef, PodResult, PodValue, SizedPod, WritePod,
};
use crate::spa::type_::Type;
use crate::wrapper::RawWrapper;
use super::restricted::{write_align_padding, write_header, write_value};
pub mod enum_format;
pub mod format;
pub mod param_buffers;
pub mod param_io;
pub mod param_latency;
pub mod param_meta;
pub mod param_port_config;
pub mod param_process_latency;
pub mod param_profile;
pub mod param_route;
pub mod profiler;
pub mod prop;
pub mod prop_info;
#[derive(RawWrapper)]
#[repr(transparent)]
struct PodObjectBodyRef {
#[raw]
raw: spa_sys::spa_pod_object_body,
}
impl PodObjectBodyRef {
pub fn type_(&self) -> Type {
Type::from_raw(self.raw.type_)
}
pub fn id(&self) -> u32 {
self.raw.id
}
}
impl Debug for PodObjectBodyRef {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PodObjectBodyRef")
.field("type", &self.type_())
.field("id", &self.id())
.finish()
}
}
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodObjectRef {
#[raw]
raw: spa_sys::spa_pod_object,
}
impl PodHeader for PodObjectRef {
fn pod_header(&self) -> &spa_sys::spa_pod {
&self.raw.pod
}
fn static_type() -> Type {
Type::OBJECT
}
}
impl Debug for PodObjectRef {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
unsafe {
f.debug_struct("PodObjectRef")
.field("pod.type", &self.pod_type())
.field("pod.size", &self.pod_size())
.field("body_type", &self.body_type())
.field("body_id", &self.body_id())
.field(
"value",
&self.value().map(|v| match v {
ObjectType::OBJECT_PROP_INFO(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PROPS(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_FORMAT(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_ENUM_FORMAT(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_BUFFERS(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_META(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_IO(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_PROFILE(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_PORT_CONFIG(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_ROUTE(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PROFILER(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_LATENCY(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
ObjectType::OBJECT_PARAM_PROCESS_LATENCY(iter) => {
iter.map(|p| format!("{:?}", p.value())).collect::<Vec<_>>()
}
}),
)
.finish()
}
}
}
impl PodObjectRef {
fn body(&self) -> &PodObjectBodyRef {
unsafe { PodObjectBodyRef::from_raw_ptr(&self.raw.body) }
}
pub fn body_type(&self) -> Type {
self.body().type_()
}
pub fn body_id(&self) -> u32 {
self.body().id()
}
pub fn set_body_id(&mut self, id: u32) {
self.raw.body.id = id;
}
pub fn from_id_and_value(
id: impl Into<u32>,
value: &<&Self as PodValue>::Value,
) -> PodResult<AllocPod<Self>> {
let mut obj = PodBuf::<Self>::from_value(value)?.into_pod();
obj.as_pod_mut().set_body_id(id.into());
Ok(obj)
}
pub fn param_value(&self, param_type: ParamType) -> PodResult<ObjectType> {
PodObjectRef::parse_raw_body(
self.body().as_raw_ptr(),
self.pod_header().size as usize,
self.body_type(),
param_type.raw,
)
}
fn parse_raw_body<'a>(
body_ptr: *const spa_pod_object_body,
size: usize,
body_type: Type,
body_id: u32,
) -> PodResult<ObjectType<'a>> {
let first_element_ptr = unsafe { body_ptr.offset(1) };
let size = size - size_of::<spa_sys::spa_pod_object_body>();
Ok(match body_type {
Type::OBJECT_PROP_INFO => {
ObjectType::OBJECT_PROP_INFO(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PROPS => {
ObjectType::OBJECT_PROPS(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_FORMAT => {
if body_id == ParamType::FORMAT.raw {
ObjectType::OBJECT_FORMAT(PodIterator::new(first_element_ptr.cast(), size))
} else {
ObjectType::OBJECT_ENUM_FORMAT(PodIterator::new(first_element_ptr.cast(), size))
}
}
Type::OBJECT_PARAM_BUFFERS => {
ObjectType::OBJECT_PARAM_BUFFERS(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PARAM_META => {
ObjectType::OBJECT_PARAM_META(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PARAM_IO => {
ObjectType::OBJECT_PARAM_IO(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PARAM_PROFILE => {
ObjectType::OBJECT_PARAM_PROFILE(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PARAM_PORT_CONFIG => ObjectType::OBJECT_PARAM_PORT_CONFIG(
PodIterator::new(first_element_ptr.cast(), size),
),
Type::OBJECT_PARAM_ROUTE => {
ObjectType::OBJECT_PARAM_ROUTE(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PROFILER => {
ObjectType::OBJECT_PROFILER(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PARAM_LATENCY => {
ObjectType::OBJECT_PARAM_LATENCY(PodIterator::new(first_element_ptr.cast(), size))
}
Type::OBJECT_PARAM_PROCESS_LATENCY => ObjectType::OBJECT_PARAM_PROCESS_LATENCY(
PodIterator::new(first_element_ptr.cast(), size),
),
type_ => return Err(PodError::UnexpectedObjectType(type_.raw)),
})
}
}
impl<'a> PodRawValue for &'a PodObjectRef {
type RawValue = spa_sys::spa_pod_object_body;
fn raw_value_ptr(&self) -> *const Self::RawValue {
&self.raw.body
}
fn parse_raw_value(ptr: *const Self::RawValue, size: usize) -> PodResult<Self::Value> {
let body = unsafe { PodObjectBodyRef::from_raw_ptr(ptr) };
PodObjectRef::parse_raw_body(body.as_raw_ptr(), size, body.type_(), body.id())
}
}
impl<'a> PodValue for &'a PodObjectRef {
type Value = ObjectType<'a>;
fn value(&self) -> PodResult<Self::Value> {
Self::parse_raw_value(self.raw_value_ptr(), self.pod_header().size as usize)
}
}
impl<'a> WritePod for &'a PodObjectRef {
fn write_pod<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
where
W: Write + Seek,
{
let (type_, content) = unsafe {
match value {
ObjectType::OBJECT_PROP_INFO(iter) => (Type::OBJECT_PROP_INFO, iter.as_bytes()),
ObjectType::OBJECT_PROPS(iter) => (Type::OBJECT_PROPS, iter.as_bytes()),
ObjectType::OBJECT_FORMAT(iter) => (Type::OBJECT_FORMAT, iter.as_bytes()),
ObjectType::OBJECT_ENUM_FORMAT(iter) => (Type::OBJECT_FORMAT, iter.as_bytes()),
ObjectType::OBJECT_PARAM_BUFFERS(iter) => {
(Type::OBJECT_PARAM_BUFFERS, iter.as_bytes())
}
ObjectType::OBJECT_PARAM_META(iter) => (Type::OBJECT_PARAM_META, iter.as_bytes()),
ObjectType::OBJECT_PARAM_IO(iter) => (Type::OBJECT_PARAM_IO, iter.as_bytes()),
ObjectType::OBJECT_PARAM_PROFILE(iter) => {
(Type::OBJECT_PARAM_PROFILE, iter.as_bytes())
}
ObjectType::OBJECT_PARAM_PORT_CONFIG(iter) => {
(Type::OBJECT_PARAM_PORT_CONFIG, iter.as_bytes())
}
ObjectType::OBJECT_PARAM_ROUTE(iter) => (Type::OBJECT_PARAM_ROUTE, iter.as_bytes()),
ObjectType::OBJECT_PROFILER(iter) => (Type::OBJECT_PROFILER, iter.as_bytes()),
ObjectType::OBJECT_PARAM_LATENCY(iter) => {
(Type::OBJECT_PARAM_LATENCY, iter.as_bytes())
}
ObjectType::OBJECT_PARAM_PROCESS_LATENCY(iter) => {
(Type::OBJECT_PARAM_PROCESS_LATENCY, iter.as_bytes())
}
}
};
write_header(
buffer,
(size_of::<spa_sys::spa_pod_object_body>() + content.len()) as u32,
Type::OBJECT,
)?;
write_value(
buffer,
&spa_sys::spa_pod_object_body {
type_: type_.raw,
id: 0,
},
)?;
buffer.write_all(content)?;
write_align_padding(buffer)
}
}
pub trait PodPropKeyType<'a>
where
Self: 'a,
Self: TryFrom<&'a PodPropRef<'a, Self>, Error = PodError>,
Self: Debug,
{
fn write_prop<W>(&self, buffer: &mut W) -> PodResult<()>
where
W: Write + Seek;
fn write_pod_prop<W, T>(buffer: &mut W, key: u32, flags: u32, pod: &T) -> PodResult<()>
where
W: Write + Seek,
T: WritePod,
T: CloneTo,
{
buffer.write_all(&key.to_ne_bytes())?;
buffer.write_all(&flags.to_ne_bytes())?;
pod.clone_to(buffer)
}
}
#[derive(RawWrapper)]
#[repr(transparent)]
pub struct PodPropRef<'a, T: PodPropKeyType<'a>> {
#[raw]
raw: spa_sys::spa_pod_prop,
phantom_type: PhantomData<&'a T>,
}
bitflags! {
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
#[repr(transparent)]
pub struct PodPropFlags: u32 {
const READONLY = spa_sys::SPA_POD_PROP_FLAG_READONLY;
const HARDWARE = spa_sys::SPA_POD_PROP_FLAG_HARDWARE;
const HINT_DICT = spa_sys::SPA_POD_PROP_FLAG_HINT_DICT;
const MANDATORY = spa_sys::SPA_POD_PROP_FLAG_MANDATORY;
const DONT_FIXATE = spa_sys::SPA_POD_PROP_FLAG_DONT_FIXATE;
}
}
impl<'a, T: PodPropKeyType<'a>> SizedPod for PodPropRef<'a, T> {
fn pod_size(&self) -> usize {
size_of::<PodPropRef<T>>() + self.pod().size() as usize
}
}
impl<'a, T: PodPropKeyType<'a>> PodPropRef<'a, T> {
pub fn key(&self) -> u32 {
self.raw.key
}
pub fn flags(&self) -> PodPropFlags {
PodPropFlags::from_bits_retain(self.raw.flags)
}
pub fn set_flags(&mut self, flags: PodPropFlags) {
self.raw.flags = flags.bits();
}
pub fn pod(&self) -> &PodRef {
unsafe { PodRef::from_raw_ptr(addr_of!(self.raw.value)) }
}
}
impl<'a, T: PodPropKeyType<'a>> PodRawValue for &'a PodPropRef<'a, T> {
type RawValue = spa_sys::spa_pod_prop;
fn raw_value_ptr(&self) -> *const Self::RawValue {
&self.raw
}
fn parse_raw_value(ptr: *const Self::RawValue, size: usize) -> PodResult<Self::Value> {
unsafe { PodPropRef::from_raw_ptr(ptr).try_into() }
}
}
impl<'a, T: PodPropKeyType<'a>> PodValue for &'a PodPropRef<'a, T> {
type Value = T;
fn value(&self) -> PodResult<Self::Value> {
let size = size_of::<<Self as PodRawValue>::RawValue>() + self.raw.value.size as usize;
Self::parse_raw_value(self.raw_value_ptr(), size)
}
}
impl<'a, T: PodPropKeyType<'a>> WritePod for &'a PodPropRef<'a, T> {
fn write_pod<W>(buffer: &mut W, value: &<Self as PodValue>::Value) -> PodResult<()>
where
W: Write + Seek,
{
value.write_prop(buffer)
}
}
impl<'a, T: PodPropKeyType<'a>> Debug for &'a PodPropRef<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
unsafe {
f.debug_struct("PodPropRef")
.field("key", &self.key())
.field("flags", &self.flags())
.field("pod", &self.pod())
.field("value", &self.value())
.finish()
}
}
}
pub type ObjectPropsIterator<'a, T> = PodIterator<'a, PodPropRef<'a, T>>;
#[repr(u32)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum ObjectType<'a> {
OBJECT_PROP_INFO(ObjectPropsIterator<'a, ObjectPropInfoType<'a>>) = Type::OBJECT_PROP_INFO.raw,
OBJECT_PROPS(ObjectPropsIterator<'a, ObjectPropType<'a>>) = Type::OBJECT_PROPS.raw,
OBJECT_FORMAT(ObjectPropsIterator<'a, ObjectFormatType<'a>>) = Type::OBJECT_FORMAT.raw,
OBJECT_ENUM_FORMAT(ObjectPropsIterator<'a, ObjectEnumFormatType<'a>>) =
Type::_OBJECT_LAST.raw + Type::OBJECT_FORMAT.raw,
OBJECT_PARAM_BUFFERS(ObjectPropsIterator<'a, ParamBuffersType<'a>>) =
Type::OBJECT_PARAM_BUFFERS.raw,
OBJECT_PARAM_META(ObjectPropsIterator<'a, ParamMetaType<'a>>) = Type::OBJECT_PARAM_META.raw,
OBJECT_PARAM_IO(ObjectPropsIterator<'a, ParamIOType<'a>>) = Type::OBJECT_PARAM_IO.raw,
OBJECT_PARAM_PROFILE(ObjectPropsIterator<'a, ParamProfileType<'a>>) =
Type::OBJECT_PARAM_PROFILE.raw,
OBJECT_PARAM_PORT_CONFIG(ObjectPropsIterator<'a, ParamPortConfigType<'a>>) =
Type::OBJECT_PARAM_PORT_CONFIG.raw,
OBJECT_PARAM_ROUTE(ObjectPropsIterator<'a, ParamRouteType<'a>>) = Type::OBJECT_PARAM_ROUTE.raw,
OBJECT_PROFILER(ObjectPropsIterator<'a, ProfilerType<'a>>) = Type::OBJECT_PROFILER.raw,
OBJECT_PARAM_LATENCY(ObjectPropsIterator<'a, ParamLatencyType<'a>>) =
Type::OBJECT_PARAM_LATENCY.raw,
OBJECT_PARAM_PROCESS_LATENCY(ObjectPropsIterator<'a, ParamProcessLatencyType<'a>>) =
Type::OBJECT_PARAM_PROCESS_LATENCY.raw,
}
#[test]
fn from_value() {
let mut allocated = PodObjectRef::from_value(&ObjectType::OBJECT_FORMAT(
ObjectPropsIterator::build()
.push_value(&ObjectFormatType::MEDIA_TYPE(
PodIdRef::from_value(&MediaType::AUDIO).unwrap().as_pod(),
))
.unwrap()
.push_value(&ObjectFormatType::MEDIA_SUBTYPE(
PodIdRef::from_value(&MediaSubType::DSP).unwrap().as_pod(),
))
.unwrap()
.into_pod_iter()
.iter(),
))
.unwrap();
allocated.as_pod_mut().set_body_id(123);
assert_eq!(allocated.as_pod().body_id(), 123);
if let ObjectType::OBJECT_FORMAT(mut props) =
allocated.as_pod().param_value(ParamType::FORMAT).unwrap()
{
if let ObjectFormatType::MEDIA_TYPE(v) = props.next().unwrap().value().unwrap() {
assert_eq!(v.value().unwrap(), MediaType::AUDIO);
} else {
panic!()
}
if let ObjectFormatType::MEDIA_SUBTYPE(v) = props.next().unwrap().value().unwrap() {
assert_eq!(v.value().unwrap(), MediaSubType::DSP);
} else {
panic!()
}
assert!(props.next().is_none())
} else {
panic!()
}
}