Skip to content

Live777 Core (liveion) ​

A Pure Single SFU Server for WebRTC.

WHIP / WHEP protocols are supported by default. When built with the rtsp feature, liveion can also act as an RTSP server: push media in via ANNOUNCE/RECORD and pull media out via DESCRIBE/PLAY.

a core SFU server, If you need a single server, use this

Current support codecs ​

protocolvideo codecsaudio codecs
WHIPAV1, VP9, VP8, H265, H264Opus, G722
WHEPAV1, VP9, VP8, H265, H264Opus, G722
RTSPAV1, VP9, VP8, H265, H264Opus, G722

live777-apps

Current client support ​

ApplicationAV1VP9VP8H265H264OPUSG722
Browser whip⭐⭐⭐⭐⭐⭐⭐
Browser whep⭐⭐⭐⭐⭐⭐⭐
Gstreamer whip⭐⭐⭐⭐⭐⭐⭐
Gstreamer whep📺 2⭐⭐⭐⭐⭐⭐
Gstreamer whipinto📺 1⭐⭐⭐⭐📺 1⭐
Gstreamer whepfrom📺 2⭐⭐⭐⭐⭐⭐
FFmpeg whipinto💩⭐⭐⭐⭐⭐⭐
FFmpeg whepfrom💩⭐⭐⭐⭐⭐⭐
VLC whipinto💩💩⭐⭐⭐⭐💩
VLC whepfrom💩💩⭐⭐⭐⭐💩
OBS Studio whip📺 3💩💩⭐⭐⭐💩
  • ⭐ It's working
  • 💩 Don't support
  • 💡 I don't know, No testing
  • 📺 Note have some problem
    1. Working, But Browser can't player this video, Gstreamer to Gstreamer is working
    2. I don't know why av1 and whep error
    3. OBS av1 codec can't play

Authentication ​

No Authentication ​

DANGER

No Authentication is Default

If no set any about [auth] section in configuration file, There will no authentication

Bearer token ​

Static HTTP bearer token is super admin access, you should use in develop, test, debug or cluster manager

toml
# WHIP/WHEP auth token
# Headers["Authorization"] = "Bearer {token}"
[auth]
# static JWT token, superadmin, debuggger can use this token
tokens = ["live777"]

JWT(JSON Web Token) ​

Use this authentication, the token include access, you can control stream, publish, subscribe...

toml
# WHIP/WHEP auth token
# Headers["Authorization"] = "Bearer {token}"
[auth]
# JSON WEB TOKEN secret
secret = "<jwt_secret>"

Cascade ​

What is cascade? ​

cascade

Very large cluster ​

mash-cascade

live777 Cascade have two mode:

  • cascade-pull
  • cascade-push

live777-cascade

Static cascade-pull (WHEP source) ​

cascade-pull can also be declared as a static stream input instead of an API call. Build with the source-whep feature and add a whep:// source to a provisioned stream; it behaves like any other configured source (on_demand start/stop, reconnect, RTCP keyframe feedback):

toml
[[stream.cam1.sources]]
url = "whep://edge-0:7777/whep/cam1"
# With Bearer auth:
# url = "whep://token@edge-0:7777/whep/cam1"

When this source is used with on_demand = true, live777 waits at least 35000ms for the upstream WHEP source to become ready, even if on_demand_start_timeout_ms is lower. This covers cold upstream on-demand pulls that may spend the WHEP HTTP request timeout before delivering media.

The outgoing WHEP peer gathers ICE candidates using the server's own [[ice_servers]] configuration (empty by default — host candidates only, no hardcoded STUN server) and binds the UDP sockets from [webrtc] ice_udp_addrs.

Chained on-demand pulls work one hop deep by default: this source's WHEP HTTP request waits up to 40000ms for the answer, covering an upstream that itself holds subscribes for up to 35000ms (an on-demand WHEP source). Deeper chains need their budgets aligned per deployment. Stopping this source (e.g. when the last subscriber leaves an on_demand stream) may wait out an in-flight WHEP HTTP request, bounded by that same 40000ms timeout.

