AES67 subscribe (SDP)
Categories:
Subscribe an amplifier channel DSP patch to an external AES67 / SDP audio source. This is the same path Maxx Control’s channel-input UI uses when you paste SDP or pick a discovered AES67 flow.
When to use
Use this when a third-party sender (PipeWire, another AES67 device, etc.) publishes an SDP session and you want a MAXX channel to receive that multicast/unicast RTP stream as its input source AES67 STREAM (source_id 25).
Do not confuse this with:
| API concept | source_id | Typical fields |
|---|---|---|
| AES67 STREAM | 25 | sdp_text (PUT) or rtp_flow |
| DANTE STREAM | 23 | label / channel_name / device_name |
| Device Dante AES67 mode | n/a | PUT /settings/device/dante/aes67 with {"value": true|false} |
Dante AES67 enable is a separate device setting (Brooklyn AES67 mode). AES67 STREAM patches use the channel patch API below.
Prerequisites
Amplifier reachable at
http://${IP}/rest-api/…(see REST API overview).Firmware that exposes AES67 STREAM as an input source. Confirm with:
curl -X OPTIONS "http://${IP}/rest-api/settings/channel/1/dsp/patch"Look for
source_id: 25/"AES67 STREAM"in thesourcelist.A complete SDP the device can parse (see SDP requirements below).
Write calls need the API key header
token(default documented in the overview).
API path and method
| Item | Value |
|---|---|
| Method | PUT (writes require token) |
| Path | /settings/channel/{channel_id}/dsp/patch |
| Body | JSON array of patch objects (SettingsPatches) |
| Read back | GET same path (or …/patch/{patch_id}) |
| Constraints | OPTIONS on the same path |
There is no PUT on /settings/channel/{channel_id}/dsp/patch/{patch_id} — only GET for a single patch. Writes always go to the collection path with one or more objects in the array.
Payload fields (AES67 STREAM)
| Field | Role |
|---|---|
patch_id | Required. Patch slot on the channel (1…16). |
source_id | 25 for AES67 STREAM. |
channel | Slot within the RTP stream (1…N from SDP a=rtpmap channel count), not the Brooklyn RX channel. |
sdp_text | PUT-only, at patch root. Full SDP string; server parses into flow params. Max length 512. Not returned on GET. |
rtp_flow | Alternative to sdp_text: structured fields (connection_ip, rtp_port, sample_rate, encoding, channels, session_name, clock_offset, session_id, source_address). Returned on GET after a successful subscribe. |
gain | Optional patch gain (dB). |
mute | Optional patch mute. |
stream_channel | GET-only Brooklyn RX channel assigned after placement (0 until placed). Do not send on PUT. |
Rules from firmware / OpenAPI:
- Put
sdp_texton the patch object root. Nestedrtp_flow.sdp_textis rejected. - Do not send
sdp_textand structuredrtp_flowparams together. - Prefer one approach: paste SDP or fill
rtp_flow.
SDP requirements
Server-side parse (same as Maxx Control paste) requires a parseable AES67 SDP with:
- PCM encoding L16 or L24 (
encoding16 or 24) o=session id and origin IPv4 (source address)a=mediaclk:direct=…(or equivalent) so clock offset is present
OpenAPI maxLength for sdp_text is 512 characters. Trim optional SDP lines if you hit the limit.
Step-by-step: PUT with sdp_text
- Choose amp channel (
channel_id) and patch (patch_id, often1). - Choose stream slot (
channel:1= first channel in the SDP,2= second, …). - PUT an array with one AES67 patch object including
sdp_text. - GET the patch back — expect
source_id25 and populatedrtp_flow(notsdp_text). - Optionally check patch status and persist with
/settings/save.
Example SDP
v=0
o=root 1 3991817848 IN IP4 172.149.4.123
s=stream1
c=IN IP4 239.192.150.1/32
t=3991817848 0
m=audio 5004 RTP/AVP 127
i=2 channels: L, R
a=recvonly
a=rtpmap:127 L24/48000/2
a=source-filter: incl IN IP4 239.192.150.1 172.149.4.123
a=ssrc:2393712005
a=ptime:1.000000
a=framecount:48
a=ts-refclk:ptp=IEEE1588-2008:00-1D-C1-FF-FE-29-1C-8A:0
a=mediaclk:direct=2626298912
a=tool:PipeWire 1.6.7
a=type:broadcast
This session is 2 channels at 48 kHz / L24. Use "channel": 1 for L and "channel": 2 for R on two amp channels (or two patches) if needed.
JSON shape
[
{
"patch_id": 1,
"source_id": 25,
"channel": 1,
"gain": 0.0,
"sdp_text": "v=0\no=root 1 3991817848 IN IP4 172.149.4.123\ns=stream1\n…"
}
]
Newlines in SDP must be encoded inside the JSON string (\n). Do not put raw multi-line SDP inside a single-quoted -d '…' shell string unless you build JSON with a tool.
curl (recommended: build JSON with Python)
export IP=10.77.150.42
export TOKEN=f4005bf8507999192162d989d5a60823
BODY=$(python3 - <<'PY'
import json
sdp = """v=0
o=root 1 3991817848 IN IP4 172.149.4.123
s=stream1
c=IN IP4 239.192.150.1/32
t=3991817848 0
m=audio 5004 RTP/AVP 127
i=2 channels: L, R
a=recvonly
a=rtpmap:127 L24/48000/2
a=source-filter: incl IN IP4 239.192.150.1 172.149.4.123
a=ssrc:2393712005
a=ptime:1.000000
a=framecount:48
a=ts-refclk:ptp=IEEE1588-2008:00-1D-C1-FF-FE-29-1C-8A:0
a=mediaclk:direct=2626298912
a=tool:PipeWire 1.6.7
a=type:broadcast"""
print(json.dumps([{
"patch_id": 1,
"source_id": 25,
"channel": 1,
"gain": 0.0,
"sdp_text": sdp,
}]))
PY
)
curl -X PUT "http://${IP}/rest-api/settings/channel/1/dsp/patch" \
-H "token: ${TOKEN}" \
-H "Content-Type: application/json" \
-d "${BODY}"
curl (inline \n escapes)
curl -X PUT "http://${IP}/rest-api/settings/channel/1/dsp/patch" \
-H "token: ${TOKEN}" \
-H "Content-Type: application/json" \
-d '[{"patch_id":1,"source_id":25,"channel":1,"gain":0.0,"sdp_text":"v=0\no=root 1 3991817848 IN IP4 172.149.4.123\ns=stream1\nc=IN IP4 239.192.150.1/32\nt=3991817848 0\nm=audio 5004 RTP/AVP 127\ni=2 channels: L, R\na=recvonly\na=rtpmap:127 L24/48000/2\na=source-filter: incl IN IP4 239.192.150.1 172.149.4.123\na=ssrc:2393712005\na=ptime:1.000000\na=framecount:48\na=ts-refclk:ptp=IEEE1588-2008:00-1D-C1-FF-FE-29-1C-8A:0\na=mediaclk:direct=2626298912\na=tool:PipeWire 1.6.7\na=type:broadcast"}]'
Structured rtp_flow alternative
If you already know the RTP parameters (or you copied them from a GET after an SDP PUT):
curl -X PUT "http://${IP}/rest-api/settings/channel/1/dsp/patch" \
-H "token: ${TOKEN}" \
-H "Content-Type: application/json" \
-d '[{
"patch_id": 1,
"source_id": 25,
"channel": 1,
"gain": 0.0,
"rtp_flow": {
"connection_ip": "239.192.150.1",
"rtp_port": 5004,
"sample_rate": 48000,
"encoding": 24,
"channels": 2,
"session_name": "stream1",
"clock_offset": 2626298912,
"session_id": "3991817848",
"source_address": "172.149.4.123"
}
}]'
Exact field meaning matches OpenAPI schema SettingsPatch / rtp_flow.
Verify
# Settings: parsed flow (sdp_text is write-only — not echoed)
curl "http://${IP}/rest-api/settings/channel/1/dsp/patch/1"
# Patch health
curl "http://${IP}/rest-api/status/channel/1/dsp/patch/1"
# Optional: SAP-discovered sessions (cache; may include sdp_text)
curl "http://${IP}/rest-api/info/aes67"
# Optional: Dante interface flows (interface_id from GET /status/interface/interfaces)
curl "http://${IP}/rest-api/status/interface/interfaces/4"
Persist runtime settings if needed:
curl -X PUT "http://${IP}/rest-api/settings/save" \
-H "token: ${TOKEN}"
Related discovery endpoints
| Path | Method | Purpose |
|---|---|---|
/info/aes67 | GET | SAP-discovered AES67 flows (cache) |
/info/aes67/refresh | PUT | Clear SAP cache (listening continues) |
/settings/device/dante/aes67 | GET/PUT | Device Dante AES67 enable ({"value": bool}) |
Source of truth
Path lists and schemas follow the on-device OpenAPI (Maxx Control → OVERVIEW → REST API DOC). Firmware versions can add fields; always confirm SettingsPatch on the live device. See also the REST API overview and input patch recipes.