pipewire: add pipewire client
This commit is contained in:
parent
2512470231
commit
2568b7b1f5
19 changed files with 4573 additions and 2 deletions
|
|
@ -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', ' ', ' '),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
178
src/macros.rs
178
src/macros.rs
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
9
src/pipewire.rs
Normal 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
451
src/pipewire/pw_con.rs
Normal 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;
|
||||
304
src/pipewire/pw_formatter.rs
Normal file
304
src/pipewire/pw_formatter.rs
Normal 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
4
src/pipewire/pw_ifs.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod pw_client;
|
||||
pub mod pw_client_node;
|
||||
pub mod pw_core;
|
||||
pub mod pw_registry;
|
||||
61
src/pipewire/pw_ifs/pw_client.rs
Normal file
61
src/pipewire/pw_ifs/pw_client.rs
Normal 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),
|
||||
}
|
||||
874
src/pipewire/pw_ifs/pw_client_node.rs
Normal file
874
src/pipewire/pw_ifs/pw_client_node.rs
Normal 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),
|
||||
}
|
||||
184
src/pipewire/pw_ifs/pw_core.rs
Normal file
184
src/pipewire/pw_ifs/pw_core.rs
Normal 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),
|
||||
}
|
||||
48
src/pipewire/pw_ifs/pw_registry.rs
Normal file
48
src/pipewire/pw_ifs/pw_registry.rs
Normal 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
152
src/pipewire/pw_mem.rs
Normal 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
52
src/pipewire/pw_object.rs
Normal 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
315
src/pipewire/pw_parser.rs
Normal 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
1451
src/pipewire/pw_pod.rs
Normal file
File diff suppressed because it is too large
Load diff
461
src/pipewire/pw_pod/pw_debug.rs
Normal file
461
src/pipewire/pw_pod/pw_debug.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
7
src/utils/page_size.rs
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue