Skip to main content

reactor-webrtc (Python)

Python bindings for the reactor-webrtc WebRTC engine, built with PyO3 and distributed as a self-contained wheel — no separate native library required at runtime.

Installation

pip install reactor-webrtc

Requires Python ≥ 3.10.

Quick start

PeerConnection's signaling methods are natively awaitable, so this runs inside an asyncio event loop:

import asyncio
import reactor_webrtc as rw

async def main():
    factory = rw.PeerConnectionFactory()

    obs = rw.PeerConnectionObserver()
    obs.on_ice_candidate = lambda c: relay_to_peer(c)
    obs.on_connection_state_change = lambda s: print("state:", s)

    config = rw.RtcConfiguration(ice_servers=[
        rw.IceServer(urls=["stun:stun.l.google.com:19302"]),
        # A turn:/turns: entry needs both credentials, or libwebrtc rejects the
        # whole configuration.
        rw.IceServer(urls=["turn:turn.example.com:3478"], username="alice", password="secret"),
    ])
    pc = factory.create_peer_connection(config, obs)

    offer = await pc.create_offer()
    await pc.set_local_description(offer)

    # Exchange offer.sdp with the remote peer via your signaling channel, then:
    # await pc.set_remote_description(remote_answer)
    # await pc.add_ice_candidate(candidate)

asyncio.run(main())

Audio

# Headless / server: push PCM programmatically (synthetic ADM, default)
factory = rw.PeerConnectionFactory()
factory.push_audio_frame(pcm_bytes, sample_rate=48000, channels=1)

# Desktop client: real mic + AEC3 + noise suppression
factory = rw.PeerConnectionFactory(
    platform_adm=True,
    echo_canceller=True,
    noise_suppression=True,
)

Pre-encoded video

factory, video = rw.PeerConnectionFactory.with_encoded_video_track(
    "camera", width=1280, height=720
)
pc = factory.create_peer_connection(config, obs)
tx = video.add_transceiver(pc, rw.TransceiverDirection.SendOnly)

# From your encoder thread:
video.push_encoded_frame(
    data=h264_annex_b,
    is_key_frame=True,
    width=1280, height=720,
)

Codec preferences

tx = pc.add_transceiver(rw.MediaKind.Video, rw.TransceiverDirection.SendOnly)
tx.set_track(video_track)
tx.set_codec_preferences([rw.VideoCodec.Vp9, rw.VideoCodec.Vp8])

answer = await pc.create_answer()
await pc.set_local_description(answer)

# Only after set_local_description does the sender's negotiated codec list
# exist to lock onto — set_codec_preferences alone shapes the SDP, not which
# negotiated codec this side's own sender actually encodes with.
tx.lock_negotiated_send_codec()

Receiving media

obs = rw.PeerConnectionObserver()

def on_track(kind, track):
    if kind == rw.MediaKind.Video:
        track.on_video_frame(lambda bgra, w, h: display(bgra, w, h))
    elif kind == rw.MediaKind.Audio:
        track.on_audio_frame(lambda pcm, sr, ch, n: play(pcm))

obs.on_track = on_track

Stats

report = await pc.get_stats()
for pair in report.candidate_pairs:
    print(pair.state, f"{pair.current_round_trip_time_s * 1000:.1f}ms")

API reference

Class Description
PeerConnectionFactory Entry point; creates peer connections and tracks
PeerConnection SDP offer/answer, ICE, tracks, data channels, stats
PeerConnectionObserver Callbacks: state, ICE candidate, track, data channel
RtcConfiguration ICE servers, ICE transport type, gathering policy
IceServer A STUN or TURN server entry
IceCandidate A trickled ICE candidate
SessionDescription SDP offer or answer (kind, sdp, ice_ufrags, with_ice_credentials, declares_frame_metadata, with_frame_metadata)
FrameMetadata Per-frame frame_id, timestamp, user_data
FrameMetadataGate What the remote declared about per-frame metadata
Track Local (push frames) or remote (attach sink) media track
EncodedVideoTrack Push pre-encoded video (H.264 Annex-B, VP8, VP9, …)
Transceiver RTP m-section: mid, kind, set_track, set_direction, set_codec_preferences, lock_negotiated_send_codec, set_sender_transform, set_receiver_transform
DataChannel SCTP data channel: send, on_message, on_open, …
StatsReport inbound_rtp, outbound_rtp, candidate_pairs
FrameMetadata, FrameAction, EncodedFrame, FrameTransform Per-frame metadata trailers and custom encoded-frame transforms — see docs/frame-metadata.md
Enum Values
PeerConnectionState New, Connecting, Connected, Disconnected, Failed, Closed
IceGatheringState New, Gathering, Complete
TransceiverDirection SendRecv, SendOnly, RecvOnly, Inactive
VideoCodec Vp8, Vp9, Av1, H264, H265
MediaKind Audio, Video
DataChannelState Connecting, Open, Closing, Closed
IceCandidatePairState Waiting, InProgress, Failed, Succeeded, Cancelled
String-valued field Values
RtcConfiguration.ice_transport_type all (default), relay, no_host, none
RtcConfiguration.continual_gathering_policy once (default), continually
RtcConfiguration.bundle_policy Balanced (default), MaxBundle, MaxCompat
RtcConfiguration.tcp_candidate_policy Disabled (default), Enabled