Static cascade-push (WHIP target) ​

cascade-push can also be declared as a static stream output instead of an API call. Build with the target-whip feature and add a whip:// target to a provisioned stream:

toml
[[stream.cam1.targets]]
url = "whip://edge-1:7777/whip/cam1"
# With Bearer auth:
# url = "whip://token@edge-1:7777/whip/cam1"

The push is media-driven: it is established when the stream gains a publisher (WHIP or a configured source) and torn down when the publisher goes away, so the downstream node sees ordinary publisher attach/detach cycles and its own auto_delete_*/on-demand strategies keep working. A configured target on an on_demand stream acts as standing demand: its sources are (re)started whenever the stream has neither a publisher nor a push session, so the relay recovers on its own once an unreachable downstream is back. A lost session (downstream close, ICE failure, provisioned-stream reset) is re-established with exponential backoff (5s doubling, 60s cap) — the same pacing bounds the on-demand source restarts, at roughly one attempt per minute.

DataChannel Forward ​

NOTE: About createDataChannel()

  1. Live777 Don't support label, createDataChannel(label) this label is not used
  2. Live777 Don't support negotiated, { id: 42, negotiated: true } this don't support

live777-datachannel

RTSP Server ​

Build with the rtsp feature to expose an RTSP server alongside WHIP/WHEP:

bash
cargo build --release --bin live777 --features rtsp

Configure the listen URL in live777.toml. When credentials are present in the URL, Digest authentication is enabled automatically:

toml
[rtsp]
# No auth:
listen = "0.0.0.0:8554"
# With Digest auth:
# listen = "rtsp://admin:secret@0.0.0.0:8554"

The same port handles both directions:

  • Push media into a stream: rtsp://host:8554/{stream_id} (ANNOUNCE + RECORD)
  • Pull media from a stream: rtsp://host:8554/{stream_id} (DESCRIBE + PLAY)

Both UDP and TCP (RTP/AVP/TCP) transports are supported. The first URL path segment is used as the liveion stream identifier.

Stream Hooks ​

Live777 can run external scripts on stream lifecycle events: stream creation/deletion, and publish start/stop (a WHIP/cascade publisher attaching, or a configured source starting/stopping). A typical use is on-demand source activation for devices live777 cannot drive directly: a hook starts a capture device / hardware encoder when media is actually wanted, and stops it when the last consumer leaves to save resources.

Built-in alternative: configured sources ([[stream.<name>.sources]]) support on_demand = true, which starts/stops the camera or RTSP pull automatically with subscriber presence — no scripts needed. See livehal — Provisioned streams and on-demand sources.

Which events signal "someone is watching"? For provisioned streams (any [stream.<name>] entry) stream-created fires once at startup, so it cannot drive power control — use publish-started / publish-stopped instead. Configured sources report their start/stop as publish events with the session id virtual-source. For dynamic streams (auto_create_whep), stream-created still coincides with first use.

toml
# Global hooks, run for every stream.
[hooks]
timeout_ms = 5000    # per-script timeout, 0 disables
on_error = "stop"    # "stop" skips the remaining hooks of the same event;
                     # "continue" runs them anyway
on_stream_created = ["/etc/live777/hooks/notify.sh"]
on_stream_deleted = ["/etc/live777/hooks/notify.sh", "/etc/live777/hooks/cleanup.sh"]
on_publish_started = ["/etc/live777/hooks/camera-power.sh"]
on_publish_stopped = ["/etc/live777/hooks/camera-power.sh"]

# Per-stream hooks, run after the global ones.
[stream.cam1.hooks]
on_stream_created = ["/etc/live777/hooks/cam1-created.sh"]
on_stream_deleted = ["/etc/live777/hooks/cam1-deleted.sh"]
on_publish_started = ["/etc/live777/hooks/cam1-power-on.sh"]
on_publish_stopped = ["/etc/live777/hooks/cam1-power-off.sh"]

Execution guarantees:

  • Hooks of one event run sequentially: global first, then per-stream, in configured order.
  • Events are processed in a single FIFO queue — all hooks of an earlier event finish before any hook of a later event starts, so a stream's stream-created hooks always complete before its stream-deleted hooks begin.
  • A failed script (non-zero exit, spawn error, timeout kill) is logged and handled per on_error; it never affects later events or the server.

Scripts are executed directly (no shell) and receive the event metadata both as argv and as environment variables:

argvenvvalue
$1LIVE777_EVENTstream-created / stream-deleted / publish-started / publish-stopped
$2LIVE777_STREAMstream name
$3 (deleted/stopped only)LIVE777_REASONstream-deleted: api-deleted / publish-leave-timeout / subscribe-leave-timeout / orphaned / reset; publish-stopped: peer-closed / api-deleted / replaced / idle-timeout
—LIVE777_SESSION(publish events only) publisher session id; virtual-source for configured sources

Notes:

  • Scripts should return quickly after initiating their work (e.g. launch an encoder in the background). A blocked script blocks the whole hook queue.
  • Make scripts idempotent: a stream-deleted hook also runs when the publisher itself died (publish-leave-timeout), so stop scripts must tolerate an already-stopped device.
  • For on-demand sources, combine with [strategy] auto_create_whep = true and a generous auto_delete_whep (e.g. 30000) so brief subscriber flapping does not cycle the hardware. Per-stream overrides live under [stream.<name>.strategy]. Streams configured with on_demand = true already debounce via on_demand_close_after_ms.
  • No hooks fire on server shutdown (no stream-deleted events are emitted then).

Source change hooks ​

on_source_changed hooks fire when a source's parameter set is changed through the admin tier API, POST /api/sources/{stream}/tier — a tier is a named preset of source parameters (see Bitrate telemetry & quality tiers), so applying one changes the source. Unlike the lifecycle hooks above, they run synchronously inside the apply, before the tier's capture+encoder re-provisioning — they never go through the FIFO hook queue.

This is where hardware that must match the new rung before the pipeline rebuild belongs: on platforms where framerate is a sensor-mode property the capture node cannot change (e.g. Rockchip V4L2 pipelines, VIDIOC_S_PARM unsupported on rkisp1), the script pokes the sensor's mode gear, then the rebuild streams into the already-correct hardware state.

Execution contract:

  • Global [hooks] scripts first, then per-stream, in configured order — all awaited before the re-provisioning. Rapid tier applies are serialized, so a script cannot interleave with another tier's rebuild.
  • A failing script aborts the tier apply when on_error = "stop", before the pipeline is touched (the API call fails).
  • When the apply does not reach the target state — aborted, or the pipeline rebuild failed and rolled back — the scripts run again with the source's current state, so hardware they switched is switched back. This compensation run is best effort (failures are only logged).
  • Make scripts idempotent: "bring the hardware in line with the given values" is the whole contract, and the same script serves both runs.

Metadata (argv is <stream> <tier>; there is no LIVE777_EVENT — the hook only ever fires for this one event):

argv / envvalue
$1 / LIVE777_STREAMstream name
$2 / LIVE777_SOURCE_TIERtier name (empty when the source runs its configured base profile)
LIVE777_SOURCE_WIDTHpre-apply run: declared tier capture width (empty when inheriting base); compensation run: the pipeline's actual current width
LIVE777_SOURCE_HEIGHTdeclared tier capture height / actual current height
LIVE777_SOURCE_FPSdeclared tier capture framerate / actual current framerate
LIVE777_SOURCE_BITRATEtier bitrate in bits/s / actual current bitrate
toml
[stream.cam.hooks]
# Switch the sensor mode gear ahead of the pipeline rebuild; on a failed
# apply the same script runs again with the restored state and switches
# the gear back.
on_source_changed = ["/etc/live777/hooks/sensor-gear.sh"]

Released under the MPL-2.0 License.