1
0
Fork 0
forked from wry/wry

pipewire: add pipewire client

This commit is contained in:
Julian Orth 2022-07-30 16:08:44 +02:00
parent 2512470231
commit 2568b7b1f5
19 changed files with 4573 additions and 2 deletions

View file

@ -1,5 +1,8 @@
use {
crate::{
pipewire::pw_pod::{
SPA_VIDEO_FORMAT_BGRx, SpaVideoFormat, SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_NV12,
},
render::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE},
utils::debug_fn::debug_fn,
},
@ -19,6 +22,7 @@ pub struct Format {
pub external_only_guess: bool,
pub has_alpha: bool,
pub shm_supported: bool,
pub pipewire: SpaVideoFormat,
}
static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
@ -29,10 +33,22 @@ static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
map
});
static PW_FORMATS_MAP: Lazy<AHashMap<SpaVideoFormat, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
assert!(map.insert(format.pipewire, format).is_none());
}
map
});
pub fn formats() -> &'static AHashMap<u32, &'static Format> {
&*FORMATS_MAP
}
pub fn pw_formats() -> &'static AHashMap<SpaVideoFormat, &'static Format> {
&*PW_FORMATS_MAP
}
const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
}
@ -77,6 +93,7 @@ pub static FORMATS: &[Format] = &[
external_only_guess: false,
has_alpha: true,
shm_supported: true,
pipewire: SPA_VIDEO_FORMAT_BGRA,
},
Format {
name: "xrgb8888",
@ -88,6 +105,7 @@ pub static FORMATS: &[Format] = &[
external_only_guess: false,
has_alpha: false,
shm_supported: true,
pipewire: SPA_VIDEO_FORMAT_BGRx,
},
Format {
name: "nv12",
@ -99,6 +117,7 @@ pub static FORMATS: &[Format] = &[
external_only_guess: true,
has_alpha: false,
shm_supported: false,
pipewire: SPA_VIDEO_FORMAT_NV12,
},
// Format {
// id: fourcc_code('C', '8', ' ', ' '),

View file

@ -8,7 +8,6 @@ use {
utils::buffd::{MsgParser, MsgParserError},
wire::{xdg_positioner::*, XdgPositionerId},
},
bitflags::bitflags,
std::{cell::RefCell, rc::Rc},
thiserror::Error,
};
@ -53,7 +52,7 @@ impl Edge {
}
}
bitflags! {
bitflags::bitflags! {
#[derive(Default)]
pub struct CA: u32 {
const NONE = 0;

View file

@ -489,3 +489,181 @@ macro_rules! containing_node_impl {
}
};
}
macro_rules! bitflags {
($name:ident: $rep:ty; $($var:ident = $val:expr,)*) => {
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct $name(pub $rep);
$(
#[allow(dead_code)]
pub const $var: $name = $name($val);
)*
#[allow(dead_code)]
impl $name {
pub fn none() -> Self {
Self(0)
}
pub fn is_some(self) -> bool {
self.0 != 0
}
}
impl crate::utils::bitflags::BitflagsExt for $name {
fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
fn not_contains(self, other: Self) -> bool {
self.0 & other.0 != other.0
}
fn intersects(self, other: Self) -> bool {
self.0 & other.0 != 0
}
}
impl std::ops::BitOr for $name {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitAnd for $name {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl std::ops::BitOrAssign for $name {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAndAssign for $name {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl std::ops::Not for $name {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut any = false;
let mut v = self.0;
$(
if v & $val == $val {
if any {
write!(f, "|")?;
}
any = true;
write!(f, "{}", stringify!($var))?;
v &= !$val;
}
)*
if !any || v != 0 {
if any {
write!(f, "|")?;
}
write!(f, "0x{:x}", v)?;
}
Ok(())
}
}
}
}
macro_rules! pw_opcodes {
($name:ident; $($var:ident = $val:expr,)*) => {
#[derive(Copy, Clone, Debug)]
#[allow(dead_code)]
pub enum $name {
$(
$var,
)*
}
#[allow(dead_code)]
impl $name {
pub fn from_id(id: u8) -> Option<Self> {
let v = match id {
$($val => Self::$var,)*
_ => return None,
};
Some(v)
}
pub fn name(self) -> &'static str {
match self {
$(Self::$var => stringify!($var),)*
}
}
}
impl crate::pipewire::pw_object::PwOpcode for $name {
fn id(&self) -> u8 {
match self {
$(Self::$var => $val,)*
}
}
}
}
}
macro_rules! pw_object_base {
($name:ident, $if:expr, $events:ident; $($event:ident => $method:ident,)*) => {
impl crate::pipewire::pw_object::PwObjectBase for $name {
fn data(&self) -> &crate::pipewire::pw_object::PwObjectData {
&self.data
}
fn interface(&self) -> &str {
$if
}
fn handle_msg(self: std::rc::Rc<Self>, opcode: u8, parser: crate::pipewire::pw_parser::PwParser<'_>) -> Result<(), crate::pipewire::pw_object::PwObjectError> {
match $events::from_id(opcode) {
None => Err(crate::pipewire::pw_object::PwObjectError {
interface: $if,
source: crate::pipewire::pw_object::PwObjectErrorType::UnknownEvent(opcode),
}),
Some(m) => {
let (res, method) = match m {
$(
$events::$event => (self.$method(parser), stringify!($event)),
)*
};
match res {
Ok(_) => Ok(()),
Err(source) => Err(crate::pipewire::pw_object::PwObjectError {
interface: $if,
source: crate::pipewire::pw_object::PwObjectErrorType::EventError {
method,
source: Box::new(source),
},
})
}
},
}
}
fn event_name(&self, opcode: u8) -> Option<&'static str> {
$events::from_id(opcode).map(|o| o.name())
}
}
}
}

View file

@ -59,6 +59,7 @@ mod logger;
mod logind;
mod object;
mod pango;
mod pipewire;
mod rect;
mod render;
mod screenshoter;

9
src/pipewire.rs Normal file
View file

@ -0,0 +1,9 @@
#![allow(dead_code)]
pub mod pw_con;
pub mod pw_formatter;
pub mod pw_ifs;
pub mod pw_mem;
pub mod pw_object;
pub mod pw_parser;
pub mod pw_pod;

451
src/pipewire/pw_con.rs Normal file
View file

@ -0,0 +1,451 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
io_uring::IoUring,
pipewire::{
pw_formatter::{format, PwFormatter},
pw_ifs::{
pw_client::{PwClient, PwClientMethods},
pw_client_node::{
PwClientNode, PW_CLIENT_NODE_FACTORY, PW_CLIENT_NODE_INTERFACE,
PW_CLIENT_NODE_VERSION,
},
pw_core::{PwCore, PwCoreMethods, PW_CORE_VERSION},
pw_registry::{PwRegistry, PW_REGISTRY_VERSION},
},
pw_mem::PwMemPool,
pw_object::{PwObject, PwObjectData, PwObjectError, PwOpcode},
pw_parser::{PwParser, PwParserError},
},
utils::{
bitfield::Bitfield,
bufio::{BufIo, BufIoError, BufIoIncoming, BufIoMessage},
clonecell::CloneCell,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
numcell::NumCell,
oserror::OsError,
xrd::xrd,
},
},
std::{
cell::{Cell, RefCell},
fmt::Display,
io::Write,
rc::{Rc, Weak},
},
thiserror::Error,
uapi::{c, OwnedFd},
};
#[derive(Debug, Error)]
pub enum PwConError {
#[error("Could not create a unix socket")]
CreateSocket(#[source] OsError),
#[error("Could not connect to the pipewire daemon")]
ConnectSocket(#[source] OsError),
#[error(transparent)]
BufIoError(#[from] BufIoError),
#[error("Server did not sent a required fd")]
MissingFd,
#[error("XDG_RUNTIME_DIR is not set")]
XrdNotSet,
#[error(transparent)]
PwObjectError(#[from] PwObjectError),
#[error(transparent)]
PwParserError(#[from] PwParserError),
}
pub struct PwConHolder {
pub con: Rc<PwCon>,
outgoing: Cell<Option<SpawnedFuture<()>>>,
incoming: Cell<Option<SpawnedFuture<()>>>,
}
pub struct PwCon {
send_seq: NumCell<u32>,
pub io: Rc<BufIo>,
holder: CloneCell<Weak<PwConHolder>>,
dead: Cell<bool>,
pub objects: CopyHashMap<u32, Rc<dyn PwObject>>,
pub ids: RefCell<Bitfield>,
pub mem: PwMemPool,
pub ring: Rc<IoUring>,
pub eng: Rc<AsyncEngine>,
pub owner: CloneCell<Option<Rc<dyn PwConOwner>>>,
registry_generation: Cell<u64>,
ack_registry_generation: Cell<u64>,
}
pub trait PwConOwner {
fn killed(&self) {}
}
impl PwCon {
pub fn create_client_node(self: &Rc<Self>, props: &[(String, String)]) -> Rc<PwClientNode> {
let node = Rc::new(PwClientNode {
data: self.proxy_data(),
con: self.clone(),
ios: Default::default(),
owner: CloneCell::new(None),
ports: Default::default(),
port_out_free: RefCell::new(Default::default()),
port_in_free: RefCell::new(Default::default()),
activation: Default::default(),
transport_in: Cell::new(None),
transport_out: Default::default(),
activations: Default::default(),
});
if !self.dead.get() {
self.objects.set(node.data.id, node.clone());
}
self.create_object(
PW_CLIENT_NODE_FACTORY,
PW_CLIENT_NODE_INTERFACE,
PW_CLIENT_NODE_VERSION,
props,
node.data.id,
);
node.send_update();
node
}
pub fn destroy_obj(&self, obj: &impl PwObject) {
obj.break_loops();
self.send2(0, "core", PwCoreMethods::Destroy, |f| {
f.write_struct(|f| {
f.write_uint(obj.data().id);
});
});
self.objects.remove(&obj.data().id);
}
pub fn kill(&self) {
for (_, obj) in self.objects.lock().drain() {
obj.break_loops();
}
self.io.shutdown();
self.dead.set(true);
if let Some(con) = self.holder.get().upgrade() {
con.outgoing.take();
con.incoming.take();
}
if let Some(owner) = self.owner.take() {
owner.killed();
}
}
pub fn id(&self) -> u32 {
self.ids.borrow_mut().acquire()
}
pub fn proxy_data(&self) -> PwObjectData {
PwObjectData {
id: self.id(),
bound_id: Cell::new(None),
sync_id: Default::default(),
}
}
pub fn send<P, O, F>(&self, proxy: &P, opcode: O, f: F)
where
P: PwObject,
O: PwOpcode,
F: FnOnce(&mut PwFormatter),
{
self.send2(proxy.data().id, proxy.interface(), opcode, f);
}
pub fn send2<O, F>(&self, id: u32, interface: &str, opcode: O, f: F)
where
O: PwOpcode,
F: FnOnce(&mut PwFormatter),
{
if self.dead.get() {
return;
}
let mut buf = self.io.buf();
let mut fds = vec![];
format(
&mut buf,
&mut fds,
id,
opcode.id(),
self.send_seq.fetch_add(1),
|fmt| {
f(fmt);
if self.ack_registry_generation.get() != self.registry_generation.get() {
let gen = self.registry_generation.get();
fmt.write_struct(|f| {
f.write_id(FOOTER_REGISTRY_GENERATION);
f.write_struct(|f| {
f.write_ulong(gen);
});
});
self.ack_registry_generation.set(gen);
}
},
);
if log::log_enabled!(log::Level::Trace) {
log::trace!("CALL {}@{}: `{:?}`:", interface, id, opcode);
let mut parser = PwParser::new(&buf[16..], &fds);
while parser.len() > 0 {
log::trace!("{:#?}", parser.read_pod().unwrap());
}
}
self.io.send(BufIoMessage { fds, buf });
}
#[allow(dead_code)]
pub fn sync<P: PwObject>(&self, p: &P) {
let seq = p.data().sync_id.fetch_add(1) + 1;
self.send2(0, "core", PwCoreMethods::Sync, |f| {
f.write_struct(|f| {
f.write_uint(p.data().id);
f.write_uint(seq);
});
});
}
pub fn send_hello(&self) {
self.send2(0, "core", PwCoreMethods::Hello, |f| {
f.write_struct(|f| f.write_int(PW_CORE_VERSION));
});
}
#[allow(dead_code)]
pub fn get_registry(self: &Rc<Self>) -> Rc<PwRegistry> {
let registry = Rc::new(PwRegistry {
data: self.proxy_data(),
con: self.clone(),
});
if !self.dead.get() {
self.objects.set(registry.data.id, registry.clone());
}
self.send2(0, "core", PwCoreMethods::GetRegistry, |f| {
f.write_struct(|f| {
f.write_int(PW_REGISTRY_VERSION);
f.write_uint(registry.data.id);
});
});
registry
}
pub fn create_object(
&self,
factory: &str,
ty: &str,
version: i32,
props: &[(String, String)],
new_id: u32,
) {
self.send2(0, "core", PwCoreMethods::CreateObject, |f| {
f.write_struct(|f| {
f.write_string(factory);
f.write_string(ty);
f.write_int(version);
f.write_struct(|f| {
f.write_int(props.len() as _);
for (key, val) in props {
f.write_string(key);
f.write_string(val);
}
});
f.write_uint(new_id);
});
});
}
pub fn send_properties(&self) {
self.send2(1, "client", PwClientMethods::UpdateProperties, |f| {
f.write_struct(|f| {
f.write_struct(|f| {
f.write_int(1);
f.write_string("application.name");
f.write_string("jay-portal");
});
});
});
}
async fn handle_outgoing(self: Rc<Self>) {
if let Err(e) = self.io.clone().outgoing().await {
log::error!("{}", ErrorFmt(e));
}
self.kill();
}
async fn handle_incoming(self: Rc<Self>) {
let incoming = Incoming {
incoming: self.io.clone().incoming(),
con: self.clone(),
buf: vec![],
fds: vec![],
};
incoming.run().await;
}
}
impl Drop for PwConHolder {
fn drop(&mut self) {
self.con.owner.take();
self.con.kill();
}
}
impl PwConHolder {
#[allow(dead_code)]
pub fn new(eng: &Rc<AsyncEngine>, ring: &Rc<IoUring>) -> Result<Rc<Self>, PwConError> {
let fd = match uapi::socket(
c::AF_UNIX,
c::SOCK_STREAM | c::SOCK_CLOEXEC | c::SOCK_NONBLOCK,
0,
) {
Ok(fd) => Rc::new(fd),
Err(e) => return Err(PwConError::CreateSocket(e.into())),
};
let mut addr = c::sockaddr_un {
sun_family: c::AF_UNIX as _,
..uapi::pod_zeroed()
};
let xrd = match xrd() {
Some(xrd) => xrd,
_ => return Err(PwConError::XrdNotSet),
};
{
let mut path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
let _ = write!(path, "{}/pipewire-0", xrd);
}
if let Err(e) = uapi::connect(fd.raw(), &addr) {
return Err(PwConError::ConnectSocket(e.into()));
}
let io = Rc::new(BufIo::new(&fd, ring));
let data = Rc::new(PwCon {
send_seq: Default::default(),
io,
holder: Default::default(),
dead: Cell::new(false),
objects: Default::default(),
ids: Default::default(),
mem: Default::default(),
ring: ring.clone(),
eng: eng.clone(),
owner: Default::default(),
registry_generation: Cell::new(0),
ack_registry_generation: Cell::new(0),
});
let core = Rc::new(PwCore {
data: data.proxy_data(),
con: data.clone(),
});
let client = Rc::new(PwClient {
data: data.proxy_data(),
con: data.clone(),
});
data.objects.set(0, core.clone());
data.objects.set(1, client.clone());
data.send_hello();
data.send_properties();
let con = Rc::new(PwConHolder {
outgoing: Cell::new(Some(eng.spawn(data.clone().handle_outgoing()))),
incoming: Cell::new(Some(eng.spawn(data.clone().handle_incoming()))),
con: data,
});
con.con.holder.set(Rc::downgrade(&con));
Ok(con)
}
}
struct Incoming {
con: Rc<PwCon>,
incoming: BufIoIncoming,
buf: Vec<u8>,
fds: Vec<Rc<OwnedFd>>,
}
impl Incoming {
async fn run(mut self) {
loop {
if let Err(e) = self.handle_msg().await {
log::error!("Could not handle incoming message: {}", ErrorFmt(e));
self.con.kill();
return;
}
}
}
async fn handle_msg(&mut self) -> Result<(), PwConError> {
self.buf.clear();
self.incoming.fill_msg_buf(16, &mut self.buf).await?;
let id: u32 = uapi::pod_read(&self.buf[0..4]).unwrap();
let p2: u32 = uapi::pod_read(&self.buf[4..8]).unwrap();
let opcode = (p2 >> 24) as u8;
let size = (p2 & 0xff_ffff) as usize;
let _seq: u32 = uapi::pod_read(&self.buf[8..12]).unwrap();
let n_fds: u32 = uapi::pod_read(&self.buf[12..16]).unwrap();
for _ in 0..n_fds {
match self.incoming.fds.pop_front() {
Some(fd) => self.fds.push(fd),
_ => return Err(PwConError::MissingFd),
}
}
self.buf.clear();
self.incoming.fill_msg_buf(size, &mut self.buf).await?;
if let Err(e) = self.handle_msg_data(id, opcode) {
log::warn!("Could not handle incoming message: {}", ErrorFmt(e));
}
self.fds.clear();
Ok(())
}
fn handle_msg_data(&self, id: u32, opcode: u8) -> Result<(), PwConError> {
let parser = PwParser::new(&self.buf, &self.fds);
{
let mut parser = parser;
parser.skip()?;
if parser.len() > 0 {
let s1 = parser.read_struct()?;
let mut p2 = s1.fields;
while p2.len() > 0 {
let opcode = p2.read_id()?;
let s2 = p2.read_struct()?;
if opcode == FOOTER_REGISTRY_GENERATION {
let mut p3 = s2.fields;
let gen = p3.read_ulong()?;
self.con.registry_generation.set(gen);
log::debug!("registry generation = {}", gen);
} else {
log::warn!("Unknown message footer: {}", opcode);
}
}
}
}
if let Some(obj) = self.con.objects.get(&id) {
'log: {
if log::log_enabled!(log::Level::Trace) {
let s;
let op: &dyn Display = match obj.event_name(opcode) {
Some(e) => {
s = e;
if e == "Done" && obj.interface() == "core" {
break 'log;
}
&s
}
_ => &opcode,
};
log::trace!("EVENT {}@{}: `{}`:", obj.interface(), obj.data().id, op);
let mut parser = parser;
while parser.len() > 0 {
log::trace!("{:#?}", parser.read_pod().unwrap());
}
}
}
obj.handle_msg(opcode, parser)?;
}
Ok(())
}
}
const FOOTER_REGISTRY_GENERATION: u32 = 0;

View file

@ -0,0 +1,304 @@
use {
crate::pipewire::pw_pod::{
PW_TYPE_Array, PW_TYPE_Bitmap, PW_TYPE_Bool, PW_TYPE_Bytes, PW_TYPE_Choice, PW_TYPE_Double,
PW_TYPE_Fd, PW_TYPE_Float, PW_TYPE_Fraction, PW_TYPE_Id, PW_TYPE_Int, PW_TYPE_Long,
PW_TYPE_None, PW_TYPE_Object, PW_TYPE_Rectangle, PW_TYPE_String, PW_TYPE_Struct,
PwChoiceType, PwPodObjectType, PwPodType, PwPropFlag,
},
std::rc::Rc,
uapi::OwnedFd,
};
pub struct PwFormatter<'a> {
data: &'a mut Vec<u8>,
fds: &'a mut Vec<Rc<OwnedFd>>,
array: bool,
first: bool,
}
impl<'a> PwFormatter<'a> {
pub fn write_bool(&mut self, b: bool) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&4u32));
self.data.extend_from_slice(uapi::as_bytes(&PW_TYPE_Bool.0));
}
self.data.extend_from_slice(uapi::as_bytes(&(b as u32)));
if !self.array {
self.data.extend_from_slice(uapi::as_bytes(&0u32));
}
self.first = false;
}
pub fn write_id(&mut self, id: u32) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&4u32));
self.data.extend_from_slice(uapi::as_bytes(&PW_TYPE_Id.0));
}
self.data.extend_from_slice(uapi::as_bytes(&id));
if !self.array {
self.data.extend_from_slice(uapi::as_bytes(&0u32));
}
self.first = false;
}
pub fn write_object<F>(&mut self, ty: PwPodObjectType, id: u32, f: F)
where
F: FnOnce(&mut PwObjectFormatter),
{
let start = self.data.len();
self.data.extend_from_slice(uapi::as_bytes(&0u32));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Object.0));
self.data.extend_from_slice(uapi::as_bytes(&ty.0));
self.data.extend_from_slice(uapi::as_bytes(&id));
let mut fmt = PwObjectFormatter {
data: self.data,
fds: self.fds,
};
f(&mut fmt);
let len = (self.data.len() - start - 8) as u32;
self.data[start..start + 4].copy_from_slice(uapi::as_bytes(&len));
}
pub fn write_uint(&mut self, int: u32) {
self.write_int(int as _)
}
pub fn write_int(&mut self, int: i32) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&4u32));
self.data.extend_from_slice(uapi::as_bytes(&PW_TYPE_Int.0));
}
self.data.extend_from_slice(uapi::as_bytes(&int));
if !self.array {
self.data.extend_from_slice(uapi::as_bytes(&0u32));
}
self.first = false;
}
pub fn write_ulong(&mut self, long: u64) {
self.write_long(long as _)
}
pub fn write_long(&mut self, long: i64) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&8u32));
self.data.extend_from_slice(uapi::as_bytes(&PW_TYPE_Long.0));
}
self.data.extend_from_slice(uapi::as_bytes(&long));
self.first = false;
}
#[allow(dead_code)]
pub fn write_float(&mut self, float: f32) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&4u32));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Float.0));
}
self.data.extend_from_slice(uapi::as_bytes(&float));
if !self.array {
self.data.extend_from_slice(uapi::as_bytes(&0u32));
}
self.first = false;
}
#[allow(dead_code)]
pub fn write_double(&mut self, double: f64) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&8u32));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Double.0));
}
self.data.extend_from_slice(uapi::as_bytes(&double));
}
pub fn write_string<S: AsRef<[u8]> + ?Sized>(&mut self, s: &S) {
let s = s.as_ref();
self.data
.extend_from_slice(uapi::as_bytes(&(s.len() as u32 + 1)));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_String.0));
self.data.extend_from_slice(s);
self.data.push(0);
self.pad();
}
#[allow(dead_code)]
pub fn write_bytes(&mut self, s: &[u8]) {
self.data
.extend_from_slice(uapi::as_bytes(&(s.len() as u32)));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Bytes.0));
self.data.extend_from_slice(s);
self.pad();
}
pub fn write_rectangle(&mut self, width: u32, height: u32) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&8u32));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Rectangle.0));
}
self.data.extend_from_slice(uapi::as_bytes(&width));
self.data.extend_from_slice(uapi::as_bytes(&height));
self.first = false;
}
#[allow(dead_code)]
pub fn write_fraction(&mut self, num: i32, denom: i32) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&8u32));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Fraction.0));
}
self.data.extend_from_slice(uapi::as_bytes(&num));
self.data.extend_from_slice(uapi::as_bytes(&denom));
self.first = false;
}
pub fn write_none(&mut self) {
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&0u32));
self.data.extend_from_slice(uapi::as_bytes(&PW_TYPE_None.0));
}
self.first = false;
}
#[allow(dead_code)]
pub fn write_bitmap(&mut self, s: &[u8]) {
self.data
.extend_from_slice(uapi::as_bytes(&(s.len() as u32)));
self.data
.extend_from_slice(uapi::as_bytes(&PW_TYPE_Bitmap.0));
self.data.extend_from_slice(s);
self.pad();
}
#[allow(dead_code)]
pub fn write_fd(&mut self, fd: &Rc<OwnedFd>) {
let pos = self.fds.len() as u64;
self.fds.push(fd.clone());
if !self.array || self.first {
self.data.extend_from_slice(uapi::as_bytes(&8u32));
self.data.extend_from_slice(uapi::as_bytes(&PW_TYPE_Fd.0));
}
self.data.extend_from_slice(uapi::as_bytes(&pos));
self.first = false;
}
pub fn write_struct<F>(&mut self, f: F)
where
F: FnOnce(&mut PwFormatter),
{
self.write_compound(PW_TYPE_Struct, |fmt| {
let mut fmt = PwFormatter {
data: fmt.data,
fds: fmt.fds,
array: false,
first: false,
};
f(&mut fmt);
});
}
#[allow(dead_code)]
pub fn write_array<F>(&mut self, f: F)
where
F: FnOnce(&mut PwFormatter),
{
self.write_compound(PW_TYPE_Array, |fmt| {
fmt.write_array_body(f);
});
self.pad();
}
fn write_array_body<F>(&mut self, f: F)
where
F: FnOnce(&mut PwFormatter),
{
let mut fmt = PwFormatter {
data: self.data,
fds: self.fds,
array: true,
first: true,
};
f(&mut fmt);
if fmt.first {
fmt.write_none();
}
}
pub fn write_choice<F>(&mut self, ty: PwChoiceType, flags: u32, f: F)
where
F: FnOnce(&mut PwFormatter),
{
self.write_compound(PW_TYPE_Choice, |fmt| {
fmt.data.extend_from_slice(uapi::as_bytes(&ty.0));
fmt.data.extend_from_slice(uapi::as_bytes(&flags));
fmt.write_array_body(f);
});
self.pad();
}
fn write_compound<F>(&mut self, ty: PwPodType, f: F)
where
F: FnOnce(&mut PwFormatter),
{
let start = self.data.len();
self.data.extend_from_slice(uapi::as_bytes(&0u32));
self.data.extend_from_slice(uapi::as_bytes(&ty.0));
f(self);
let len = (self.data.len() - start - 8) as u32;
self.data[start..start + 4].copy_from_slice(uapi::as_bytes(&len));
}
fn pad(&mut self) {
let todo = self.data.len().wrapping_neg() & 7;
self.data.extend_from_slice(&uapi::as_bytes(&0u64)[..todo]);
}
}
pub struct PwObjectFormatter<'a> {
data: &'a mut Vec<u8>,
fds: &'a mut Vec<Rc<OwnedFd>>,
}
impl<'a> PwObjectFormatter<'a> {
pub fn write_property<F>(&mut self, key: u32, flags: PwPropFlag, f: F)
where
F: FnOnce(&mut PwFormatter),
{
self.data.extend_from_slice(uapi::as_bytes(&key));
self.data.extend_from_slice(uapi::as_bytes(&flags.0));
let mut fmt = PwFormatter {
data: self.data,
fds: self.fds,
array: false,
first: false,
};
f(&mut fmt);
}
}
pub fn format<F>(buf: &mut Vec<u8>, fds: &mut Vec<Rc<OwnedFd>>, id: u32, opcode: u8, seq: u32, f: F)
where
F: FnOnce(&mut PwFormatter),
{
buf.clear();
buf.extend_from_slice(uapi::as_bytes(&id));
buf.extend_from_slice(uapi::as_bytes(&0u32));
buf.extend_from_slice(uapi::as_bytes(&seq));
buf.extend_from_slice(uapi::as_bytes(&0u32));
let mut fmt = PwFormatter {
data: buf,
fds,
array: false,
first: false,
};
f(&mut fmt);
let p2 = (buf.len() - 16) as u32 | ((opcode as u32) << 24);
buf[4..8].copy_from_slice(uapi::as_bytes(&p2));
let nfds = fds.len() as u32;
buf[12..16].copy_from_slice(uapi::as_bytes(&nfds));
}