RtcConfiguration also takes a min_port/max_port pair (UDP port range), ice_connection_receiving_timeout_ms, and ice_check_interval_strong_connectivity_ms; PeerConnection.set_bitrate sets congestion-control bitrate limits after the connection is created. All covered in docs/configuration.md.

Per-frame metadata

Arbitrary bytes can ride alongside each encoded video frame, in a protobuf trailer appended to the payload:

video.push_video_frame(bgra, 320, 240, user_data=b"anything you like")

def on_frame(bgra, w, h, meta):
    if meta is not None:
        print(meta.frame_id, meta.timestamp, meta.user_data)

track.on_video_frame(on_frame)

That only works if the far end strips the trailer before its decoder sees it, so support is negotiated in the SDP and you do not have to do anything for it:

  • create_offer advertises the capability as one session-level a=x-reactor-frame-metadata:1 (rw.FRAME_METADATA_ATTRIBUTE, rw.FRAME_METADATA_VERSION).
  • create_answer mirrors an offer that asked for it.
  • set_remote_description arms pc.frame_metadata_gate() and, when it is open, installs the embed and strip transforms on the video transceivers. The sender transform still checks the gate per frame, so a renegotiation that drops support stops the trailers.

A peer that has never heard of the attribute ignores it, the gate stays closed, and user_data is silently dropped rather than corrupting that peer's decode. Read pc.frame_metadata_gate().is_open() if you want to know whether the peer agreed.

Read the declaration from the signalled SDP string, not from pc.remoteDescription or its equivalents: libwebrtc and browsers both discard a= lines they do not recognise while parsing.

A FrameTransform of your own composes with the metadata step rather than displacing it — the library owns libwebrtc's single transformer slot per sender/receiver and runs both. Your callback goes first in both directions, so it sees exactly the bytes that traverse the network.

To keep frame metadata out of a connection entirely:

config = rw.RtcConfiguration(frame_metadata=False)

No a=extmap, no mirroring, no transforms, and user_data is dropped — the connection is indistinguishable from one built before the capability existed.

Choosing your own ICE credentials

libwebrtc generates the ICE ufrag and password itself and offers no setter. If something upstream needs to recognise a session by its ufrag — an edge relay that demultiplexes on it, say — substitute the credentials in the description before setting it locally:

answer = await pc.create_answer()
answer = answer.with_ice_credentials(my_ufrag, my_password)
await pc.set_local_description(answer)

answer.ice_ufrags()  # ["<my_ufrag>", ...] — one per m-section

The local description is what libwebrtc reads the transport's ICE parameters from, so the substituted values are the ones that end up on the wire.

Two things to get right:

  • Order. Setting the local description is what creates the transport and starts gathering, so substituting afterwards acts on nothing.
  • Renegotiation. Changing the credentials between generations is an ICE restart (RFC 8445 §9). On a renegotiation that is not meant to restart ICE, pass the values the session already uses.

Raises if a value is outside RFC 8445's ranges (ufrag 4–256 characters, password 22–256) or contains anything outside ice-char — which is also what stops a newline in a credential from injecting an SDP line.

Thread safety

PeerConnection's signaling methods (create_offer, create_answer, set_local_description, set_remote_description, add_ice_candidate, get_stats) plus set_bitrate are natively awaitable — await them directly, no asyncio.to_thread()/executor wrapping needed. They still take a few milliseconds to resolve while the WebRTC engine responds, but that wait happens off the event loop thread, so it never blocks other coroutines.

