Connections
RTCPeerConnection is one end of a connection. Each end describes itself in an SDP description, and
the two exchange their descriptions and ICE candidates over a signalling channel the application provides.
Configuration
A connection takes an RTCConfiguration. To connect across NATs, give it STUN and TURN servers:
config = webrtc.RTCConfiguration(
ice_servers=[
webrtc.RTCIceServer(urls='stun:stun.l.google.com:19302'),
webrtc.RTCIceServer(urls='turn:turn.example.com:3478', username='user', credential='secret'),
],
)
pc = webrtc.RTCPeerConnection(config)
Dictionaries of the specification are typed dataclasses, with the names in snake_case. Methods take these classes in place of plain dicts, so a misspelled member raises an error where itβs written. See MDN: RTCPeerConnection.
Signalling
The side that starts creates an offer, the other one answers:
# the caller
offer = await pc.create_offer()
await pc.set_local_description(offer)
await signaling.send(offer)
# the callee
await pc.set_remote_description(offer)
answer = await pc.create_answer()
await pc.set_local_description(answer)
await signaling.send(answer)
# the caller again
await pc.set_remote_description(answer)
signaling stands for your own channel. Descriptions are RTCSessionDescriptionInit objects with a
type and an sdp, which is all a channel has to carry.
Candidates are gathered after set_local_description, and trickle in the icecandidate event. Send each one to the
other side, which adds it with add_ice_candidate(). The last event has no candidate,
which means gathering is complete. See MDN: RTCPeerConnection/icecandidate_event.
@pc.on('icecandidate')
async def on_candidate(event):
if event.candidate is not None:
await signaling.send(event.candidate.to_json())
To skip trickling, wait for ice_gathering_state to be 'complete' and send pc.local_description, which then has
every candidate in it.
States
The state of a connection is in connection_state, and each change fires
connectionstatechange (see MDN: RTCPeerConnection/connectionState):
@pc.on('connectionstatechange')
def on_state(_event):
if pc.connection_state == 'failed':
pc.restart_ice()
Enums are strings: pc.connection_state == webrtc.RTCPeerConnectionState.connected and
pc.connection_state == 'connected' are the same check.
Stats
get_stats() returns an RTCStatsReport, a mapping of ids to typed
stats objects (see MDN: RTCStatsReport):
report = await pc.get_stats()
for stats in report.values():
if stats.type == 'candidate-pair' and stats.nominated:
print('round trip time:', stats.current_round_trip_time)
Closing
close() stops the transports and the media at once. A closed connection canβt be
reused, so create a new one to connect again.