4
src/pipewire/pw_ifs.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod pw_client;
pub mod pw_client_node;
pub mod pw_core;
pub mod pw_registry;

View file

@ -0,0 +1,61 @@
use {
crate::pipewire::{
pw_con::PwCon,
pw_object::{PwObject, PwObjectData},
pw_parser::{PwParser, PwParserError},
},
std::rc::Rc,
thiserror::Error,
};
pw_opcodes! {
PwClientMethods;
Error = 1,
UpdateProperties = 2,
GetPermissions = 3,
UpdatePermissions = 4,
}
pw_opcodes! {
PwClientEvents;
Info = 0,
Permissions = 1,
}
pub struct PwClient {
pub data: PwObjectData,
pub con: Rc<PwCon>,
}
impl PwClient {
fn handle_info(&self, mut p: PwParser<'_>) -> Result<(), PwClientError> {
let s1 = p.read_struct()?;
let mut p2 = s1.fields;
let _id = p2.read_int()?;
let _change_mask = p2.read_long()?;
let props = p2.read_dict_struct()?;
log::debug!("Pipewire properties: {:#?}", props);
Ok(())
}
fn handle_permissions(&self, _p: PwParser<'_>) -> Result<(), PwClientError> {
Ok(())
}
}
pw_object_base! {
PwClient, "client", PwClientEvents;
Info => handle_info,
Permissions => handle_permissions,
}
impl PwObject for PwClient {}
#[derive(Debug, Error)]
pub enum PwClientError {
#[error(transparent)]
PwParserError(#[from] PwParserError),
}

View file

@ -0,0 +1,874 @@
#![allow(non_upper_case_globals)]
use {
crate::{
async_engine::SpawnedFuture,
format::{pw_formats, Format},
pipewire::{
pw_con::PwCon,
pw_mem::{PwMemError, PwMemMap, PwMemSlice, PwMemTyped},
pw_object::{PwObject, PwObjectData},
pw_parser::{PwParser, PwParserError},
pw_pod::{
pw_node_activation, spa_chunk, spa_io_buffers, spa_meta_bitmap, spa_meta_busy,
spa_meta_cursor, spa_meta_header, spa_meta_region, PW_CHOICE_Enum, PW_CHOICE_Flags,
PW_OBJECT_Format, PW_OBJECT_ParamBuffers, PW_OBJECT_ParamMeta, PwIoType,
PwPodFraction, PwPodObject, PwPodRectangle, PwPropFlag, SPA_DATA_DmaBuf,
SPA_DATA_MemFd, SPA_DATA_MemPtr, SPA_FORMAT_VIDEO_format,
SPA_FORMAT_VIDEO_framerate, SPA_FORMAT_VIDEO_modifier, SPA_FORMAT_VIDEO_size,
SPA_FORMAT_mediaSubtype, SPA_FORMAT_mediaType, SPA_IO_Buffers, SPA_META_Bitmap,
SPA_META_Busy, SPA_META_Control, SPA_META_Cursor, SPA_META_Header,
SPA_META_VideoCrop, SPA_META_VideoDamage, SPA_NODE_COMMAND_Pause,
SPA_NODE_COMMAND_Start, SPA_NODE_COMMAND_Suspend, SPA_PARAM_BUFFERS_blocks,
SPA_PARAM_BUFFERS_buffers, SPA_PARAM_BUFFERS_dataType, SPA_PARAM_Buffers,
SPA_PARAM_EnumFormat, SPA_PARAM_Format, SPA_PARAM_META_size, SPA_PARAM_META_type,
SPA_PARAM_Meta, SpaDataFlags, SpaDataType, SpaDirection, SpaIoType,
SpaMediaSubtype, SpaMediaType, SpaMetaType, SpaNodeBuffersFlags, SpaNodeCommand,
SpaParamType, SpaVideoFormat, SPA_DATA_FLAG_READABLE, SPA_DIRECTION_INPUT,
SPA_DIRECTION_OUTPUT, SPA_NODE_BUFFERS_FLAG_ALLOC, SPA_PARAM_INFO_READ,
SPA_PORT_FLAG, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS,
},
},
utils::{
bitfield::Bitfield, bitflags::BitflagsExt, clonecell::CloneCell,
copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
},
video::dmabuf::DmaBuf,
},
std::{
cell::{Cell, RefCell},
mem,
ops::Deref,
rc::Rc,
},
thiserror::Error,
uapi::OwnedFd,
};
pw_opcodes! {
PwClientNodeMethods;
GetNode = 1,
Update = 2,
PortUpdate = 3,
SetActive = 4,
Event = 5,
PortBuffers = 6,
}
pw_opcodes! {
PwClientNodeEvents;
Transport = 0,
SetParam = 1,
SetIo = 2,
Event = 3,
Command = 4,
AddPort = 5,
RemovePort = 6,
PortSetParam = 7,
PortUseBuffers = 8,
PortSetIo = 9,
SetActivation = 10,
PortSetMixInfo = 11,
}
pub trait PwClientNodeOwner {
fn port_format_changed(&self, port: &Rc<PwClientNodePort>) {
let _ = port;
}
fn use_buffers(&self, port: &Rc<PwClientNodePort>) {
let _ = port;
}
fn start(self: Rc<Self>) {}
fn pause(self: Rc<Self>) {}
fn suspend(self: Rc<Self>) {}
fn bound_id(&self, id: u32) {
let _ = id;
}
}
bitflags! {
PwClientNodePortChanges: u32;
CHANGED_SUPPORTED_PARAMS = 1 << 0,
}
bitflags! {
PwClientNodePortSupportedMetas: u32;
SUPPORTED_META_HEADER = 1 << 0,
SUPPORTED_META_BUSY = 1 << 1,
SUPPORTED_META_VIDEO_CROP = 1 << 2,
}
pub struct PwClientNodePort {
pub node: Rc<PwClientNode>,
pub direction: SpaDirection,
pub id: u32,
pub destroyed: Cell<bool>,
pub effective_format: Cell<PwClientNodePortFormat>,
pub supported_formats: RefCell<Option<PwClientNodePortSupportedFormats>>,
pub supported_metas: Cell<PwClientNodePortSupportedMetas>,
pub can_alloc_buffers: Cell<bool>,
pub buffers: RefCell<Vec<Rc<PwClientNodeBuffer>>>,
pub buffer_config: Cell<Option<PwClientNodeBufferConfig>>,
pub io_buffers: CopyHashMap<u32, Rc<PwMemTyped<spa_io_buffers>>>,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct PwClientNodeBufferConfig {
pub num_buffers: usize,
pub planes: usize,
pub size: Option<u32>,
pub stride: Option<u32>,
pub align: usize,
pub data_type: SpaDataType,
}
pub struct PwClientNodeBuffer {
pub meta_header: Option<Rc<PwMemTyped<spa_meta_header>>>,
pub meta_busy: Option<Rc<PwMemTyped<spa_meta_busy>>>,
pub meta_video_crop: Option<Rc<PwMemTyped<spa_meta_region>>>,
pub chunks: Vec<Rc<PwMemTyped<spa_chunk>>>,
pub slices: Vec<Rc<PwMemSlice>>,
}
#[derive(Clone, Debug, Default)]
pub struct PwClientNodePortSupportedFormats {
pub media_type: Option<SpaMediaType>,
pub media_sub_type: Option<SpaMediaSubtype>,
pub video_size: Option<PwPodRectangle>,
pub formats: Vec<&'static Format>,
pub modifiers: Vec<u64>,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct PwClientNodePortFormat {
pub media_type: Option<SpaMediaType>,
pub media_sub_type: Option<SpaMediaSubtype>,
pub video_size: Option<PwPodRectangle>,
pub format: Option<&'static Format>,
pub framerate: Option<PwPodFraction>,
}
pub struct PwClientNode {
pub data: PwObjectData,
pub con: Rc<PwCon>,
pub ios: CopyHashMap<PwIoType, Rc<PwMemMap>>,
pub owner: CloneCell<Option<Rc<dyn PwClientNodeOwner>>>,
pub ports: CopyHashMap<(SpaDirection, u32), Rc<PwClientNodePort>>,
pub port_out_free: RefCell<Bitfield>,
pub port_in_free: RefCell<Bitfield>,
pub activation: CloneCell<Option<Rc<PwMemTyped<pw_node_activation>>>>,
pub transport_in: Cell<Option<SpawnedFuture<()>>>,
pub transport_out: CloneCell<Option<Rc<OwnedFd>>>,
pub activations: CopyHashMap<u32, Rc<PwNodeActivation>>,
}
pub struct PwNodeActivation {
pub activation: Rc<PwMemTyped<pw_node_activation>>,
pub fd: Rc<OwnedFd>,
}
// pub struct PwNodeBuffer {
// pub width: i32,
// pub height: i32,
// pub stride: i32,
// pub offset: i32,
// pub fd: Rc<OwnedFd>,
// }
pub const PW_CLIENT_NODE_FACTORY: &str = "client-node";
pub const PW_CLIENT_NODE_INTERFACE: &str = "PipeWire:Interface:ClientNode";
pub const PW_CLIENT_NODE_VERSION: i32 = 4;
#[allow(dead_code)]
const PW_CLIENT_NODE_UPDATE_PARAMS: u32 = 1 << 0;
const PW_CLIENT_NODE_UPDATE_INFO: u32 = 1 << 1;
const SPA_NODE_CHANGE_MASK_FLAGS: u64 = 1 << 0;
#[allow(dead_code)]
const SPA_NODE_CHANGE_MASK_PROPS: u64 = 1 << 1;
const SPA_NODE_CHANGE_MASK_PARAMS: u64 = 1 << 2;
const PW_CLIENT_NODE_PORT_UPDATE_PARAMS: u32 = 1 << 0;
const PW_CLIENT_NODE_PORT_UPDATE_INFO: u32 = 1 << 1;
const SPA_PORT_CHANGE_MASK_FLAGS: u64 = 1 << 0;
const SPA_PORT_CHANGE_MASK_RATE: u64 = 1 << 1;
#[allow(dead_code)]
const SPA_PORT_CHANGE_MASK_PROPS: u64 = 1 << 2;
const SPA_PORT_CHANGE_MASK_PARAMS: u64 = 1 << 3;
impl PwClientNode {
pub fn send_update(&self) {
self.con.send(self, PwClientNodeMethods::Update, |f| {
f.write_struct(|f| {
f.write_uint(PW_CLIENT_NODE_UPDATE_INFO);
f.write_uint(0);
f.write_struct(|f| {
f.write_uint(0);
f.write_uint(1);
f.write_ulong(SPA_NODE_CHANGE_MASK_PARAMS | SPA_NODE_CHANGE_MASK_FLAGS);
f.write_ulong(0);
f.write_uint(0);
f.write_uint(0);
});
});
});
}
pub fn send_active(&self, active: bool) {
self.con.send(self, PwClientNodeMethods::SetActive, |f| {
f.write_struct(|f| {
f.write_bool(active);
});
});
}
pub fn create_port(self: &Rc<Self>, output: bool) -> Rc<PwClientNodePort> {
let (ids, direction) = match output {
true => (&self.port_out_free, SPA_DIRECTION_OUTPUT),
false => (&self.port_in_free, SPA_DIRECTION_INPUT),
};
let port = Rc::new(PwClientNodePort {
node: self.clone(),
direction,
id: ids.borrow_mut().acquire(),
destroyed: Cell::new(false),
effective_format: Cell::new(Default::default()),
supported_formats: RefCell::new(None),
supported_metas: Cell::new(PwClientNodePortSupportedMetas::none()),
can_alloc_buffers: Cell::new(false),
buffers: RefCell::new(vec![]),
buffer_config: Cell::new(None),
io_buffers: Default::default(),
});
self.ports.set((direction, port.id), port.clone());
port
}
pub fn send_port_output_buffers(&self, port: &PwClientNodePort, buffers: &[DmaBuf]) {
self.con.send(self, PwClientNodeMethods::PortBuffers, |f| {
f.write_struct(|f| {
// direction
f.write_uint(port.direction.0);
// id
f.write_uint(port.id);
// mix_id
f.write_int(-1);
// n_buffers
f.write_uint(buffers.len() as _);
for buffer in buffers.deref() {
// n_datas
f.write_uint(buffer.planes.len() as _);
for plane in &buffer.planes {
// type
f.write_id(SPA_DATA_DmaBuf.0);
// fd
f.write_fd(&plane.fd);
// flags
f.write_uint(SPA_DATA_FLAG_READABLE.0);
// offset
f.write_uint(plane.offset);
// size
f.write_uint(plane.stride * buffer.height as u32);
}
}
});
});
}
pub fn send_port_update(&self, port: &PwClientNodePort) {
self.con.send(self, PwClientNodeMethods::PortUpdate, |f| {
f.write_struct(|f| {
// direction
f.write_uint(port.direction.0);
// id
f.write_uint(port.id);
// change flags
f.write_uint(PW_CLIENT_NODE_PORT_UPDATE_PARAMS | PW_CLIENT_NODE_PORT_UPDATE_INFO);
let sm = port.supported_metas.get();
let mut metas = vec![];
if sm.contains(SUPPORTED_META_HEADER) {
metas.push((SPA_META_Header, mem::size_of::<spa_meta_header>()));
}
if sm.contains(SUPPORTED_META_BUSY) {
metas.push((SPA_META_Busy, mem::size_of::<spa_meta_busy>()));
}
if sm.contains(SUPPORTED_META_VIDEO_CROP) {
metas.push((SPA_META_VideoCrop, mem::size_of::<spa_meta_region>()));
}
let sf = port.supported_formats.borrow_mut();
let bc = port.buffer_config.get();
let mut num_params = metas.len() as u32;
if sf.is_some() {
num_params += 1;
}
if bc.is_some() {
num_params += 1;
}
// num params
f.write_uint(num_params);
if let Some(sf) = sf.deref() {
f.write_object(PW_OBJECT_Format, SPA_PARAM_EnumFormat.0, |f| {
if let Some(mt) = sf.media_type {
f.write_property(SPA_FORMAT_mediaType.0, PwPropFlag::none(), |f| {
f.write_id(mt.0);
});
}
if let Some(mst) = sf.media_sub_type {
f.write_property(SPA_FORMAT_mediaSubtype.0, PwPropFlag::none(), |f| {
f.write_id(mst.0);
});
}
if sf.formats.len() > 0 {
f.write_property(SPA_FORMAT_VIDEO_format.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
f.write_id(sf.formats[0].pipewire.0);
for format in &sf.formats {
f.write_id(format.pipewire.0);
}
});
});
}
if sf.modifiers.len() > 0 {
f.write_property(
SPA_FORMAT_VIDEO_modifier.0,
PwPropFlag::none(),
|f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
f.write_ulong(sf.modifiers[0]);
for modifier in &sf.modifiers {
f.write_ulong(*modifier);
}
});
},
);
}
if let Some(vs) = sf.video_size {
f.write_property(SPA_FORMAT_VIDEO_size.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Enum, 0, |f| {
f.write_rectangle(vs.width, vs.height);
f.write_rectangle(vs.width, vs.height);
});
});
}
});
}
if let Some(bc) = &bc {
f.write_object(PW_OBJECT_ParamBuffers, SPA_PARAM_Buffers.0, |f| {
f.write_property(SPA_PARAM_BUFFERS_buffers.0, PwPropFlag::none(), |f| {
f.write_uint(bc.num_buffers as _);
});
f.write_property(SPA_PARAM_BUFFERS_blocks.0, PwPropFlag::none(), |f| {
f.write_uint(bc.planes as _);
});
// if let Some(size) = bc.size {
// f.write_property(SPA_PARAM_BUFFERS_size.0, PwPropFlag::none(), |f| {
// f.write_uint(size as _);
// });
// }
// if let Some(stride) = bc.stride {
// f.write_property(SPA_PARAM_BUFFERS_stride.0, PwPropFlag::none(), |f| {
// f.write_uint(stride as _);
// });
// }
// f.write_property(SPA_PARAM_BUFFERS_align.0, PwPropFlag::none(), |f| {
// f.write_uint(bc.align as _);
// });
f.write_property(SPA_PARAM_BUFFERS_dataType.0, PwPropFlag::none(), |f| {
f.write_choice(PW_CHOICE_Flags, 0, |f| {
f.write_uint(1 << bc.data_type.0);
});
});
});
}
for (key, size) in metas {
f.write_object(PW_OBJECT_ParamMeta, SPA_PARAM_Meta.0, |f| {
f.write_property(SPA_PARAM_META_type.0, PwPropFlag::none(), |f| {
f.write_id(key.0);
});
f.write_property(SPA_PARAM_META_size.0, PwPropFlag::none(), |f| {
f.write_uint(size as u32);
});
});
}
f.write_struct(|f| {
// change mask
f.write_ulong(
SPA_PORT_CHANGE_MASK_FLAGS
// | SPA_PORT_CHANGE_MASK_PROPS
| SPA_PORT_CHANGE_MASK_PARAMS
| SPA_PORT_CHANGE_MASK_RATE,
);
let mut flags = SPA_PORT_FLAG::none();
if port.can_alloc_buffers.get() {
flags = SPA_PORT_FLAG_CAN_ALLOC_BUFFERS;
}
// flags
f.write_ulong(flags.0);
// rate num
f.write_int(0);
// rate denom
f.write_int(1);
// num props
f.write_int(0);
let mut num_params = 1;
if sf.is_some() {
num_params += 1;
}
if bc.is_some() {
num_params += 1;
}
// num params
f.write_uint(num_params);
if sf.is_some() {
f.write_id(SPA_PARAM_EnumFormat.0);
f.write_uint(SPA_PARAM_INFO_READ.0);
}
if bc.is_some() {
f.write_id(SPA_PARAM_Buffers.0);
f.write_uint(SPA_PARAM_INFO_READ.0);
}
f.write_id(SPA_PARAM_Meta.0);
f.write_uint(SPA_PARAM_INFO_READ.0);
});
});
});
}
fn handle_set_param(&self, _p: PwParser<'_>) -> Result<(), PwClientNodeError> {
Ok(())
}
fn handle_set_io(&self, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s = p.read_struct()?;
let mut p2 = s.fields;
let id = PwIoType(p2.read_id()?);
let memid = p2.read_uint()?;
let offset = p2.read_uint()?;
let size = p2.read_uint()?;
log::debug!("set io {:?}", id);
if memid == !0 {
self.ios.remove(&id);
} else {
let map = match self.con.mem.map(memid, offset, size) {
Ok(m) => m,
Err(e) => {
log::error!("Could not map memory from the pool: {}", ErrorFmt(e));
return Ok(());
}
};
self.ios.set(id, map);
}
Ok(())
}
fn handle_event(&self, _p: PwParser<'_>) -> Result<(), PwClientNodeError> {
Ok(())
}
fn handle_command(self: &Rc<Self>, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s1 = p.read_struct()?;
let mut p1 = s1.fields;
let obj = p1.read_object()?;
match SpaNodeCommand(obj.id) {
SPA_NODE_COMMAND_Start => {
if let Some(owner) = self.owner.get() {
owner.start();
}
}
SPA_NODE_COMMAND_Pause => {
if let Some(owner) = self.owner.get() {
owner.pause();
}
}
SPA_NODE_COMMAND_Suspend => {
if let Some(owner) = self.owner.get() {
owner.suspend();
}
}
v => {
log::warn!("Unhandled node command {:?}", v);
}
}
Ok(())
}
fn handle_add_port(&self, _p: PwParser<'_>) -> Result<(), PwClientNodeError> {
Ok(())
}
fn handle_remove_port(&self, _p: PwParser<'_>) -> Result<(), PwClientNodeError> {
Ok(())
}
fn port_set_format(
&self,
port: &Rc<PwClientNodePort>,
obj: Option<PwPodObject<'_>>,
) -> Result<(), PwClientNodeError> {
let mut obj = match obj {
Some(obj) => obj,
_ => {
port.effective_format.take();
return Ok(());
}
};
let mut format = PwClientNodePortFormat::default();
if let Some(mt) = obj.get_param(SPA_FORMAT_mediaType.0)? {
format.media_type = Some(SpaMediaType(mt.pod.get_id()?));
}
if let Some(mt) = obj.get_param(SPA_FORMAT_mediaSubtype.0)? {
format.media_sub_type = Some(SpaMediaSubtype(mt.pod.get_id()?));
}
if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_size.0)? {
format.video_size = Some(mt.pod.get_rectangle()?);
}
if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_format.0)? {
if let Some(fmt) = pw_formats().get(&SpaVideoFormat(mt.pod.get_id()?)) {
format.format = Some(*fmt);
}
}
if let Some(mt) = obj.get_param(SPA_FORMAT_VIDEO_framerate.0)? {
format.framerate = Some(mt.pod.get_fraction()?);
}
port.effective_format.set(format);
Ok(())
}
fn handle_port_set_param(&self, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s1 = p.read_struct()?;
let mut p1 = s1.fields;
let direction = SpaDirection(p1.read_uint()?);
let port_id = p1.read_uint()?;
let id = SpaParamType(p1.read_id()?);
let _flags = p1.read_int()?;
let obj = p1.read_object_opt()?;
let port = self.get_port(direction, port_id)?;
match id {
SPA_PARAM_Format => {
self.port_set_format(&port, obj)?;
if let Some(owner) = self.owner.get() {
owner.port_format_changed(&port);
}
}
_ => {
log::warn!(
"port_set_param: Ignoring unexpected port parameter {:?}",
id
);
}
}
Ok(())
}
fn handle_port_use_buffers(&self, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s1 = p.read_struct()?;
let mut p1 = s1.fields;
let direction = SpaDirection(p1.read_uint()?);
let port_id = p1.read_uint()?;
let _mix_id = p1.read_int()?;
let buffer_flags = SpaNodeBuffersFlags(p1.read_uint()?);
let n_buffers = p1.read_uint()?;
let port = self.get_port(direction, port_id)?;
let mut res = vec![];
for _ in 0..n_buffers {
let mem_id = p1.read_uint()?;
let offset = p1.read_uint()?;
let size = p1.read_uint()?;
let n_metas = p1.read_uint()?;
let mut meta_header = Default::default();
let mut meta_video_crop = Default::default();
let mut meta_busy = Default::default();
let mut chunks = vec![];
let mut slices = vec![];
let mem = self.con.mem.map(mem_id, offset, size)?;
log::debug!(" mem_id={}, offset={}, size={}", mem_id, offset, size);
log::debug!(" n_metas={}", n_metas);
let mut offset = 0;
for _ in 0..n_metas {
let ty = SpaMetaType(p1.read_id()?);
let size = p1.read_uint()? as usize;
match ty {
SPA_META_Header => {
let header = mem.typed_at::<spa_meta_header>(offset);
meta_header = Some(header);
}
SPA_META_VideoCrop => {
let crop = mem.typed_at::<spa_meta_region>(offset);
meta_video_crop = Some(crop);
}
SPA_META_VideoDamage => {
let _video_damage = mem.typed_at::<spa_meta_region>(offset);
}
SPA_META_Bitmap => {
let _bitmap = mem.typed_at::<spa_meta_bitmap>(offset);
}
SPA_META_Cursor => {
let _cursor = mem.typed_at::<spa_meta_cursor>(offset);
}
SPA_META_Control => {}
SPA_META_Busy => {
let busy = mem.typed_at::<spa_meta_busy>(offset);
meta_busy = Some(busy);
}
_ => {}
}
offset += (size + 7) & !7;
}
let n_datas = p1.read_uint()?;
log::debug!(" offset = {}, n_datas={}", offset, n_datas);
for _ in 0..n_datas {
let ty = SpaDataType(p1.read_id()?);
let data_id = p1.read_uint()?;
let _flags = SpaDataFlags(p1.read_uint()?);
let mapoffset = p1.read_uint()?;
let maxsize = p1.read_uint()?;
chunks.push(mem.typed_at(offset));
if !buffer_flags.contains(SPA_NODE_BUFFERS_FLAG_ALLOC) {
if ty == SPA_DATA_MemPtr {
let offset = data_id as usize;
slices.push(mem.slice(offset..offset + maxsize as usize));
} else if ty == SPA_DATA_MemFd {
let mem = self.con.mem.map(data_id, mapoffset, maxsize)?;
slices.push(mem.slice(0..maxsize as usize));
}
}
}
res.push(Rc::new(PwClientNodeBuffer {
meta_header,
meta_busy,
meta_video_crop,
chunks,
slices,
}));
}
*port.buffers.borrow_mut() = res;
if let Some(owner) = self.owner.get() {
owner.use_buffers(&port);
}
Ok(())
}
fn handle_port_set_io(&self, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s = p.read_struct()?;
let mut p2 = s.fields;
let direction = SpaDirection(p2.read_uint()?);
let port_id = p2.read_uint()?;
let mix_id = p2.read_uint()?;
let id = SpaIoType(p2.read_id()?);
let mem_id = p2.read_uint()?;
let offset = p2.read_uint()?;
let size = p2.read_uint()?;
let port = self.get_port(direction, port_id)?;
match id {
SPA_IO_Buffers => {
if mem_id == !0 {
port.io_buffers.remove(&mix_id);
} else {
port.io_buffers.set(mix_id, self.con.mem.map(mem_id, offset, size)?.typed());
}
}
_ => {}
}
Ok(())
}
fn handle_transport(self: &Rc<Self>, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s = p.read_struct()?;
let mut p2 = s.fields;
let readfd = p2.read_fd()?;
let writefd = p2.read_fd()?;
let memid = p2.read_uint()?;
let offset = p2.read_uint()?;
let size = p2.read_uint()?;
let map = match self.con.mem.map(memid, offset, size) {
Ok(m) => m,
Err(e) => {
log::error!("Could not map memory from the pool: {}", ErrorFmt(e));
return Ok(());
}
};
let typed = map.typed::<pw_node_activation>();
self.activation.set(Some(typed.clone()));
self.transport_in.set(Some(
self.con.eng.spawn(self.clone().transport_in(typed, readfd)),
));
self.transport_out.set(Some(writefd));
Ok(())
}
fn handle_set_activation(
self: &Rc<Self>,
mut p: PwParser<'_>,
) -> Result<(), PwClientNodeError> {
let s = p.read_struct()?;
let mut p2 = s.fields;
let node = p2.read_uint()?;
let signalfd = p2.read_fd_opt()?;
if let Some(signalfd) = signalfd {
let memid = p2.read_uint()?;
let offset = p2.read_uint()?;
let size = p2.read_uint()?;
let map = match self.con.mem.map(memid, offset, size) {
Ok(m) => m,
Err(e) => {
log::error!("Could not map memory from the pool: {}", ErrorFmt(e));
return Ok(());
}
};
let typed = map.typed::<pw_node_activation>();
self.activations.set(
node,
Rc::new(PwNodeActivation {
activation: typed,
fd: signalfd,
}),
);
} else {
self.activations.remove(&node);
}
Ok(())
}
fn get_port(
&self,
direction: SpaDirection,
port_id: u32,
) -> Result<Rc<PwClientNodePort>, PwClientNodeError> {
match self.ports.get(&(direction, port_id)) {
Some(p) => Ok(p),
_ => Err(PwClientNodeError::UnknownPort(direction, port_id)),
}
}
fn handle_port_set_mix_info(&self, mut p: PwParser<'_>) -> Result<(), PwClientNodeError> {
let s1 = p.read_struct()?;
let mut p1 = s1.fields;
let direction = SpaDirection(p1.read_uint()?);
let port_id = p1.read_uint()?;
let mix_id = p1.read_int()?;
let peer_id = p1.read_int()?;
let dict = p1.read_dict_struct()?;
let _port = self.get_port(direction, port_id)?;
log::debug!(
"mix info: mix_id={}, peer_id={}, dict={:#?}",
mix_id,
peer_id,
dict
);
Ok(())
}
async fn transport_in(
self: Rc<Self>,
_activation: Rc<PwMemTyped<pw_node_activation>>,
fd: Rc<OwnedFd>,
) {
loop {
// unsafe {
// log::info!("transport = {:#?}", activation.read());
// }
if let Err(e) = self.con.ring.readable(&fd).await {
log::error!(
"Could not wait for transport to become readable: {}",
ErrorFmt(e)
);
return;
}
// log::info!("transport in");
// for port in self.ports.lock().values() {
// for io in port.io_buffers.lock().values() {
// unsafe {
// log::info!("status = {:?}", io.read().status);
// }
// }
// }
// unsafe {
// log::info!("state = {:#?}", activation.read().state[0]);
// }
let mut n = 0u64;
if let Err(e) = uapi::read(fd.raw(), &mut n) {
log::error!("Could not read from eventfd: {}", ErrorFmt(e));
return;
}
if n > 1 {
log::warn!("Missed {} transport changes", n - 1);
}
}
}
}
pw_object_base! {
PwClientNode, "client-node", PwClientNodeEvents;
Transport => handle_transport,
SetParam => handle_set_param,
SetIo => handle_set_io,
Event => handle_event,
Command => handle_command,
AddPort => handle_add_port,
RemovePort => handle_remove_port,
PortSetParam => handle_port_set_param,
PortUseBuffers => handle_port_use_buffers,
PortSetIo => handle_port_set_io,
SetActivation => handle_set_activation,
PortSetMixInfo => handle_port_set_mix_info,
}
impl PwObject for PwClientNode {
fn bound_id(&self, id: u32) {
if let Some(owner) = self.owner.get() {
owner.bound_id(id);
}
}
fn break_loops(&self) {
self.owner.take();
self.ports.clear();
self.transport_in.take();
self.transport_out.take();
}
}
#[derive(Debug, Error)]
pub enum PwClientNodeError {
#[error(transparent)]
PwParserError(#[from] PwParserError),
#[error(transparent)]
PwMemError(#[from] PwMemError),
#[error("Unknown port {0:?}@{1}")]
UnknownPort(SpaDirection, u32),
}

View file

@ -0,0 +1,184 @@
#![allow(non_upper_case_globals)]
use {
crate::{
pipewire::{
pw_con::PwCon,
pw_mem::{PwMem, PwMemType},
pw_object::{PwObject, PwObjectData},
pw_parser::{PwParser, PwParserError},
pw_pod::{SPA_DATA_DmaBuf, SPA_DATA_MemFd, SpaDataType},
},
utils::bitflags::BitflagsExt,
},
std::rc::Rc,
thiserror::Error,
};
pub struct PwCore {
pub data: PwObjectData,
pub con: Rc<PwCon>,
}
pw_opcodes! {
PwCoreMethods;
Hello = 1,
Sync = 2,
Pong = 3,
Error = 4,
GetRegistry = 5,
CreateObject = 6,
Destroy = 7,
}
pw_opcodes! {
PwCoreEvents;
Info = 0,
Done = 1,
Ping = 2,
Error = 3,
RemoveId = 4,
BoundId = 5,
AddMem = 6,
RemoveMem = 7,
}
pub const PW_CORE_VERSION: i32 = 3;
impl PwCore {
pub fn handle_info(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_int()?;
let cookie = p2.read_int()?;
let user_name = p2.read_string()?;
let host_name = p2.read_string()?;
let version_name = p2.read_string()?;
let name = p2.read_string()?;
let change_mask = p2.read_long()?;
let dict = p2.read_dict_struct()?;
log::info!("info: id={id}, cookie={cookie}, user_name={user_name}, host_name={host_name}, version_name={version_name}, name={name}, change_mask={change_mask}");
log::info!("dict: {:#?}", dict);
Ok(())
}
pub fn handle_done(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_uint()?;
let seq = p2.read_uint()?;
if let Some(obj) = self.con.objects.get(&id) {
if obj.data().sync_id.get() <= seq {
obj.data().sync_id.set(seq);
obj.done();
}
}
Ok(())
}
pub fn handle_ping(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_int()?;
let seq = p2.read_int()?;
self.con.send(self, PwCoreMethods::Pong, |f| {
f.write_struct(|f| {
f.write_int(id);
f.write_int(seq);
});
});
Ok(())
}
pub fn handle_error(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_int()?;
let seq = p2.read_int()?;
let res = p2.read_int()?;
let error = p2.read_string()?;
log::info!("error: id={id}, seq={seq}, res={res}, error={error}");
Ok(())
}
pub fn handle_remove_id(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_uint()?;
self.con.objects.remove(&id);
self.con.ids.borrow_mut().release(id);
Ok(())
}
pub fn handle_bound_id(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_uint()?;
let bound_id = p2.read_uint()?;
if let Some(obj) = self.con.objects.get(&id) {
obj.data().bound_id.set(Some(bound_id));
obj.bound_id(bound_id);
}
Ok(())
}
pub fn handle_add_mem(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_uint()?;
let ty = SpaDataType(p2.read_id()?);
let fd = p2.read_fd()?;
let flags = p2.read_int()?;
let read = flags.contains(1);
let write = flags.contains(2);
let ty = match ty {
SPA_DATA_MemFd => PwMemType::MemFd,
SPA_DATA_DmaBuf => PwMemType::DmaBuf,
_ => {
log::error!("Ignoring unknown mem type {:?}", ty);
return Ok(());
}
};
self.con.mem.mems.set(
id,
Rc::new(PwMem {
ty,
read,
write,
fd,
}),
);
Ok(())
}
pub fn handle_remove_mem(&self, mut p1: PwParser<'_>) -> Result<(), PwCoreError> {
let s1 = p1.read_struct()?;
let mut p2 = s1.fields;
let id = p2.read_uint()?;
self.con.mem.mems.remove(&id);
Ok(())
}
}
pw_object_base! {
PwCore, "core", PwCoreEvents;
Info => handle_info,
Done => handle_done,
Ping => handle_ping,
Error => handle_error,
RemoveId => handle_remove_id,
BoundId => handle_bound_id,
AddMem => handle_add_mem,
RemoveMem => handle_remove_mem,
}
impl PwObject for PwCore {}
#[derive(Debug, Error)]
pub enum PwCoreError {
#[error(transparent)]
PwParserError(#[from] PwParserError),
}

View file

@ -0,0 +1,48 @@
use {
crate::pipewire::{
pw_con::PwCon,
pw_object::{PwObject, PwObjectData},
pw_parser::{PwParser, PwParserError},
},
std::rc::Rc,
thiserror::Error,
};
pub const PW_REGISTRY_VERSION: i32 = 3;
pw_opcodes! {
PwRegistryEvents;
Global = 0,
GlobalRemove = 1,
}
pub struct PwRegistry {
pub data: PwObjectData,
pub con: Rc<PwCon>,
}
impl PwRegistry {
fn handle_global(&self, _p: PwParser<'_>) -> Result<(), PwRegistryError> {
Ok(())
}
fn handle_global_remove(&self, _p: PwParser<'_>) -> Result<(), PwRegistryError> {
Ok(())
}
}
pw_object_base! {
PwRegistry, "registry", PwRegistryEvents;
Global => handle_global,
GlobalRemove => handle_global_remove,
}
impl PwObject for PwRegistry {}
#[derive(Debug, Error)]
pub enum PwRegistryError {
#[error(transparent)]
PwParserError(#[from] PwParserError),
}

152
src/pipewire/pw_mem.rs Normal file
View file

@ -0,0 +1,152 @@
use {
crate::utils::{
copyhashmap::CopyHashMap,
mmap::{mmap, Mmapped},
oserror::OsError,
page_size::page_size,
ptr_ext::{MutPtrExt, PtrExt},
},
std::{marker::PhantomData, mem, ops::Range, rc::Rc},
thiserror::Error,
uapi::{c, OwnedFd, Pod},
};
#[derive(Default)]
pub struct PwMemPool {
pub mems: CopyHashMap<u32, Rc<PwMem>>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PwMemType {
MemFd,
DmaBuf,
}
pub struct PwMem {
pub ty: PwMemType,
pub read: bool,
pub write: bool,
pub fd: Rc<OwnedFd>,
}
pub struct PwMemMap {
pub mem: Rc<PwMem>,
pub range: Range<usize>,
pub map: Mmapped,
}
pub struct PwMemTyped<T> {
mem: Rc<PwMemMap>,
offset: usize,
_phantom: PhantomData<T>,
}
pub struct PwMemSlice {
mem: Rc<PwMemMap>,
range: Range<usize>,
}
impl PwMemPool {
pub fn map(&self, memid: u32, offset: u32, size: u32) -> Result<Rc<PwMemMap>, PwMemError> {
match self.mems.get(&memid) {
Some(m) => m.map(offset, size),
_ => Err(PwMemError::MemidDoesNotExist(memid)),
}
}
}
impl PwMem {
pub fn map(self: &Rc<Self>, offset: u32, size: u32) -> Result<Rc<PwMemMap>, PwMemError> {
let mask = page_size() - 1;
let offset = offset as usize;
let size = size as usize;
let start = offset & !mask;
let dist = offset - start;
let len = (size + dist + mask) & !mask;
let range = dist..dist + size;
let mut prot = 0;
if self.read {
prot |= c::PROT_READ;
}
if self.write {
prot |= c::PROT_WRITE;
}
let map = match mmap(len as _, prot, c::MAP_SHARED, self.fd.raw(), start as _) {
Ok(m) => m,
Err(e) => return Err(PwMemError::MmapFailed(e)),
};
Ok(Rc::new(PwMemMap {
mem: self.clone(),
range,
map,
}))
}
}
impl PwMemMap {
#[allow(dead_code)]
pub unsafe fn read<T: Pod>(&self) -> &T {
self.check::<T>(0);
(self.map.ptr.cast::<u8>().add(self.range.start) as *const T).deref()
}
#[allow(dead_code)]
pub unsafe fn write<T: Pod>(&self) -> &mut T {
self.check::<T>(0);
(self.map.ptr.cast::<u8>().add(self.range.start) as *mut T).deref_mut()
}
#[allow(dead_code)]
pub unsafe fn bytes_mut(&self) -> &mut [u8] {
std::slice::from_raw_parts_mut(
self.map.ptr.cast::<u8>().add(self.range.start) as _,
self.range.len(),
)
}
fn check<T>(&self, offset: usize) {
assert!(offset <= self.range.len());
assert!(mem::size_of::<T>() <= self.range.len() - offset);
assert_eq!((mem::align_of::<T>() - 1) & (self.range.start + offset), 0);
}
pub fn typed<T: Pod>(self: &Rc<Self>) -> Rc<PwMemTyped<T>> {
self.typed_at(0)
}
pub fn typed_at<T: Pod>(self: &Rc<Self>, offset: usize) -> Rc<PwMemTyped<T>> {
self.check::<T>(offset);
Rc::new(PwMemTyped {
mem: self.clone(),
offset: self.range.start + offset,
_phantom: Default::default(),
})
}
pub fn slice(self: &Rc<Self>, range: Range<usize>) -> Rc<PwMemSlice> {
assert!(range.start <= self.range.len());
assert!(range.len() <= self.range.len() - range.start);
Rc::new(PwMemSlice {
mem: self.clone(),
range: self.range.start + range.start..self.range.start + range.end,
})
}
}
impl<T: Pod> PwMemTyped<T> {
pub unsafe fn read(&self) -> &T {
(self.mem.map.ptr.cast::<u8>().add(self.offset) as *const T).deref()
}
pub unsafe fn write(&self) -> &mut T {
(self.mem.map.ptr.cast::<u8>().add(self.offset) as *mut T).deref_mut()
}
}
#[derive(Debug, Error)]
pub enum PwMemError {
#[error("mmap failed")]
MmapFailed(#[source] OsError),
#[error("memid {0} does not exist")]
MemidDoesNotExist(u32),
}

52
src/pipewire/pw_object.rs Normal file
View file

@ -0,0 +1,52 @@
use {
crate::{pipewire::pw_parser::PwParser, utils::numcell::NumCell},
std::{cell::Cell, fmt::Debug, rc::Rc},
thiserror::Error,
};
pub trait PwObjectBase {
fn data(&self) -> &PwObjectData;
fn interface(&self) -> &str;
fn handle_msg(self: Rc<Self>, opcode: u8, parser: PwParser<'_>) -> Result<(), PwObjectError>;
fn event_name(&self, opcode: u8) -> Option<&'static str>;
}
pub trait PwObject: PwObjectBase {
fn bound_id(&self, id: u32) {
let _ = id;
}
fn done(&self) {}
fn break_loops(&self) {}
}
pub struct PwObjectData {
pub id: u32,
pub bound_id: Cell<Option<u32>>,
pub sync_id: NumCell<u32>,
}
#[derive(Debug, Error)]
#[error("An error occurred in a `{interface}`")]
pub struct PwObjectError {
pub interface: &'static str,
#[source]
pub source: PwObjectErrorType,
}
#[derive(Debug, Error)]
pub enum PwObjectErrorType {
#[error("Unknown event {0}")]
UnknownEvent(u8),
#[error("An error occurred in event `{method}`")]
EventError {
method: &'static str,
#[source]
source: Box<dyn std::error::Error>,
},
}
pub trait PwOpcode: Debug {
fn id(&self) -> u8;
}

315
src/pipewire/pw_parser.rs Normal file
View file

@ -0,0 +1,315 @@
#![allow(non_upper_case_globals)]
use {
crate::pipewire::pw_pod::{
PW_CHOICE_None, PW_TYPE_Array, PW_TYPE_Bitmap, PW_TYPE_Bool, PW_TYPE_Bytes, PW_TYPE_Choice,
PW_TYPE_Double, PW_TYPE_Fd, PW_TYPE_Float, PW_TYPE_Fraction, PW_TYPE_Id, PW_TYPE_Int,
PW_TYPE_Long, PW_TYPE_None, PW_TYPE_Object, PW_TYPE_Pod, PW_TYPE_Pointer,
PW_TYPE_Rectangle, PW_TYPE_Sequence, PW_TYPE_String, PW_TYPE_Struct, PwChoiceType,
PwControlType, PwPod, PwPodArray, PwPodChoice, PwPodControl, PwPodFraction, PwPodObject,
PwPodObjectType, PwPodPointer, PwPodRectangle, PwPodSequence, PwPodStruct, PwPodType,
PwPointerType, PwProp, PwPropFlag,
},
ahash::AHashMap,
bstr::{BStr, BString, ByteSlice},
std::{
fmt::Debug,
mem::{self, MaybeUninit},
rc::Rc,
},
thiserror::Error,
uapi::{OwnedFd, Pod},
};
#[derive(Debug, Error)]
pub enum PwParserError {
#[error("Unexpected EOF")]
UnexpectedEof,
#[error("Message references an FD that is out of bounds")]
MissingFd,
#[error("Array element type has size of 0")]
ZeroSizedArrayElementType,
#[error("Unknown POD type: {0:?}")]
UnknownType(PwPodType),
#[error("Unexpected POD type: Expected {0:?}, got {1:?}")]
UnexpectedPodType(PwPodType, PwPodType),
}
#[derive(Copy, Clone)]
pub struct PwParser<'a> {
data: &'a [u8],
fds: &'a [Rc<OwnedFd>],
pos: usize,
}
impl<'a> PwParser<'a> {
pub fn new(data: &'a [u8], fds: &'a [Rc<OwnedFd>]) -> Self {
Self { data, fds, pos: 0 }
}
pub fn reset(&mut self) {
self.pos = 0;
}
fn read_raw<T: Pod>(&mut self, offset: usize) -> Result<T, PwParserError> {
if self.pos + offset + mem::size_of::<T>() <= self.data.len() {
unsafe {
let mut res = MaybeUninit::uninit();
let src = self.data[self.pos + offset..].as_ptr();
std::ptr::copy_nonoverlapping(src, res.as_mut_ptr() as _, mem::size_of::<T>());
Ok(res.assume_init())
}
} else {
Err(PwParserError::UnexpectedEof)
}
}
pub fn len(&self) -> usize {
self.data.len() - self.pos
}
pub fn pos(&self) -> usize {
self.pos
}
fn read_array(&mut self, offset: usize, len: usize) -> Result<PwPodArray<'a>, PwParserError> {
let child_len = self.read_raw::<u32>(offset)? as usize;
if child_len == 0 {
return Err(PwParserError::ZeroSizedArrayElementType);
}
let ty = PwPodType(self.read_raw(offset + 4)?);
Ok(PwPodArray {
ty,
child_len,
n_elements: (len - 8) / child_len,
elements: PwParser::new(
&self.data[self.pos + offset + 8..self.pos + offset + len],
self.fds,
),
})
}
pub fn read_dict_struct(&mut self) -> Result<AHashMap<BString, BString>, PwParserError> {
let s2 = self.read_struct()?;
let mut p3 = s2.fields;
let num_dict_entries = p3.read_int()?;
let mut de = AHashMap::new();
for _ in 0..num_dict_entries {
de.insert(p3.read_string()?.to_owned(), p3.read_string()?.to_owned());
}
Ok(de)
}
pub fn read_struct(&mut self) -> Result<PwPodStruct<'a>, PwParserError> {
match self.read_pod()? {
PwPod::Struct(s) => Ok(s),
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_Struct, v.ty())),
}
}
pub fn read_uint(&mut self) -> Result<u32, PwParserError> {
self.read_int().map(|v| v as u32)
}
pub fn read_int(&mut self) -> Result<i32, PwParserError> {
match self.read_value()? {
PwPod::Int(s) => Ok(s),
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_Int, v.ty())),
}
}
pub fn read_object_opt(&mut self) -> Result<Option<PwPodObject>, PwParserError> {
match self.read_pod()? {
PwPod::Object(p) => Ok(Some(p)),
PwPod::None => Ok(None),
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_Object, v.ty())),
}
}
pub fn read_object(&mut self) -> Result<PwPodObject, PwParserError> {
match self.read_object_opt()? {
Some(p) => Ok(p),
_ => Err(PwParserError::UnexpectedPodType(
PW_TYPE_Object,
PW_TYPE_None,
)),
}
}
pub fn read_id(&mut self) -> Result<u32, PwParserError> {
match self.read_value()? {
PwPod::Id(s) => Ok(s),
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_Id, v.ty())),
}
}
pub fn read_fd_opt(&mut self) -> Result<Option<Rc<OwnedFd>>, PwParserError> {
match self.read_pod()? {
PwPod::Fd(idx) if idx == !0 => Ok(None),
PwPod::Fd(idx) => match self.fds.get(idx as usize) {
Some(fd) => Ok(Some(fd.clone())),
_ => Err(PwParserError::MissingFd),
},
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_Id, v.ty())),
}
}
pub fn read_fd(&mut self) -> Result<Rc<OwnedFd>, PwParserError> {
match self.read_fd_opt()? {
Some(fd) => Ok(fd),
_ => Err(PwParserError::MissingFd),
}
}
pub fn read_ulong(&mut self) -> Result<u64, PwParserError> {
self.read_long().map(|l| l as _)
}
pub fn read_long(&mut self) -> Result<i64, PwParserError> {
match self.read_value()? {
PwPod::Long(s) => Ok(s),
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_Long, v.ty())),
}
}
pub fn read_string(&mut self) -> Result<&'a BStr, PwParserError> {
match self.read_value()? {
PwPod::String(s) => Ok(s),
v => Err(PwParserError::UnexpectedPodType(PW_TYPE_String, v.ty())),
}
}
pub fn read_value(&mut self) -> Result<PwPod<'a>, PwParserError> {
let mut v = self.read_pod();
if let Ok(PwPod::Choice(v)) = &mut v {
if v.ty == PW_CHOICE_None && v.elements.n_elements > 0 {
return v
.elements
.elements
.read_pod_body_packed(v.elements.ty, v.elements.child_len);
}
}
v
}
pub fn read_pod(&mut self) -> Result<PwPod<'a>, PwParserError> {
let len = self.read_raw::<u32>(0)? as usize;
let ty = PwPodType(self.read_raw(4)?);
self.pos += 8;
self.read_pod_body(ty, len)
}
pub fn read_pod_body_packed(
&mut self,
ty: PwPodType,
len: usize,
) -> Result<PwPod<'a>, PwParserError> {
self.read_pod_body2(ty, len, true)
}
pub fn read_pod_body(&mut self, ty: PwPodType, len: usize) -> Result<PwPod<'a>, PwParserError> {
self.read_pod_body2(ty, len, false)
}
fn read_pod_body2(
&mut self,
ty: PwPodType,
len: usize,
packed: bool,
) -> Result<PwPod<'a>, PwParserError> {
let size = if packed { len } else { (len + 7) & !7 };
if self.len() < size as usize {
return Err(PwParserError::UnexpectedEof);
}
let val = match ty {
PW_TYPE_None => PwPod::None,
PW_TYPE_Bool => PwPod::Bool(self.read_raw::<i32>(0)? != 0),
PW_TYPE_Id => PwPod::Id(self.read_raw(0)?),
PW_TYPE_Int => PwPod::Int(self.read_raw(0)?),
PW_TYPE_Long => PwPod::Long(self.read_raw(0)?),
PW_TYPE_Float => PwPod::Float(self.read_raw(0)?),
PW_TYPE_Double => PwPod::Double(self.read_raw(0)?),
PW_TYPE_String => {
let s = if len == 0 {
&[][..]
} else {
&self.data[self.pos..self.pos + len - 1]
};
PwPod::String(s.as_bstr())
}
PW_TYPE_Bytes => PwPod::Bytes(&self.data[self.pos..self.pos + len]),
PW_TYPE_Rectangle => PwPod::Rectangle(PwPodRectangle {
width: self.read_raw(0)?,
height: self.read_raw(4)?,
}),
PW_TYPE_Fraction => PwPod::Fraction(PwPodFraction {
num: self.read_raw(0)?,
denom: self.read_raw(4)?,
}),
PW_TYPE_Bitmap => PwPod::Bitmap(&self.data[self.pos..self.pos + len]),
PW_TYPE_Array => PwPod::Array(self.read_array(0, len)?),
PW_TYPE_Struct => PwPod::Struct(PwPodStruct {
fields: PwParser::new(&self.data[self.pos..self.pos + len], self.fds),
}),
PW_TYPE_Object => PwPod::Object(PwPodObject {
ty: PwPodObjectType(self.read_raw(0)?),
id: self.read_raw(4)?,
probs: PwParser::new(&self.data[self.pos + 8..self.pos + len], self.fds),
}),
PW_TYPE_Sequence => PwPod::Sequence(PwPodSequence {
unit: self.read_raw(0)?,
controls: PwParser::new(&self.data[self.pos + 8..self.pos + len], self.fds),
}),
PW_TYPE_Pointer => PwPod::Pointer(PwPodPointer {
ty: PwPointerType(self.read_raw(0)?),
value: self.read_raw(8)?,
}),
PW_TYPE_Fd => PwPod::Fd(self.read_raw(0)?),
PW_TYPE_Choice => PwPod::Choice(PwPodChoice {
ty: PwChoiceType(self.read_raw(0)?),
flags: self.read_raw(4)?,
elements: self.read_array(8, len - 8)?,
}),
PW_TYPE_Pod => {
let pos = self.pos;
let pod = self.read_pod()?;
self.pos = pos;
pod
}
_ => return Err(PwParserError::UnknownType(ty)),
};
self.pos += size as usize;
Ok(val)
}
pub fn read_prop(&mut self) -> Result<PwProp<'a>, PwParserError> {
let key = self.read_raw(0)?;
let flags = PwPropFlag(self.read_raw(4)?);
self.pos += 8;
Ok(PwProp {
key,
flags,
pod: self.read_pod()?,
})
}
pub fn read_control(&mut self) -> Result<PwPodControl<'a>, PwParserError> {
let offset = self.read_raw(0)?;
let ty = PwControlType(self.read_raw(4)?);
self.pos += 8;
Ok(PwPodControl {
offset,
ty,
value: self.read_pod()?,
})
}
pub fn skip(&mut self) -> Result<(), PwParserError> {
let size = self.read_raw::<u32>(0)? as usize;
if self.len() < size + 8 {
return Err(PwParserError::UnexpectedEof);
}
self.pos += size + 8;
Ok(())
}
}

