Migrating from aiortc
webrtc.aiortc runs code written for aiortc 1.15 on Python WebRTC’s engine. Install the extra, change the imports,
and the rest of the code stays as it is:
pip install "wrtc[aiortc]"
# before
from aiortc import RTCPeerConnection, RTCSessionDescription
from aiortc.contrib.media import MediaPlayer, MediaRecorder
# after
from webrtc.aiortc import RTCPeerConnection, RTCSessionDescription
from webrtc.aiortc.contrib.media import MediaPlayer, MediaRecorder
The layer follows aiortc’s API: its names, argument orders, dataclasses, events and errors. These pages don’t document
it again, so read aiortc’s documentation for the API. Every other module path changes
the same way: aiortc.sdp becomes webrtc.aiortc.sdp, aiortc.contrib.signaling becomes
webrtc.aiortc.contrib.signaling.
Everything built on RTCPeerConnection works, contrib.media and contrib.signaling included, except what
What differs lists. All 7 of aiortc’s examples run with only the imports changed, also against an
aiortc peer. H.264 works through VideoToolbox on macOS. Elsewhere, call await webrtc.openh264.install() once at
startup, see H.264.
Python WebRTC isn’t affiliated with or endorsed by aiortc.
What differs
Not supported:
ORTC below ICE. Standalone
RTCIceGatherer,RTCIceTransport,RTCDtlsTransport,RTCSctpTransport,RTCRtpSenderandRTCRtpReceiver, andRTCDataChannelon anything butpc.sctp, raiseNotImplementedError. Transports come fromRTCPeerConnection.Certificate import.
RTCCertificate(key, cert)raisesNotImplementedError. Generated certificates are ECDSA, with one SHA-256 fingerprint.Internal modules.
rtp,jitterbuffer,clock,rateandutilsaren’t there, andcodecshas only the bitrate constants: no encoder and decoder classes, noCODECSorHEADER_EXTENSIONStable.MAX_FRAME_RATEexists but isn’t applied, as in aiortc.An explicit
idon a non-negotiated data channel. It’s ignored, as the specification says.Encoded passthrough.
MediaPlayer(decode=False)works, but its packets are decoded and encoded again: the same output, more CPU.
Behaves differently:
ICE gathering.
setLocalDescriptionwaits for gathering at most 5 s, as aiortc does. A STUN server never answers through some network interfaces, such as a VPN’s or a virtual machine’s, and then both wait the full 5 s. ICE checks start while gathering runs, so the ICE state can be ahead of where aiortc would be. Every configured STUN and TURN server is used, where aiortc uses the first of each.SDP. Descriptions are the engine’s: other codecs and payload types, more attributes, and stricter validation of munged SDP.
getCapabilitieslists more codecs, so code that picks a codec by position can get another one. A format parameter you add to your own description changes what the peer sends you, not what you send.Opus. It negotiates as with the native API: mono at 32 kbps unless the peer asks for stereo, then stereo at 64 kbps. aiortc offers stereo and sends it at 96 kbps.
recv()still returns 48 kHz stereo frames, with a mono stream in both channels. To receive stereo, addstereo=1to the Opus format parameters of your description beforesetLocalDescription, as the Janus streaming example does.Video bitrate and resolution. The engine adapts the bitrate to the network instead of aiortc’s fixed range, unless you change the
codecsconstants. It also starts video at a low resolution and raises it as the bandwidth estimate grows, as browsers do, so the peer can receive frames smaller than the ones you send. aiortc always sends the full frame.Transports before negotiation.
sender.transportandreceiver.transportareNoneuntilsetLocalDescription.Data channels. Messages are limited to 256 KiB between Python WebRTC peers and to 64 KiB with an aiortc peer; a larger one raises
TypeError. AmaxPacketLifeTimeof 0 makes the channel unreliable.Remote media.
recv()buffers about 1 s (30 video frames, 100 audio chunks) and drops the oldest after that, where aiortc queues without limit. Remote video can arrive asyuv420p10leornv12, besidesyuv420p.Track end. Remote tracks end when the peer closes the connection or you call
close(). A remotetransceiver.stop()alone doesn’t end them.Stats. The ids are the engine’s.
remote-inbound-rtpandremote-outbound-rtpfollow the engine’s RTCP interval, so they arrive later than in aiortc.
Performance
Python WebRTC opens connections 7–8x faster than aiortc, moves 4.4–9x more data through a data channel and spends less CPU on audio. See the benchmarks.
The layer gives up some of that for compatibility: it costs up to 8% more than the same code on the native API. Each received video frame is copied into a PyAV frame, and importing PyAV adds about 24 MB at startup. The native API has neither cost, see Moving to the native API.
Moving to the native API
The layer is a way in. The native API has more to offer:
The browser API. W3C names in snake_case (camelCase works too), so MDN’s guides apply as they are. See MDN: RTCPeerConnection.
Trickle ICE. Send each candidate as it’s found, in the
icecandidateevent, instead of waiting for gathering. Connections set up sooner. See Connections.Frames without conversion.
MediaStreamTrackProcessorgivesVideoFrameandAudioData, and you copy only what you read. No PyAV import. See Media.More features. Bitrate caps for any sender, simulcast,
RTCRtpScriptTransformfor encoded frames, end-to-end encryption with SFrame, DTMF, typed dataclasses and full type stubs.
Common calls:
aiortc |
Python WebRTC |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
A |
|
An |
|
|
|
|
|
What catches code ported this way:
Handlers get one event argument. A handler written for aiortc, with no argument, raises
TypeErrorthat goes to the event loop’s exception handler, so it looks like the event never fires. Register handlers from a running event loop. See Events.ice_connection_statereachesconnected. aiortc reportscompletedinstead.A peer’s hang-up isn’t seen at once.
connection_statebecomesdisconnectedseveral seconds after the peer closes. Send a bye message through your signalling, or watch the DTLS transport’sstatechangeforclosed.close()is not a coroutine and fires no events, so don’t wait for a finalclosedstate change.replace_trackis a coroutine. Withoutawait, it does nothing.