Every other method (add_track, add_transceiver, transceivers, create_data_channel, and everything on Track/Transceiver/DataChannel) is a fast synchronous call with no native round-trip, and stays a plain function call — no await.

Callbacks fire on WebRTC internal threads with the GIL acquired; keep them fast.

License

Apache-2.0. Upstream WebRTC is BSD-3-Clause + the WebRTC patent grant.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

reactor_webrtc-0.8.0-cp310-abi3-win_amd64.whl (8.0 MB view details)

Uploaded CPython 3.10+Windows x86-64

reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl (9.1 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ x86-64

reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl (8.6 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ ARM64

reactor_webrtc-0.8.0-cp310-abi3-macosx_13_0_x86_64.whl (8.3 MB view details)

Uploaded CPython 3.10+macOS 13.0+ x86-64

reactor_webrtc-0.8.0-cp310-abi3-macosx_11_0_arm64.whl (7.3 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file reactor_webrtc-0.8.0-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for reactor_webrtc-0.8.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 a2e9b23415f567a635fd9c3482849dac5c8a479eb36a661b5350a809a2339c3f
MD5 84db65bc1ee50f10f486ef98afd95047
BLAKE2b-256 2f23698aee5c2e73329874a626a742c8d8712817dca7a970c10b648b0dd917ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for reactor_webrtc-0.8.0-cp310-abi3-win_amd64.whl:

Publisher: publish.yml on reactor-team/reactor-webrtc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 ebf164e3724f35524e7f8509b7f960196994c7384a1e5e4673864f82f5917872
MD5 7a4b691d0dcef3d32d1a75547b74ef98
BLAKE2b-256 9b295c520a45bbfbdb7a21bdeab39ff5e7b97d3a4d69ea869a6b29eda6db7554

See more details on using hashes here.

Provenance

The following attestation bundles were made for reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl:

Publisher: publish.yml on reactor-team/reactor-webrtc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl.

File metadata

File hashes

Hashes for reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl
Algorithm Hash digest
SHA256 3dc9f92d53cd45acc5dfe9d64873fd7da7b64dcd7697e87886c84eacdf6a8ab4
MD5 2c479ddfaa2ddf1553e77c57d4182cf1
BLAKE2b-256 3184ad76d58d95b9ab85e4adb9adf586d3f0fa710c748ea97c0ab0d3569f4180

See more details on using hashes here.

Provenance

The following attestation bundles were made for reactor_webrtc-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl:

Publisher: publish.yml on reactor-team/reactor-webrtc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reactor_webrtc-0.8.0-cp310-abi3-macosx_13_0_x86_64.whl.

File metadata

File hashes

Hashes for reactor_webrtc-0.8.0-cp310-abi3-macosx_13_0_x86_64.whl
Algorithm Hash digest
SHA256 ffba64b21dac9af1ba445650f081e77f4cb41b3c9e91437fffb250429c3eb8fe
MD5 8357d7c83d6e2c1ee96835539a13e668
BLAKE2b-256 501ec6f1619ed046fb4cd9ae02603b3a93c9acb88e9485191aad6e02fd370f97

See more details on using hashes here.

Provenance

The following attestation bundles were made for reactor_webrtc-0.8.0-cp310-abi3-macosx_13_0_x86_64.whl:

Publisher: publish.yml on reactor-team/reactor-webrtc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reactor_webrtc-0.8.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for reactor_webrtc-0.8.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4c8506c69310a5f1a12a35c9717b1d2858f866b997210978b40d4f8c6b77fd74
MD5 2d4fa413b695904699514aa8cd2a59d4
BLAKE2b-256 fa6c6605b87d6407e3bf688412211cb0542e67e4de77b146ff6c4491be02a76d

See more details on using hashes here.

Provenance

The following attestation bundles were made for reactor_webrtc-0.8.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on reactor-team/reactor-webrtc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.14.0

5 files

0.13.0

5 files

0.12.1

5 files

0.12.0

5 files

0.11.0

5 files

0.10.0

5 files

0.9.0

5 files

This release

0.8.0 This release

5 files

0.7.2

5 files

0.7.1

5 files

0.7.0

5 files

0.6.0

5 files

0.5.0

5 files

0.4.2

5 files

0.4.1

5 files

0.4.0

5 files

0.3.0

4 files

0.2.0

4 files

0.1.0

4 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page