1451
src/pipewire/pw_pod.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,461 @@
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::{debug_fn::debug_fn, errorfmt::ErrorFmt},
},
std::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",
&debug_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",
&debug_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(&debug_fn(|fmt| f(fmt, p)));
true
}
Err(e) => {
let e = ErrorFmt(e);
l.entry(&debug_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(&debug_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 {
if let Some(n) = d.id_name(self.id) {
name = n;
id = &name;
}
}
s.field("id", id);
s.field(
"props",
&debug_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(&debug_fn(|fmt| d.debug_property(fmt, p))),
_ => l.entry(&p),
},
Err(e) => {
let e = ErrorFmt(e);
l.entry(&debug_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",
&debug_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(&debug_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,
&debug_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(&debug_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),
}
}
}

View file

@ -20,6 +20,7 @@ pub mod numcell;
pub mod once;
pub mod option_ext;
pub mod oserror;
pub mod page_size;
pub mod ptr_ext;
pub mod queue;
pub mod rc_eq;

7
src/utils/page_size.rs Normal file
View file

@ -0,0 +1,7 @@
use {once_cell::sync::Lazy, uapi::c};
static PAGE_SIZE: Lazy<usize> = Lazy::new(|| uapi::sysconf(c::_SC_PAGESIZE).unwrap_or(4096) as _);
pub fn page_size() -> usize {
*PAGE_SIZE
}