Source
A Source is one capture endpoint as a Kubernetes resource: an
analog input, or the capture side of a USB card. The operator
creates one for every capture endpoint it publishes, cluster-scoped
like a Node, named the same way a Sink is. You never create or
delete one. The operator writes the whole of status, and you write
spec, which states the settings you want for the endpoint.
apiVersion: audio.liken.sh/v1alpha1
kind: Source
metadata:
name: node-1-usb-0573-1573-a34004801402-usb-audio-capture
spec:
mute: true
status:
node: node-1
location: "1-6"
connectionType: usb
pcm:
device: 0
id: USB Audio
nodeName: liken.audio.card1-pcm0c
capabilities:
Mic Capture Volume:
type: integer
min: 0
max: 16
channels: 1
Mic Capture Switch:
type: boolean
channels: 1
observed:
volume: 100
mute: true
controls:
Mic Capture Volume: "8"
Mic Capture Switch: "on"
conditions:
- type: Connected
status: "True"
- type: Ready
status: "True"
For a microphone, mute is the field that matters most. Setting
spec.mute: true on a Source closes that microphone for everyone,
a rule that closes every microphone is one patch per Source, and
kubectl get sources shows which ones are open. A pod that records
still holds the microphone through a claim, and its stream reaches
the node that status.nodeName names.
An HDA card serves several input jacks through one capture PCM and
picks the live jack with its Input Source control, so the Source
is the PCM and the jack is a control in spec.controls. A Bluetooth
headset’s microphone is not published yet, because it needs the
headset profiles the pod does not run.
One capture endpoint: an analog input, or a USB card’s capture side. A Bluetooth headset’s microphone is not published yet. An HDA card serves several input jacks through one capture PCM and picks the live jack with its Input Source control, so the Source is the PCM and the jack is a control. The name is the machine’s name and the card’s identity, as the Sink reference gives them. When a USB card’s name would pass 63 characters, it holds the first eight hex digits of the SHA-256 of the serial in place of the serial.
spec
The settings you want for the endpoint. Every field is optional. The operator writes volume and mute when they change and when the endpoint appears, and otherwise reports the level the endpoint holds. It writes a declared control back when the endpoint diverges from it. It never writes a field the spec leaves out.
| Field | Type | Required | Description |
|---|---|---|---|
volume |
integer | no | The gain PipeWire applies to what the endpoint captures, as a percent of unity, applied to every channel alike. It applies at once, under a claim or not. |
mute |
boolean | no | Whether the endpoint captures silence. It applies at once. A closed microphone is this field, on every Source, and kubectl get sources shows which ones are open. |
controls |
map[string]string | no | The card’s own controls, keyed by the kernel’s control name as status.capabilities lists them, such as Capture Volume or Input Source. An integer control takes a number within its range, a boolean control takes on or off, and an enumerated control takes one of its values. The operator writes a control only when it is stated here. |
status
What the hardware declares and what the operator last read. The operator owns every field here.
| Field | Type | Required | Description |
|---|---|---|---|
node |
string | no | The machine that holds the endpoint. The name of every Source starts with this machine’s name, so a card that moves to another machine gets a new Source. The operator on each machine lists and watches the Sources by this field. |
location |
string | no | Where the card is on the machine, in the kernel’s spelling: a PCI address such as 0000:00:1f.3, or a USB port path such as 1-6. |
connectionType |
string | no | How sound enters the machine. One of: analog, usb. |
card |
object | no | The ALSA card the endpoint is on. The number and the id are this boot’s, and a second card can change both, so nothing durable is keyed to them. |
pcm |
object | no | The PCM device the endpoint captures through. |
nodeName |
string | no | The PipeWire node a consumer’s capture streams target, the same value a prepared claim delivers as PIPEWIRE_NODE. Absent while PipeWire holds no node for the endpoint. |
format |
object | no | The format the node runs at, from PipeWire’s own Format parameter. Absent while the node is not running. |
capabilities |
map[string]object | no | The card’s own controls that belong to this endpoint, keyed by the kernel’s control name. A Capture control goes to the card’s sources, and a jack control is not listed because it is read-only and feeds the Connected condition. |
observed |
object | no | The last value the operator read for each setting. The operator reads the card’s control device on every event it delivers, and PipeWire’s graph on every change it prints, so a change a person made with a knob or a client shows here without a poll. |
claim |
object | no | The claim that holds the endpoint now, and absent between holders. |
conditions |
[]object | no | Connected reports that the endpoint can capture now: a plug in an analog jack, and always on USB. Ready reports that PipeWire holds a node for it. |
status.card
The ALSA card the endpoint is on. The number and the id are this boot’s, and a second card can change both, so nothing durable is keyed to them.
| Field | Type | Required | Description |
|---|---|---|---|
number |
integer | no | The card number the kernel assigned this boot. |
id |
string | no | The card’s short id, such as PCH, with the suffix the kernel appends on a clash, such as PCH_1. |
driver |
string | no | The kernel driver that binds the card, such as USB-Audio. |
name |
string | no | The card’s name, as the driver states it, such as HDA Intel PCH. |
status.pcm
The PCM device the endpoint captures through.
| Field | Type | Required | Description |
|---|---|---|---|
device |
integer | no | The PCM device number on the card, this boot. |
id |
string | no | The driver’s name for the PCM, such as USB Audio. It is the part of the endpoint’s name that outlives the number. |
status.format
The format the node runs at, from PipeWire’s own Format parameter. Absent while the node is not running.
| Field | Type | Required | Description |
|---|---|---|---|
rate |
integer | no | The sample rate in hertz. |
channels |
integer | no | The channel count. |
positions |
[]string | no | The channel positions in order, in PipeWire’s names: FL, FR, MONO. |
status.capabilities.*
The card’s own controls that belong to this endpoint, keyed by the kernel’s control name. A Capture control goes to the card’s sources, and a jack control is not listed because it is read-only and feeds the Connected condition.
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | no | What the control takes. One of: integer, boolean, enumerated. |
min |
integer | no | The smallest number an integer control accepts. |
max |
integer | no | The largest number an integer control accepts. |
step |
integer | no | The step between numbers an integer control accepts, absent when every number in the range is accepted. |
minDecibels |
string | no | The level at min, in decibels, when the control declares one, as a decimal string such as -65.25. |
maxDecibels |
string | no | The level at max, in decibels, when the control declares one. |
values |
[]string | no | Every value an enumerated control accepts. |
channels |
integer | no | How many channels the control carries. A write from spec sets every channel to the same value. |
status.observed
The last value the operator read for each setting. The operator reads the card’s control device on every event it delivers, and PipeWire’s graph on every change it prints, so a change a person made with a knob or a client shows here without a poll.
| Field | Type | Required | Description |
|---|---|---|---|
volume |
integer | no | The gain PipeWire applies, as a percent of unity. An idle node reports no level of its own, so an idle endpoint reports the level the operator last wrote to it, and nothing until a level is declared. |
mute |
boolean | no | Whether the endpoint captures silence, on the same terms as volume. |
controls |
map[string]string | no | The value of every control in capabilities, in the same spelling spec.controls takes. A control with several channels reports the first channel’s value. |
status.claim
The claim that holds the endpoint now, and absent between holders.
| Field | Type | Required | Description |
|---|---|---|---|
namespace |
string | no | |
name |
string | no |
status.conditions[]
Connected reports that the endpoint can capture now: a plug in an analog jack, and always on USB. Ready reports that PipeWire holds a node for it.
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | yes | |
status |
string | yes | One of: True, False, Unknown. |
reason |
string | yes | |
message |
string | no | |
lastTransitionTime |
string | yes |
The name and the spec
A Source is named by the same rule as a Sink, and its spec
works the same way: the operator writes a declared volume and
mute when the declaration changes and when the endpoint appears,
writes a declared control back only where the hardware diverges from
it, validates a control against status.capabilities, and invents no
value. A Source has no status.session, and its spec.volume is a
number, the level alone. The
Sink reference
has the name table and
the rules in full.
The one difference is which controls attach. A Capture control,
Input Source, and a Mic Boost go to the card’s sources, and a
Playback control goes to its sinks.
Events
The operator posts a Normal Event on a Source for each change
of its Connected and Ready conditions, with the condition’s own
reason and message, and a Warning when Ready becomes False while
Connected is True. It posts SpecRefused when the spec states
a value the endpoint does not take, and the capture API posts
Captured for each recording a caller takes. The Events are in the
default namespace. The Sink reference
describes each reason.