Proposal Title
SAP Announcement Support for libAES67
Proposal ID
SE-00004
Author
Nevio Hirani
Motivation
While the SDP subsystem enables libAES67 to generate and interpret stream metadata, many AES67 systems rely on SAP (Session Announcement Protocol) to broadcast the availability of RTP streams over multicast. SAP is defined in RFC 2974 and is commonly used in AES67 deployments for automatic stream discovery in unmanaged or loosely coordinated networks.
Without SAP support, libAES67-based transmitters cannot advertise their presence on the network, and receivers cannot discover streams automatically without out-of-band mechanisms. Adding native SAP support will improve zero-configuration interoperability with devices from Dante, Ravenna, Livewire, and other AES67-compliant vendors.
Specification
This proposal introduces SAP as a transmission mechanism for SDP-formatted stream announcements. The SAP sender will periodically multicast SDP packets to a defined group address and port (239.255.255.255:9875 by default). The SAP receiver will listen for announcements on the same multicast group and parse received SAP messages to extract SDP payloads.
Proposed Design
The SAP support in libAES67 will be implemented as a lightweight, modular subsystem that interfaces directly with the existing SDP core. It is designed to allow applications built on top of libAES67 to announce and discover AES67-compliant RTP streams using multicast SAP packets, without requiring any additional configuration or external tools.
On the transmitter side, the library will periodically generate a SAP packet embedding an SDP payload that describes the current stream. The SDP content will be obtained using the existing aes67_sdp_generate() function and encapsulated into a SAP-compliant message format. This packet will then be transmitted over UDP to a well-known multicast address (by default 239.255.255.255) on port 9875, as defined in RFC 2974. The transmission interval will be configurable, with a typical default of 5 seconds. Only SAP version 1 will be supported, and encryption or authentication features will be excluded from the initial implementation to reduce complexity and remain compatible with typical AES67 environments.
Each SAP packet consists of a fixed-length header, followed by a UTF-8 encoded SDP payload. The header includes metadata such as the version, message type (announcement or deletion), address type (IPv4 or IPv6), encryption flag, authentication header length (unused), and a 16-bit message ID hash used for duplicate suppression. In our case, the address type will always be IPv4, the encryption bit will be set to 0 (plaintext), and authentication will be disabled (Auth Len = 0).
SAP Packet Format
SAP messages are defined by the following structure, where the first 4 bytes represent the fixed-length header:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=1|A|R|T|E| Auth Len | Message ID Hash |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Fields
V (3 bits): SAP version, always 001.
A (1 bit): Address type, 0 for IPv4 (used here), 1 for IPv6.
R (1 bit): Reserved, must be zero.
T (1 bit): Message type: 0 for announcement, 1 for deletion.
E (1 bit): Encryption bit. Set to 0 (plaintext).
Auth Length (6 bits): Authentication header length in 32-bit words. Always 0 in this proposal.
Message ID Hash (16 bits): Computed CRC16 or hash of the SDP payload to identify duplicates.
Immediately after the header, the payload begins with the SDP data, encoded as UTF-8 text. No null terminator is required, but the message must not exceed the typical UDP MTU. In libAES67, the SAP message will be constructed in memory and sent over UDP using a dedicated socket. No authentication, source filtering, or encryption will be supported in the initial version.
On the receiver side, libAES67 will provide a SAP listener interface that opens a UDP socket and joins the designated SAP multicast group. This listener will continuously monitor incoming SAP packets on the socket and attempt to extract embedded SDP messages. When a valid SAP packet is received, the listener will verify the version and address type, check the header fields for conformity, and extract the SDP text payload. The payload is passed to aes67_sdp_parse() and then validated using aes67_sdp_validate(). If the result is AES67-compliant, a user-defined callback is invoked with the parsed aes67_sdp_session_t structure for further processing (e.g., joining a stream or updating a UI).
To support this architecture, the SAP subsystem will define two core public structures. The first is aes67_sap_announcement_t, which holds metadata for outgoing announcements, including the SDP string, announcement interval, and source address. The second is aes67_sap_listener_t, which encapsulates state for the UDP listener socket, callback function, and any internal buffering or filtering logic.
The sender side will expose functions to initialize, start, and stop periodic SAP announcements. Internally, this will be implemented using a timer or thread that calls aes67_sdp_generate() and wraps the result into a SAP header before transmitting it via a raw socket to the configured group address. The receiver will offer APIs to start listening, register callbacks, and stop reception cleanly.
The packet construction, parsing, and validation logic will be strictly compliant with RFC 2974, with allowances made for AES67-specific payload contents and constraints (e.g., payloads will always be SDP, and announcements will never use encryption or authentication headers).
Ultimately, this subsystem allows any libAES67 application to participate in multicast-based discovery workflows by broadcasting or passively receiving stream descriptions. This significantly improves interoperability in AoIP environments, particularly in zero-config scenarios, and provides a foundation for integrating with directory services or control protocols such as SIP or RTSP.
Required Types and Functions
To support this specification, the following public and internal APIs must be introduced in libAES67 to implement full SAP support for AES67 SDP stream advertisement and discovery.
Core Data Types
SAP Lifecycle Functions
Internal Packet Utilities
Error Handling
Constants and Macros
Debugging and Diagnostics
Configuration Functions
Announcement Management
Discovery Utilities
Affected Layer
sdp
Alternatives Considered
None
Impact
This change will not break backward compatibility, has minimal performance impact, and will require updated documentation for the new SAP API.
References
Additional Notes
This proposal is part of a larger effort to enhance AES67 interoperability; feedback and contributions are welcome.
Is there an existing proposal for this?
Proposal Title
SAP Announcement Support for libAES67
Proposal ID
SE-00004
Author
Nevio Hirani
Motivation
While the SDP subsystem enables libAES67 to generate and interpret stream metadata, many AES67 systems rely on SAP (Session Announcement Protocol) to broadcast the availability of RTP streams over multicast. SAP is defined in RFC 2974 and is commonly used in AES67 deployments for automatic stream discovery in unmanaged or loosely coordinated networks.
Without SAP support, libAES67-based transmitters cannot advertise their presence on the network, and receivers cannot discover streams automatically without out-of-band mechanisms. Adding native SAP support will improve zero-configuration interoperability with devices from Dante, Ravenna, Livewire, and other AES67-compliant vendors.
Specification
This proposal introduces SAP as a transmission mechanism for SDP-formatted stream announcements. The SAP sender will periodically multicast SDP packets to a defined group address and port (
239.255.255.255:9875by default). The SAP receiver will listen for announcements on the same multicast group and parse received SAP messages to extract SDP payloads.Proposed Design
The SAP support in libAES67 will be implemented as a lightweight, modular subsystem that interfaces directly with the existing SDP core. It is designed to allow applications built on top of libAES67 to announce and discover AES67-compliant RTP streams using multicast SAP packets, without requiring any additional configuration or external tools.
On the transmitter side, the library will periodically generate a SAP packet embedding an SDP payload that describes the current stream. The SDP content will be obtained using the existing
aes67_sdp_generate()function and encapsulated into a SAP-compliant message format. This packet will then be transmitted over UDP to a well-known multicast address (by default239.255.255.255) on port9875, as defined in RFC 2974. The transmission interval will be configurable, with a typical default of 5 seconds. Only SAP version 1 will be supported, and encryption or authentication features will be excluded from the initial implementation to reduce complexity and remain compatible with typical AES67 environments.Each SAP packet consists of a fixed-length header, followed by a UTF-8 encoded SDP payload. The header includes metadata such as the version, message type (announcement or deletion), address type (IPv4 or IPv6), encryption flag, authentication header length (unused), and a 16-bit message ID hash used for duplicate suppression. In our case, the address type will always be IPv4, the encryption bit will be set to
0(plaintext), and authentication will be disabled (Auth Len = 0).SAP Packet Format
SAP messages are defined by the following structure, where the first 4 bytes represent the fixed-length header:
Fields
V(3 bits): SAP version, always 001.A(1 bit): Address type, 0 for IPv4 (used here), 1 for IPv6.R(1 bit): Reserved, must be zero.T(1 bit): Message type: 0 for announcement, 1 for deletion.E(1 bit): Encryption bit. Set to0(plaintext).Auth Length(6 bits): Authentication header length in 32-bit words. Always0in this proposal.Message ID Hash(16 bits): Computed CRC16 or hash of the SDP payload to identify duplicates.Immediately after the header, the payload begins with the SDP data, encoded as UTF-8 text. No null terminator is required, but the message must not exceed the typical UDP MTU. In libAES67, the SAP message will be constructed in memory and sent over UDP using a dedicated socket. No authentication, source filtering, or encryption will be supported in the initial version.
On the receiver side, libAES67 will provide a SAP listener interface that opens a UDP socket and joins the designated SAP multicast group. This listener will continuously monitor incoming SAP packets on the socket and attempt to extract embedded SDP messages. When a valid SAP packet is received, the listener will verify the version and address type, check the header fields for conformity, and extract the SDP text payload. The payload is passed to
aes67_sdp_parse()and then validated usingaes67_sdp_validate(). If the result is AES67-compliant, a user-defined callback is invoked with the parsedaes67_sdp_session_tstructure for further processing (e.g., joining a stream or updating a UI).To support this architecture, the SAP subsystem will define two core public structures. The first is
aes67_sap_announcement_t, which holds metadata for outgoing announcements, including the SDP string, announcement interval, and source address. The second isaes67_sap_listener_t, which encapsulates state for the UDP listener socket, callback function, and any internal buffering or filtering logic.The sender side will expose functions to initialize, start, and stop periodic SAP announcements. Internally, this will be implemented using a timer or thread that calls
aes67_sdp_generate()and wraps the result into a SAP header before transmitting it via a raw socket to the configured group address. The receiver will offer APIs to start listening, register callbacks, and stop reception cleanly.The packet construction, parsing, and validation logic will be strictly compliant with RFC 2974, with allowances made for AES67-specific payload contents and constraints (e.g., payloads will always be SDP, and announcements will never use encryption or authentication headers).
Ultimately, this subsystem allows any libAES67 application to participate in multicast-based discovery workflows by broadcasting or passively receiving stream descriptions. This significantly improves interoperability in AoIP environments, particularly in zero-config scenarios, and provides a foundation for integrating with directory services or control protocols such as SIP or RTSP.
Required Types and Functions
To support this specification, the following public and internal APIs must be introduced in libAES67 to implement full SAP support for AES67 SDP stream advertisement and discovery.
Core Data Types
aes67_sap_announcement_t: Struct representing a local SDP announcement, including metadata like interval, source address, and SDP payload.typedef void (*aes67_sap_callback_t)(const aes67_sdp_session_t* session, const struct sockaddr_in* from, void* user_ctx): Callback type invoked when a valid SAP packet with SDP content is received.aes67_sap_listener_t: Opaque struct representing the state of a multicast SAP receiver (socket, callback, buffers).SAP Lifecycle Functions
aes67_sap_start_announcement(const aes67_sap_announcement_t* ann): Starts periodic SAP announcement of the given SDP session.aes67_sap_stop_announcement(void): Stops the current SAP announcement loop.aes67_sap_start_listener(aes67_sap_listener_t* listener, aes67_sap_callback_t callback, void* user_ctx): Starts a SAP listener that invokes the callback on each valid announcement.aes67_sap_stop_listener(aes67_sap_listener_t* listener): Stops the listener socket and background receiver logic.Internal Packet Utilities
aes67_sap_build_packet(const char* sdp_text, size_t len, uint8_t* out_buf, size_t max_len): Builds a SAP-compliant packet header and appends SDP payload.aes67_sap_parse_packet(const uint8_t* data, size_t len, char* out_sdp, size_t max_len): Extracts the SDP payload from a received SAP packet.aes67_sap_hash(const char* sdp_text, size_t len): Computes the 16-bit Message ID Hash used for duplicate suppression.Error Handling
aes67_sap_result_t: Enum representing the result of SAP operations (OK,INVALID_HEADER,SOCKET_ERROR, etc.).aes67_sap_strerror(aes67_sap_result_t result): Converts a result code into a human-readable string.Constants and Macros
AES67_SAP_PORT: Default port number (9875) for SAP messages.AES67_SAP_GROUP_IPV4: Default IPv4 multicast address (239.255.255.255) for SAP.AES67_SAP_VERSION: SAP version used (1).AES67_SAP_HEADER_LEN: Length of the SAP packet header (4 bytes minimum).AES67_SAP_MAX_PACKET_SIZE: Maximum size of a full SAP packet (typically 1400 bytes).AES67_SAP_MAX_SDP_LEN: Maximum allowed size of the embedded SDP payload.AES67_SAP_MSG_TYPE_ANNOUNCE: SAP message type constant for "announce" (0).AES67_SAP_MSG_TYPE_DELETE: SAP message type constant for "delete" (1).AES67_SAP_DEFAULT_INTERVAL: Default interval between announcements (e.g.,5000ms).Debugging and Diagnostics
aes67_sap_dump_packet(const uint8_t* packet, size_t len, FILE* out): Dumps the contents of a SAP packet for debug logging.aes67_sap_dump_announcement(const aes67_sap_announcement_t* ann, FILE* out): Prints the metadata of an active SAP announcement.Configuration Functions
aes67_sap_set_multicast_group(const char* ip): Sets the multicast group IP address used for SAP transmission/reception.aes67_sap_set_multicast_port(uint16_t port): Sets the UDP port used for SAP.Announcement Management
aes67_sap_register_announcement(const aes67_sap_announcement_t* ann): Registers an SDP stream for periodic SAP broadcast.aes67_sap_unregister_by_id(uint16_t message_id_hash): Unregisters a specific SAP announcement by hash.Discovery Utilities
aes67_sap_get_active_sessions(aes67_sdp_session_t* out_list[], size_t max_sessions)Returns a list of parsed and validated SDP sessions from recently received SAP packets.
Affected Layer
sdp
Alternatives Considered
None
Impact
This change will not break backward compatibility, has minimal performance impact, and will require updated documentation for the new SAP API.
References
Additional Notes
This proposal is part of a larger effort to enhance AES67 interoperability; feedback and contributions are welcome.
Is there an existing proposal for this?