AES67 subscribe (SDP)

Subscribe a channel input patch to an AES67 RTP stream by PUTting SDP text (or structured rtp_flow) on the Maxx Control REST API.

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 conceptsource_idTypical fields
AES67 STREAM25sdp_text (PUT) or rtp_flow
DANTE STREAM23label / channel_name / device_name
Device Dante AES67 moden/aPUT /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 the source list.

  • 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

ItemValue
MethodPUT (writes require token)
Path/settings/channel/{channel_id}/dsp/patch
BodyJSON array of patch objects (SettingsPatches)
Read backGET same path (or …/patch/{patch_id})
ConstraintsOPTIONS 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)

FieldRole
patch_idRequired. Patch slot on the channel (116).
source_id25 for AES67 STREAM.
channelSlot within the RTP stream (1N from SDP a=rtpmap channel count), not the Brooklyn RX channel.
sdp_textPUT-only, at patch root. Full SDP string; server parses into flow params. Max length 512. Not returned on GET.
rtp_flowAlternative 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.
gainOptional patch gain (dB).
muteOptional patch mute.
stream_channelGET-only Brooklyn RX channel assigned after placement (0 until placed). Do not send on PUT.

Rules from firmware / OpenAPI:

  • Put sdp_text on the patch object root. Nested rtp_flow.sdp_text is rejected.
  • Do not send sdp_text and structured rtp_flow params 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 (encoding 16 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

  1. Choose amp channel (channel_id) and patch (patch_id, often 1).
  2. Choose stream slot (channel: 1 = first channel in the SDP, 2 = second, …).
  3. PUT an array with one AES67 patch object including sdp_text.
  4. GET the patch back — expect source_id 25 and populated rtp_flow (not sdp_text).
  5. 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.

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}"
PathMethodPurpose
/info/aes67GETSAP-discovered AES67 flows (cache)
/info/aes67/refreshPUTClear SAP cache (listening continues)
/settings/device/dante/aes67GET/PUTDevice Dante AES67 enable ({"value": bool})

Source of truth

Path lists and schemas follow the on-device OpenAPI (Maxx Control → OVERVIEWREST API DOC). Firmware versions can add fields; always confirm SettingsPatch on the live device. See also the REST API overview and input patch recipes.