Skip to content

Commit 9f60cc6

Browse files
committed
stateful negotiation & pipewire audio capture cleanup
1 parent 7b04437 commit 9f60cc6

4 files changed

Lines changed: 464 additions & 198 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use pipewire::spa::{
2+
param::{ParamType, audio::AudioFormat},
3+
pod::{ChoiceValue, Object, Property, Value},
4+
sys,
5+
utils::{self, Choice, ChoiceEnum, ChoiceFlags, Id, SpaTypes},
6+
};
7+
use std::ops::RangeBounds;
8+
9+
#[derive(Debug, Default)]
10+
pub struct AudioCaps {
11+
pub(super) format: Option<Property>,
12+
pub(super) rate: Option<Property>,
13+
pub(super) channels: Option<Property>,
14+
}
15+
16+
unsafe impl Send for AudioCaps {}
17+
18+
impl AudioCaps {
19+
pub fn new() -> AudioCaps {
20+
AudioCaps::default()
21+
}
22+
23+
pub fn format(mut self, format: AudioFormat) -> Self {
24+
self.format = Some(Property::new(
25+
sys::SPA_FORMAT_AUDIO_format,
26+
Value::Id(utils::Id(format.as_raw())),
27+
));
28+
self
29+
}
30+
31+
pub fn format_choice(mut self, formats: impl IntoIterator<Item = AudioFormat>) -> Self {
32+
let mut formats = formats.into_iter();
33+
34+
self.format = Some(Property::new(
35+
sys::SPA_FORMAT_AUDIO_format,
36+
Value::Choice(ChoiceValue::Id(Choice(
37+
ChoiceFlags::empty(),
38+
ChoiceEnum::Enum {
39+
default: Id(formats
40+
.next()
41+
.expect("must not pass empty iterator")
42+
.as_raw()),
43+
alternatives: formats.map(|f| Id(f.as_raw())).collect(),
44+
},
45+
))),
46+
));
47+
48+
self
49+
}
50+
51+
pub fn rate(mut self, rate: u32) -> Self {
52+
self.rate = Some(Property::new(
53+
sys::SPA_FORMAT_AUDIO_rate,
54+
Value::Int(rate as i32),
55+
));
56+
57+
self
58+
}
59+
60+
pub fn rate_choice(mut self, rates: impl IntoIterator<Item = u32>) -> Self {
61+
let mut rates = rates.into_iter();
62+
63+
self.rate = Some(Property::new(
64+
sys::SPA_FORMAT_AUDIO_rate,
65+
Value::Choice(ChoiceValue::Int(Choice(
66+
ChoiceFlags::empty(),
67+
ChoiceEnum::Enum {
68+
default: rates.next().expect("must not pass empty iterator") as i32,
69+
alternatives: rates.map(|rate| rate as i32).collect(),
70+
},
71+
))),
72+
));
73+
74+
self
75+
}
76+
77+
pub fn channels(mut self, channels: u32) -> Self {
78+
self.channels = Some(Property::new(
79+
sys::SPA_FORMAT_AUDIO_channels,
80+
Value::Int(channels as i32),
81+
));
82+
83+
self
84+
}
85+
86+
pub fn channels_choice(mut self, channels: impl IntoIterator<Item = u32>) -> Self {
87+
let mut channels = channels.into_iter();
88+
89+
self.channels = Some(Property::new(
90+
sys::SPA_FORMAT_AUDIO_channels,
91+
Value::Choice(ChoiceValue::Int(Choice(
92+
ChoiceFlags::empty(),
93+
ChoiceEnum::Enum {
94+
default: channels.next().expect("must not pass empty iterator") as i32,
95+
alternatives: channels.map(|channels| channels as i32).collect(),
96+
},
97+
))),
98+
));
99+
100+
self
101+
}
102+
103+
pub fn channels_range(mut self, range: impl RangeBounds<u32>) -> Self {
104+
let start = match range.start_bound() {
105+
std::ops::Bound::Included(v) => *v,
106+
std::ops::Bound::Excluded(v) => v.saturating_sub(1),
107+
std::ops::Bound::Unbounded => 1,
108+
}
109+
.max(1);
110+
111+
let end = match range.end_bound() {
112+
std::ops::Bound::Included(v) => *v,
113+
std::ops::Bound::Excluded(v) => v.saturating_sub(1),
114+
std::ops::Bound::Unbounded => 1,
115+
}
116+
.max(start);
117+
118+
self.channels = Some(Property::new(
119+
sys::SPA_FORMAT_AUDIO_channels,
120+
Value::Choice(ChoiceValue::Int(Choice(
121+
ChoiceFlags::empty(),
122+
ChoiceEnum::Range {
123+
default: 0,
124+
min: start as i32,
125+
max: end as i32,
126+
},
127+
))),
128+
));
129+
130+
self
131+
}
132+
133+
pub(super) fn into_object(self) -> Object {
134+
let Self {
135+
format,
136+
rate,
137+
channels,
138+
} = self;
139+
140+
let mut properties = vec![];
141+
142+
properties.push(Property::new(
143+
sys::SPA_FORMAT_mediaType,
144+
Value::Id(utils::Id(sys::SPA_MEDIA_TYPE_audio)),
145+
));
146+
properties.push(Property::new(
147+
sys::SPA_FORMAT_mediaSubtype,
148+
Value::Id(utils::Id(sys::SPA_MEDIA_SUBTYPE_raw)),
149+
));
150+
151+
if let Some(format) = format {
152+
properties.push(format);
153+
}
154+
155+
if let Some(rate) = rate {
156+
properties.push(rate);
157+
}
158+
159+
if let Some(channels) = channels {
160+
properties.push(channels);
161+
}
162+
163+
Object {
164+
type_: SpaTypes::ObjectParamFormat.as_raw(),
165+
id: ParamType::EnumFormat.as_raw(),
166+
properties,
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)