Real-time Transport Protocol (RTP)
voip.rtp
Real-time Transport Protocol (RTP) implementation of RFC 3550.
RTPCall
dataclass
One call leg managed by the RTP multiplexer.
Associates a SIP dialog with the RealtimeTransportProtocol media
stream. Subclass and override packet_received to process incoming
media, and use send_packet to transmit outbound media.
The rtp and sip back-references allow the handler to send data
back to the caller and to terminate the call via SIP BYE.
Subclass voip.audio.AudioCall for audio calls with codec
negotiation, buffering, and decoding.
Attributes:
| Name | Type | Description |
|---|---|---|
rtp |
RealtimeTransportProtocol
|
Shared RTP multiplexer socket that delivers packets to this handler. |
sip |
SessionInitiationProtocol
|
SIP session that answered this call (used for BYE etc.). |
caller |
CallerID
|
Caller identifier as received in the SIP From header. |
media |
MediaDescription
|
Negotiated SDP media description for this call leg. |
srtp |
SRTPSession | None
|
Optional SRTP session for encrypting and decrypting media. |
Source code in voip/rtp.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
hang_up()
async
Terminate the call by sending a SIP BYE request.
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
Not yet implemented; the call_id and remote SIP address need to be stored per call to make this work. |
Source code in voip/rtp.py
141 142 143 144 145 146 147 148 | |
negotiate_codec(remote_media)
classmethod
Negotiate a media codec from the remote SDP offer.
Override in subclasses to implement codec selection. The SIP layer calls this before sending a 200 OK; if the method raises the exception propagates and the call is not answered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
remote_media
|
MediaDescription
|
The SDP |
required |
Returns:
| Type | Description |
|---|---|
MediaDescription
|
A |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
When not overridden by a subclass. |
Source code in voip/rtp.py
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
packet_received(packet, addr)
Handle a parsed RTP packet. Override in subclasses to process media.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
packet
|
RTPPacket
|
Parsed RTP packet. |
required |
addr
|
tuple[str, int]
|
Remote |
required |
Source code in voip/rtp.py
119 120 121 122 123 124 125 | |
send_packet(packet, addr)
Serialize packet and send it via the shared RTP socket.
Encrypts the packet with the call's SRTP session when one is set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
packet
|
RTPPacket
|
RTP packet to send. |
required |
addr
|
tuple[str, int]
|
Destination |
required |
Source code in voip/rtp.py
127 128 129 130 131 132 133 134 135 136 137 138 139 | |
RTPPacket
dataclass
Bases: ByteSerializableObject
RTP data packet RFC 3550 §5.1.
Source code in voip/rtp.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
RTPPayloadType
Bases: IntEnum
Common RTP payload types, aligned with SDP media format identifiers.
Static payload types (0–95) are defined by RFC 3551. Dynamic payload types (96–127) are negotiated via SDP. Opus uses payload type 111 per RFC 7587.
Source code in voip/rtp.py
32 33 34 35 36 37 38 39 40 41 42 43 | |
RealtimeTransportProtocol
dataclass
Bases: STUNProtocol
RTP multiplexer: routes incoming datagrams to per-call handlers (RFC 3550).
One instance manages multiple simultaneous calls on a single UDP socket.
Register per-call Call handlers with
register_call; each incoming datagram is dispatched to the
matching handler's datagram_received method by
remote source address.
Use addr=None in register_call as a wildcard catch-all for
calls whose remote RTP address is not known in advance (no SDP in INVITE).
Source code in voip/rtp.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
packet_received(data, addr)
Route an incoming SRTP datagram to the matching per-call handler.
Looks up addr in the call registry. Falls back to the wildcard
None handler when no exact match exists. Drops the packet with a
debug log when no handler is registered at all.
When the matched handler carries an SRTP session the packet is authenticated and decrypted before being forwarded; packets that fail authentication are logged at WARNING level and discarded.
Source code in voip/rtp.py
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
register_call(addr, handler)
Register handler for RTP traffic arriving from addr.
Use addr=None as a wildcard to handle traffic from any source that
has no dedicated routing entry (useful when the caller's RTP address is
not known in advance from the INVITE SDP).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
addr
|
tuple[str, int] | None
|
Remote |
required |
handler
|
RTPCall
|
A |
required |
Source code in voip/rtp.py
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
unregister_call(addr)
Remove the handler registered for addr.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
addr
|
tuple[str, int] | None
|
The same key that was passed to |
required |
Source code in voip/rtp.py
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
Encryption
voip.srtp
Secure Real-time Transport Protocol (SRTP) implementation of RFC 3711.
Provides symmetric key encryption and authentication for RTP media streams using the AES_CM_128_HMAC_SHA1_80 cipher suite. Keys are negotiated via SDP Security Descriptions (SDES, RFC 4568) carried in the SIP 200 OK SDP body, which is itself protected by TLS.
Requires the cryptography package (included in any installation).
SRTPSession
dataclass
SRTP session for one call leg using AES_CM_128_HMAC_SHA1_80.
Handles symmetric encryption and authentication of RTP packets.
Key material is derived from master_key and master_salt via the
SRTP pseudo-random function (RFC 3711 §4.3.1).
Create a fresh session for each answered call via generate and pass
it to the RTPCall instance. The SDP a=crypto: attribute is produced
by sdes_attribute.
Attributes:
| Name | Type | Description |
|---|---|---|
master_key |
bytes
|
16-byte AES master key (randomly generated). |
master_salt |
bytes
|
14-byte master salt (randomly generated). |
Source code in voip/srtp.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
sdes_attribute
property
SDP a=crypto: attribute value for SDES key exchange (RFC 4568).
The key material (master key followed by master salt) is base64-encoded
and wrapped in the standard SDES inline format. Include this value in
the SDP a=crypto: attribute of the answered media description:
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:<value>
decrypt(packet)
Decrypt and authenticate an SRTP packet.
Verifies the HMAC-SHA1-80 authentication tag and, if valid, decrypts the payload with AES-CM. The packet index is estimated per RFC 3711 §3.3.1, tracking rollovers across the 16-bit sequence space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
packet
|
bytes
|
Raw SRTP packet bytes (at least 12 + 10 bytes). |
required |
Returns:
| Type | Description |
|---|---|
bytes | None
|
Decrypted RTP packet bytes, or |
bytes | None
|
or the packet is too short. |
Source code in voip/srtp.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
encrypt(packet)
Encrypt an RTP packet to produce an SRTP packet.
Encrypts the RTP payload with AES-CM and appends an 80-bit HMAC-SHA1 authentication tag. Tracks sequence number rollover per RFC 3711 §3.3.1 to ensure the packet index remains unique.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
packet
|
bytes
|
Raw RTP packet bytes (at least 12 bytes). |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
SRTP packet with encrypted payload and appended auth tag. |
Source code in voip/srtp.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
generate()
classmethod
Generate a new SRTP session with a cryptographically random key and salt.
Source code in voip/srtp.py
96 97 98 99 100 101 102 | |
NAT Traversal
voip.stun
Session Traversal Utilities for NAT (STUN) implementation of RFC 5389.
STUNAttributeType
Bases: IntEnum
STUN attribute types (RFC 5389 §15).
Source code in voip/stun.py
27 28 29 30 31 | |
STUNMessageType
Bases: IntEnum
STUN message types (RFC 5389 §6).
Source code in voip/stun.py
20 21 22 23 24 | |
STUNProtocol
dataclass
Bases: DatagramProtocol
Protocol for demultiplexing STUN (RFC 5389/7983) from other traffic.
Use this as the base class for any protocol that shares a UDP socket with
STUN. Incoming datagrams whose first byte is in [0, 3] (RFC 7983) are
treated as STUN messages and routed to the STUN handler. All other
datagrams are forwarded to packet_received.
When the socket is ready and the reachable address is known,
stun_connection_made is called. If stun_server_address is
None this happens synchronously from connection_made with the
local socket address. If STUN is configured it is called from
datagram_received when the Binding Response arrives, with the
discovered public address. Subclasses only need to override
stun_connection_made — no connection_made override is
required:
class MyProtocol(STUNProtocol):
def stun_connection_made(
self,
transport: asyncio.DatagramTransport,
addr: tuple[str, int],
) -> None:
# socket is ready; addr is the reachable (public or local) address
...
def packet_received(self, data: bytes, addr: tuple[str, int]) -> None:
process(data)
Source code in voip/stun.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | |
close()
Close the underlying UDP transport.
Source code in voip/stun.py
114 115 116 117 | |
connection_lost(exc)
Clear the internal transport reference on disconnect.
Source code in voip/stun.py
131 132 133 | |
packet_received(data, addr)
Override in subclasses to handle non-STUN datagrams.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes
|
Raw datagram payload (first byte ≥ 4, not a STUN packet). |
required |
addr
|
tuple[str, int]
|
Source |
required |
Source code in voip/stun.py
138 139 140 141 142 143 144 | |
send(data, addr)
Send a raw datagram through the shared UDP socket.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes
|
Raw bytes to transmit. |
required |
addr
|
tuple[str, int]
|
Destination |
required |
Source code in voip/stun.py
104 105 106 107 108 109 110 111 112 | |
stun_connection_made(transport, addr)
Called when the socket is ready and the reachable address is known.
When STUN is configured, addr is the public (ip, port)
discovered from the STUN Binding Response and this method is called
by datagram_received. When stun_server_address=None,
addr is the local socket address and this method is called
synchronously from connection_made.
Subclasses override this method to trigger protocol-specific initialisation once the socket is ready.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transport
|
DatagramTransport
|
The UDP transport bound to this protocol. |
required |
addr
|
tuple[str, int]
|
Reachable |
required |
Source code in voip/stun.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | |