Skip to content

Latest commit

 

History

422 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LiveForge

High-performance multi-protocol live streaming server written in Go

Go License CI

English | 中文


📖 Wiki Documentation (EN) | 📖 Wiki 文档 (中文)

Full guides on deployment, configuration, cluster topologies, GB28181, audio transcoding, and more.


LiveForge is a modular live streaming media server that ingests, transmuxes, and delivers audio/video in real time. It supports RTMP, RTSP, SRT, WebRTC (WHIP/WHEP), HLS, LL-HLS, DASH, HTTP-FLV, FMP4, GB28181, and WebSocket streaming from one server binary. The default build has no native FFmpeg requirement; optional audio transcoding requires the audiocodec build tag, CGO, and FFmpeg/libav libraries.

Highlights

Feature Description
🔀 Any-to-any protocol bridge Push RTMP, pull via WebRTC; push WebRTC, pull via HLS — any combination works
🎵 On-demand audio transcoding Automatic codec bridging (AAC ↔ Opus ↔ G.711 ↔ MP3) between protocols, powered by FFmpeg/libav
📡 GB28181 video surveillance Full SIP signaling stack, device registration, live invite, playback, PTZ control, alarm handling — plus a built-in device simulator for testing
🌐 Multi-protocol cluster Origin-edge cascading via RTMP / SRT / RTSP / RTP / GB28181 with HTTP scheduler callback for dynamic topology
LL-HLS Low-Latency HLS with fMP4 partial segments, blocking playlist reload (_HLS_msn/_HLS_part), and delta playlist updates
🖥️ Web console Permission-aware tabs for Streams, GB28181, Config, Cluster, SIP Calls, Storage, and Security; Recent Audit is inside Security; grouped as Workspace, Operations, and System; Streams has stable rows with inline four-metric trends; /console/publish is a WHIP/SIP/GB28181 broadcaster workspace
🛡️ Production-ready Slow consumer protection (EWMA frame dropping), GCC congestion control, per-IP rate limiting, Prometheus metrics

Features

Protocols

  • Multi-protocol ingest — Publish via RTMP, RTSP (TCP + UDP, separate eligible audio/video SETUP tracks), SRT, WebRTC WHIP, or GB28181
  • Multi-protocol playback — Pull via RTMP, RTSP, SRT, WebRTC WHEP, HLS, LL-HLS, DASH, HTTP-FLV, HTTP-TS, FMP4, or WebSocket
  • Continuous HTTP integrity — HTTP-FLV, HTTP-TS, FMP4, and their WebSocket outputs terminate on a ring overwrite and never bridge the media gap with a retained post-gap packet
  • Segment overwrite handling — HLS and LL-HLS discard partial media, reopen the refreshed direct/transformed audio source when needed, and recover at live media with one discontinuity (video waits for a keyframe; audio-only resumes immediately); retained LL-HLS fMP4 media keeps matching immutable versioned init data, while DASH preserves completed single-Period media and retires the affected manager
  • SRT — Secure Reliable Transport with AES encryption, low-latency MPEG-TS delivery (pure Go via datarhei/gosrt)
  • WebRTC — WHIP/WHEP with a 1 MiB SDP offer limit, ICE Lite, GCC send-side bandwidth estimation, and browser-based publish
  • Codec support — H.264, H.265/HEVC, VP8, VP9, AV1, AAC, Opus, G.711 (μ-law/A-law), MP3

Audio Transcoding

On builds with the audiocodec tag and FFmpeg/libav available, LiveForge transparently bridges audio codecs between protocols. When a subscriber requires a different audio codec than the publisher provides, on-demand transcoding kicks in automatically — zero configuration needed. Portable no-CGO builds keep compatible audio pass-through and may omit unsupported audio while retaining playable video.

Publisher → Subscriber Codec Path Use Case
RTMP (AAC) → WebRTC (Opus) AAC → PCM → Opus Browser playback of RTMP streams
WebRTC (Opus) → RTMP (AAC) Opus → PCM → AAC Re-stream browser input to CDN
GB28181 (G.711) → HLS (AAC) G.711 → PCM → AAC Surveillance camera to web player
Any → Any (tagged build) Decode → Resample → Encode Supported audio codec combinations

Transcoding is shared per target codec — multiple subscribers requesting the same codec share one transcode pipeline. When the publisher's codec matches the subscriber's, frames pass through with zero overhead.

Requires the audiocodec build tag, CGO, and FFmpeg/libav development libraries at build time. See Wiki: Audio Transcoding for build instructions and details.

For an FFmpeg H.264+AAC RTMP stream, Console HLS/DASH playback retains AAC audio without transcoding. When the server reports audio transcoding unavailable, Console WebRTC requests video only and displays a persistent notice; AAC/MP3 audio-only WebRTC previews show a capability error. Enable the tagged build and audio_codec.enabled for AAC-to-Opus WebRTC audio. External WHEP clients that request unsupported audio still receive HTTP 415. See RTMP playback troubleshooting.

Long GOPs that exceed the persistent cache limit recover their complete startup sequence from the retained ring buffer. If the keyframe has already left that buffer, playback waits for a fresh keyframe. Console HLS waits for 0.8 seconds of contiguous buffered media before starting and targets 1.5 seconds of live latency; actual latency depends on the stream and network.

GB28181 Video Surveillance

Full GB/T 28181 national standard support for connecting IP cameras and NVRs:

  • SIP signaling — Device registration, keepalive monitoring, digest authentication
  • Catalog query — Automatic device and channel discovery
  • Live invite — Server-initiated INVITE to pull live video from cameras
  • Recording playback — Time-range playback of device-side recordings
  • PTZ control — Pan/tilt/zoom and preset commands per GB28181 Annex A
  • Alarm handling — Receive and process device alarm notifications
  • MPEG-PS demuxing — RTP/PS stream receiving with H.264 + AAC extraction
  • REST API — Full device/channel/session management via /api/v1/gb28181/*
  • Streams as first-class citizens — GB28181 streams appear in the stream hub and can be played via any output protocol (HLS, RTMP, WebRTC, etc.)

See Wiki: GB28181 Guide for configuration and usage details.

GB28181 Device Simulator

A built-in simulator (tools/gb28181-sim) emulates a GB28181 IPC camera for acceptance testing:

# Build and run the simulator
go run ./tools/gb28181-sim -server 127.0.0.1:5060 -fps 25

# Customizable: device ID, domain, transport, keepalive, audio toggle
go run ./tools/gb28181-sim \
  -device-id 34020000001110000001 \
  -domain 3402000000 \
  -transport udp \
  -keepalive 30s \
  -no-audio

The simulator performs: SIP REGISTER → periodic keepalive → responds to catalog queries → streams RTP/PS (H.264+AAC) on INVITE → handles BYE.

Cluster

Multi-protocol forwarding and on-demand origin pull for building CDN-like topologies:

  • Forward (push) — Automatically push streams to downstream nodes when published
  • Origin pull — Lazily pull from upstream when a subscriber arrives, with idle timeout
  • Multi-protocol relay — RTMP, SRT, RTSP, RTP, and GB28181 transports
  • HTTP scheduler — Dynamic target resolution via external HTTP callback, or static target lists
  • Topologies — Origin-edge, origin-multi-edge, origin-center-edge (three-tier)
  • Retry & resilience — Configurable retry count, interval, and backoff
  • Forwarding hot path — Relay readers use independent blocking waits; WHEP uses one condition-backed pump per source or target-audio reader so readiness and atomic reads cannot race; RTMP push reuses FLV encoding buffers, RTSP interleaving uses vectored writes, and relay byte metrics batch after the first observation to reduce per-frame overhead

See Wiki: Cluster Deployment for topology examples and configuration.

For focused forwarding measurements, run go test -bench='BenchmarkRingReader|BenchmarkRTMPConn' -benchmem ./pkg/util ./module/cluster. Benchmark values depend on the host and are not capacity guarantees.

The production regression suite also covers the cached bitrate-limit admission path and tagged shared-transcode fanout: go test -run '^$' -bench 'BenchmarkStreamIngressProduction|BenchmarkStreamIngressWithBitrateLimit|BenchmarkRTMPRelaySendMediaFrameProduction|BenchmarkRTSPRelaySendFrameProduction|BenchmarkRelayObservationAccounting' -benchmem -count=3 ./core ./module/cluster and, with FFmpeg available, go test -tags audiocodec -run '^$' -bench '^BenchmarkTranscodeReaderFanoutAdmission$' -benchmem -count=3 ./core. These are bounded regression measurements, not deployment capacity guarantees.

For production-path regression measurements that include stable-publisher Stream admission, ring/GOP writes, complete RTMP FLV/chunk framing, RTSP H.264 packetization/RTP/interleaved framing, and bounded relay-byte accounting, run go test -run '^$' -bench 'BenchmarkStreamIngressProduction|BenchmarkRTMPRelaySendMediaFrameProduction|BenchmarkRTSPRelaySendFrameProduction|BenchmarkRelayObservationAccounting' -benchmem -count=3 ./core ./module/cluster. On an Apple M1 Pro with Go 1.26.0, the fixture measured stable Stream ingress at 65.86-67.28 ns/op (29 B/op, 0 allocs/op), RTMP H.264 at 155.1-155.6 ns/op (24 B/op, 3 allocs/op), RTMP AAC at 73.60-73.76 ns/op (21 B/op, 3 allocs/op), RTSP single-NAL H.264 at 1.825-1.833 us/op (4,044 B/op, 9 allocs/op), and RTSP three-packet FU-A H.264 at 4.593-4.605 us/op (9,892 B/op, 23 allocs/op). Stream ingress uses a preallocated 64-second monotonic 25 fps H.264/50 fps G.711A frame pool with shared immutable payloads, no subscribers, bitrate limiting disabled, two retained GOPs, a 300-frame GOP bound, and a 4,096-entry ring. RTMP uses fixed-timestamp media frames and payload-scoped relay accounting; RTSP uses fixed-timestamp RTP input and framed-byte accounting. Both egress fixtures terminate at bounded in-memory writers, excluding socket writes, TCP writev, deadlines, and kernel/network syscall cost. The isolated accounting ns/op values also exclude the production context lookup and are primarily allocation-regression evidence. These paths are deliberately not compared with the older narrower BenchmarkStreamWriteFrame microbenchmark, and none of these figures predict subscriber count, concurrency, or deployment capacity.

For the bounded concurrent Stream ingress matrix, run go test -run '^$' -bench '^BenchmarkStreamIngressMatrix$' -benchtime=100ms -benchmem -count=3 ./core. It covers 1, 8, and 32 independent streams with 0, 4, and 16 in-process RingBuffer readers per stream; the benchmark and race tests are regression evidence, not deployment capacity guarantees.

For the bounded Prometheus cardinality matrix, run go test ./module/metrics -run '^$' -bench '^BenchmarkMetricsCardinalityMatrix$' -benchtime=100ms -benchmem -count=3. It covers 1000 active streams at limits 32 and 512, a 1000-entry exact allowlist, and eight concurrent Gather workers; admission keys are published as an immutable snapshot so stable Gather does not hold the admission mutex.

LL-HLS (Low-Latency HLS)

Apple LL-HLS implementation for sub-second latency HLS delivery:

  • Partial segments — Configurable part duration (default 200ms) for fine-grained delivery
  • Blocking playlist reload_HLS_msn and _HLS_part query parameters for server-push style updates
  • Delta playlist_HLS_skip=YES support to reduce playlist transfer size
  • fMP4 container — Default fMP4 with TS fallback option
  • fMP4 fragment parsing — Complete media segments assembled from multiple moof/mdat fragments are parsed without dropping earlier fragments
  • fMP4 AAC timing — Omitted AAC sample rate and channel count are derived from the AudioSpecificConfig; the resolved sample rate is reused as the media timescale so DTS intervals remain stable
  • Legacy player compat — Graceful degradation for players without LL-HLS support (buffered segment delivery)
  • Keyframe-aligned startup — Cached and live GOP frames remain continuous; HLS, LL-HLS, and DASH segmenters wait for the current publisher generation's required sequence headers and bind those headers, replay frames, and the live cursor from one startup snapshot. The initial Hls.js manifest waits for one complete segment without duplicating its parts. Its bounded wait covers the configured full-segment target plus one part (10-second floor, 30-second cap), and returns 503 instead of a part-only manifest if no full segment becomes available. Blocking reloads retain the latest completed part identities while consuming new low-latency parts. DASH also starts after one complete segment, uses a one-fragment live delay, and refreshes its MPD within two seconds. Manifest and segment write deadlines start immediately before the response write, so delayed readiness does not consume the write window. HLS, LL-HLS, and DASH manifests escape each stream-key segment, DASH URL attributes are XML-safe, and media-segment routing preserves valid keys at arbitrary path depth
  • Generation-safe finalization — Publisher stop retires the matching HLS, DASH, or LL-HLS manager from new request lookup while it drains every accepted frame through that generation's captured end cursor and finalizes once. A replacement publisher uses a distinct manager and cannot cross-contaminate the retired output. LL-HLS blocking reloads also terminate when the manager stops; HTTP module shutdown force-stops and joins active and draining manager workers

Management & Operations

  • Web console — Seven permission-aware tabs with multi-protocol preview and a dedicated /console/publish broadcaster workspace. All management views share the dark teal, mint/cyan/amber/coral visual system used by the console direction demo. Streams keeps fixed column tracks, restores a Preview action, and expands the selected row with four compact 60-second trends (bitrate, video FPS, audio FPS, and GOP duration) sampled once per second. The publish workspace selects WHIP/WebRTC, SIP, or GB28181 while preserving the existing protocol-lab lifecycle. Recent Audit is a surface inside Security, not a separate tab.
  • REST API — Stream lifecycle, config refresh/status, cluster status, SIP call control, recording/DVR management, security/audit, GB28181, and public health probes
  • Auth and RBAC — Named viewer/operator/admin API tokens, console sessions, JWT/callback publish/subscribe auth, bounded redacted audit trail
  • Recording and DVR — FLV, fragmented MP4, MP4, MPEG-TS, and HLS recording; new recordings default to fMP4/.mp4; every rotated recording file retains the declared tracks and latest codec initialization, starts each track on a zero-based file-local timeline, and remains independently parseable; TS emits PAT/PMT before its first media PES, while classic MP4 calculates audio/video durations on their independent clocks, saturates version-0 timing fields instead of wrapping, uses signed ctts version 1 for negative B-frame composition offsets, and writes expandable AAC ESDS lengths; fMP4 declares AAC directly, converts non-AAC source audio such as G.711, Opus, and MP3 to AAC through the optional audiocodec/FFmpeg build, and filters audio to keep playable video-only output when that path is unavailable; a stopped transformed recording drains source frames already committed before finalizing, while publisher-generation completion flushes retained resampler samples, pads the final partial PCM frame, and emits delayed encoder packets exactly once before Record/DVR output closes; DVR TS similarly normalizes audio unsupported by its target; segmentation, storage health, download/range/inline-play/delete management, exact full-ID action routing, retryable cleanup-before-primary deletion, zero-byte session protection, and time-shift status. Recording play/download acquire the global connection budget before opening media and apply a 10-second write deadline.
  • Recording/DVR state and routing — Only completed recordings are served by download or inline play; active and failed recordings return JSON 409 without media bytes. Record formats are flv, fmp4, mp4, ts, and hls (hls stores TS), with empty/zero or decimal B/KB/MB/GB size limits. Audio-only DVR publishes a segment at the audio DTS duration boundary while its publisher remains online. Generation-bound Record sessions wait only for zero-codec late discovery (the GB28181 SIP/PS path); publishers that declare codecs up front can begin before sequence headers arrive. DVR nested stream-key routes preserve slash hierarchy, reject encoded separators and dot segments, and escape ?, #, and % independently per key segment; /api/v1/server/info supplies the bound non-zero DVR port and its actual HTTP/TLS scheme.
  • Local protocol labs — SIP and GB28181 pages run one-shot and persistent fake-device checks locally without another platform or device. SIP uses separate H.264 and PCMA/PCMU RTP/RTCP tracks and never mutates a receive-mode source stream. Receive mode waits for the selected publisher generation's required sequence headers before signaling and treats the selected PCMA/PCMU value as the outbound target codec; a differing source uses a generation-bound shared audio transcode reader when the tagged runtime can produce that target, while H.264 remains on its original live cursor. Known unsupported conversions are rejected immediately. GB28181 publish registers a listening fake device and exercises LiveForge's normal server-initiated live-play and real RTP/RTCP receive path; receive requires H.264 plus direct G.711A or audio that the tagged runtime can convert to G.711A, then admits its source subscriber before module-owned PS/RTP/RTCP egress becomes active; transformed audio uses an independent generation-bound reader while H.264 remains direct. Subscriber-limit rejection fails startup synchronously, while a later media-send failure moves the Lab to failed and releases signaling and media resources. The dependency-free moving 160x90 test pattern runs at 25 fps with one IDR per second and audible 20 ms audio frames. Persistent GB28181 sessions renew Keepalive at roughly one-third of gb28181.keepalive.timeout. When both modules share one SIP listener, H.264 plus PCMA/PCMU RTP offers route to SIP Gateway while PS/90000 offers route to GB28181
  • Protocol lab admissionsip.gateway.max_lab_sessions and gb28181.max_lab_sessions bound active persistent lab sessions independently; the default is 16, terminal history does not consume the limit, non-positive values use the default, and a full ceiling returns HTTP 429 before sockets or media resources are allocated
  • SIP RTP port ownership — Gateway media pairs skip externally occupied ports and remain socket-bound throughout SDP negotiation; fake Lab endpoints avoid the configured gateway RTP range
  • SIP outbound retirement — Requested PCMA/PCMU conversion uses an independent publisher-generation-bound audio reader. Each ready frame is packetized before final send admission, then rechecks cancellation and the current publisher generation under the terminal send gate. Terminal teardown closes admission and owned sockets, drains already admitted sends without holding lifecycle or admission locks, and only then publishes terminal state and callbacks; publisher retirement releases the transcode reader and subscriber, reclaims the RTP/RTCP pair, and emits one BYE even when another teardown arrives concurrently
  • SIP overwrite recovery — Outbound SIP discards retained gap-crossing media and advances only the affected source or target-audio reader. A source gap keeps transformed audio flowing and gates direct H.264 until the newest same-generation sequence header plus IDR; a target-audio gap keeps direct video flowing and resumes audio at live media. Active-generation transformed-audio EOF fails the call as network_lost, and the dual-reader parent cancels and joins both media pumps before returning
  • Protocol Lab stream keys — SIP and GB28181 accept printable ASCII keys up to 256 bytes whose slash-separated segments are non-empty and are neither . nor ... GB28181 publish uses that requested key only for the loopback simulator; real devices retain {stream_prefix}/{channel_id}
  • GB28181 PS compatibility — Outbound PS converts internal AVCC/HVCC video samples to Annex-B so real GB28181 receivers can decode video
  • GB28181 overwrite recovery — Outbound PS/RTP serializes source and transformed-audio control results ahead of pending output, discards overwritten and pending pre-gap media, advances only the affected reader, and keeps unaffected media flowing. Source gaps start fresh PS state without resetting RTP sequence and resume H.264 only at the latest post-gap header plus IDR; transformed-audio gaps preserve clean video and PS state without restarting its original 20 ms holdback deadline
  • Lab diagnostics — Managers retain all active sessions plus the newest 16 terminal records. Failed sessions expose a bounded last_error with SIP credentials and bearer tokens removed; session views expose receiver-side RTCP and separate audio/video counters. Playback paths escape each stream-key segment and use actual bound listeners for absolute RTMP/RTSP URLs; Console Lab Preview consumes those returned paths directly
  • Startup rollback — Listener or module initialization failures report the original error, close only modules whose initialization was attempted, and do not panic while rolling back later uninitialized modules
  • Notifications — HTTP webhook (HMAC-SHA256 signed) and WebSocket real-time events
  • Prometheus metrics — Server-level gauges are always available when enabled; per-stream bitrate/FPS/GOP/subscriber labels are opt-in. Without an allowlist, the configured limit is a Collector-lifetime cardinality budget: active keys are admitted in creation order, retained as scalar keys after their streams disappear, and never replaced by churn. An exact allowlist defines the only eligible keys and the limit still bounds each scrape. stream_detail_limit: 0 disables per-stream series; negative configured limits are invalid and rejected. Use the management API for current stream detail or an exact allowlist for selected Prometheus labels
  • Rate limiting — Per-IP token bucket for connection flood protection; trusted proxy chains are resolved right-to-left so attacker-controlled XFF prefixes cannot select new buckets
  • HTTP connection timeouts — API, WebRTC signaling, and metrics listeners bound header parsing to 5 seconds and idle keep-alive connections to 2 minutes; existing write deadlines remain unchanged
  • Slow consumer protection — EWMA-based lag detection with progressive frame dropping
  • GCC congestion control — Send-side bandwidth estimation for WebRTC WHEP with adaptive bitrate pacing
  • Generation-bound startup — SIP, GB28181, recording, DVR, and cluster egress capture one publisher snapshot, replay only the required current headers/GOP once, then continue from its live cursor. DVR carries that validated snapshot through retained-index/storage recovery and rechecks the generation immediately before session installation; replacement during setup discards the candidate. DVR shutdown starts its absolute drain deadline before waiting for setup ownership, so blocked setup cannot extend the configured shutdown bound. SIP inbound INVITEs run synchronous publish authorization before RTP allocation and emit matching start/stop lifecycle events after activation, so recording and DVR follow the call. Publisher replacement cancels old readers, pure-audio streams never replay retained history, and sequence-header-only recordings are failed rather than published as successful media
  • Publisher ownership isolation — Each non-empty publisher ID can create only one generation during a Stream object's lifetime. Reusing A after an intervening B is rejected before stream state changes, so delayed A frames, activity, and cleanup cannot affect the active owner; a newly created Stream starts a separate identity lifetime. Once stream destruction starts, late cleanup cannot return it to an attachable state or reopen its closed ring
  • Hot GOP-bound reload — Tightening frame, duration, or byte bounds keeps the shortest keyframe-led playable prefix allowed by all active bounds and may seal it immediately. Duration uses the full unordered min/max DTS span without rewriting media order. With GOP caching enabled, at least one frame or byte bound must remain positive; zero disables only that bound. Relaxation lets only the active retained GOP admit future interleaved frames until the remaining bounds; older GOPs stay trimmed, omitted frames are not restored, and the next keyframe starts a new complete GOP

Architecture

graph LR
    subgraph Ingest
        OBS[OBS / FFmpeg] -->|RTMP| RTMP_MOD[RTMP Module]
        CAM[IP Camera] -->|RTSP| RTSP_MOD[RTSP Module]
        SRT_PUB[SRT Source] -->|SRT| SRT_MOD[SRT Module]
        BROWSER_PUB[Browser] -->|WHIP| WEBRTC_MOD[WebRTC Module]
        GB_DEV[GB28181 Device] -->|SIP+RTP| GB_MOD[GB28181 Module]
    end

    subgraph Core
        RTMP_MOD --> STREAM[Stream + GOP Cache + Ring Buffer]
        RTSP_MOD --> STREAM
        SRT_MOD --> STREAM
        WEBRTC_MOD --> STREAM
        GB_MOD --> STREAM
        STREAM --> TRANSCODE[Audio Transcode Manager]
        STREAM --> MUXER[Muxer Manager]
    end

    subgraph Delivery
        MUXER -->|HLS / LL-HLS / DASH| HTTP_MOD[HTTP Stream Module]
        MUXER -->|HTTP-FLV / TS / FMP4| HTTP_MOD
        MUXER -->|WebSocket| HTTP_MOD
        STREAM -->|RTMP| RTMP_SUB[RTMP Subscriber]
        STREAM -->|RTSP| RTSP_SUB[RTSP Subscriber]
        STREAM -->|SRT| SRT_SUB[SRT Subscriber]
        TRANSCODE -->|WHEP| WEBRTC_SUB[WebRTC Subscriber]
    end

    subgraph Cluster
        STREAM -->|Forward| FWD[Forward Manager]
        FWD -->|RTMP/SRT/RTSP/RTP/GB| EDGE[Edge Nodes]
        ORIGIN[Origin Servers] -->|Pull| OPL[Origin Pull Manager]
        OPL --> STREAM
    end

    subgraph Management
        API[REST API + Web Console]
        AUTH[Auth Module]
        NOTIFY[Webhook + WS Notifications]
        RECORD[FLV / FMP4 / MP4 / TS / HLS Recording]
        METRICS[Prometheus Metrics]
    end
Loading

Quick Start

Docker (released image)

The release workflow publishes versioned images to ghcr.io/im-pingo/liveforge after a v* tag completes. Make the GHCR package Public for anonymous pulls, or authenticate with docker login ghcr.io. Use a concrete version rather than latest. Before the first release, use the local Compose build in docs/recipes/docker-local.md.

docker run -d --name liveforge \
  -p 1935:1935 -p 8554:8554 -p 8080:8080 -p 8443:8443 \
  -p 6000:6000 -p 5060:5060/udp -p 8090:8090 \
  ghcr.io/im-pingo/liveforge:vX.Y.Z

Or with docker compose:

git clone https://github.com/im-pingo/liveforge.git
cd liveforge
docker compose up -d

Open http://localhost:8090/console to access the web console.

To use a custom config:

docker run -d --name liveforge \
  -v /path/to/liveforge.yaml:/etc/liveforge/liveforge.yaml:ro \
  -p 1935:1935 -p 8554:8554 -p 8080:8080 -p 8443:8443 \
  -p 6000:6000 -p 5060:5060/udp -p 8090:8090 \
  ghcr.io/im-pingo/liveforge:vX.Y.Z

Build from Source

git clone https://github.com/im-pingo/liveforge.git
cd liveforge
go build -o liveforge ./cmd/liveforge
./liveforge -c configs/liveforge.yaml

To enable audio transcoding, build with CGO and FFmpeg/libav:

CGO_ENABLED=1 go build -tags audiocodec -o liveforge ./cmd/liveforge

Publish a Stream

RTMP (OBS / FFmpeg):

ffmpeg -re -i input.mp4 -c copy -f flv rtmp://localhost:1935/live/stream1

RTSP:

ffmpeg -re -i input.mp4 -c copy -f rtsp rtsp://localhost:8554/live/stream1

SRT:

ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://localhost:6000?streamid=publish:/live/stream1"

WebRTC (Browser): Open http://localhost:8090/console, click "+ WebRTC Publish", select camera/mic, and start streaming.

The Console can publish H.265/HEVC video with Opus audio when the browser and platform expose an H.265 WebRTC encoder. WHIP maps audio and video RTP onto one session timeline, and HLS/DASH/FLV/TS use a combined transcode reader from the cached GOP source position so target audio history and live source video continue without a first-frame freeze or duplicate cached video. Its FMP4 preview preserves signed B-frame composition offsets on a near-zero timeline established when the shared muxer starts; later subscribers seek to their first buffered timestamp. For G.711 sources, the Console declares AAC in the FMP4 SourceBuffer only when GET /api/v1/server/info reports that the configured process can actually transcode both G.711 variants to AAC; portable builds keep the video-only declaration. WHEP Live replays the atomic cached GOP while source video continues from the matching ring cursor, with transcoded target audio read independently. The WebRTC transcode worker waits without consuming the source playback wakeup, so video pacing remains stable even when source audio pauses. The tagged audio build is the complete cross-protocol profile; see WHIP H.265 + Opus playback verification.

The Console's default WHEP preview uses the cached live startup path, so a normal H.264 GOP does not have to wait for a post-snapshot IDR. Protocol Lab returns distinct whep/whep_live (mode=live) and whep_realtime (mode=realtime) paths. A requested source audio or video track must negotiate successfully: an unsupported requested codec returns 415 and an internal track setup failure returns 500 instead of silently serving only the other track; disabled or non-receiving offer m-lines remain intentionally omitted. Receiving direction follows media-level attributes first and then session-level attributes, and codec matching requires an exact rtpmap name on a payload listed by that m-line. Explicit realtime mode reports a distinct waiting-keyframe state, including mixed feeds whose audio advances while video interframes are still being discarded before the first IDR. During active playback, WHEP binds each source or transformed-audio overwrite to its atomic reader result, discards the retained post-gap frame, and advances only that reader to live. One condition-backed pump exclusively owns each reader's readiness check, atomic read, and live advance; shutdown cancels and joins both pumps before releasing transformed-audio ownership once. A source overwrite preserves established audio while video returns to waiting_keyframe, resets pacing/DTS/PTS state, and resumes with the latest same-generation parameter sets plus a keyframe; audio-only playback resumes at the next live frame. A transformed target-audio overwrite leaves clean video continuous, while active expected target-audio EOF terminates as target_audio_failed instead of silently degrading to video-only. GET /webrtc/session/{sessionId}/status exposes expected media kinds, the first successful sample time and stable first_media_wait_ms, per-kind last-advance timestamps, generation, cursor, media counters, actual RTP packet/byte and received RTCP packet counters, and bounded sample-write errors. Dropped counters cover negotiated tracks only; session close preserves one final monotonic transport snapshot before storing the terminal status. Both requested kinds must advance before playing; after startup, eight seconds without advancement from any expected kind reports recoverable media_stalled, and every stale kind must advance before recovery. The Console names only the stale expected kinds using server timestamps. Real state transitions emit one structured log with generation, cursor, mode, previous/next state, and a bounded error when present; each overwrite emits one bounded warning with reader identity, exact overwrite count, and recovery action. Feed termination closes and releases the session automatically, while at most 64 terminal status records remain readable for up to two minutes. The tagged Chromium matrix verifies SIP publish to GB28181 receive plus WHEP, GB28181 publish to SIP receive plus WHEP, and WHIP H.264/Opus publish to both SIP and GB28181 receive plus WHEP. It requires expected decoded dimensions, advancing media time, increasing video/audio RTP and decoded-frame counters, connected ICE, and non-stalled server RTP/RTCP status; LIVEFORGE_PROTOCOL_MATRIX_SOAK=60s extends those checks per second without claiming deployment capacity. See the technical risk record; SDP success or an ontrack callback alone is not proof of playback.

WHEP status also exposes source_overwrites. This is the number of source-ring positions lost during recovery and is intentionally separate from dropped_video and dropped_audio, because a mixed source ring cannot attribute each lost position to one media kind. Direct audio pacing is reset at the same recovery boundary; transformed target-audio pacing remains independent.

When source audio is not directly offered by the browser, WHEP prefers Opus and falls back to an offered PCMU/PCMA target when the configured audiocodec runtime can convert it; each answer uses the selected target's RTP format (Opus 48 kHz stereo, G.711 8 kHz mono). The Streams API exposes bounded transcode_tasks diagnostics for active conversion readers.

GB28181: Configure your IP camera's SIP server to point at localhost:5060, or use the built-in simulator:

go run ./tools/gb28181-sim -server 127.0.0.1:5060

Play a Stream

Protocol URL
RTMP rtmp://localhost:1935/live/stream1
RTSP rtsp://localhost:8554/live/stream1
SRT srt://localhost:6000?streamid=subscribe:/live/stream1
HLS http://localhost:8080/live/stream1.m3u8
LL-HLS http://localhost:8080/live/stream1.m3u8 (auto when enabled)
DASH http://localhost:8080/live/stream1.mpd
HTTP-FLV http://localhost:8080/live/stream1.flv
HTTP-TS http://localhost:8080/live/stream1.ts
FMP4 http://localhost:8080/live/stream1.mp4
WebRTC Open console → Preview → WebRTC tab

Pure-audio AAC publishers produce completed HLS, DASH, and LL-HLS segments while the source is still live. For stream key live/audio, inspect the HLS or LL-HLS playlist at /live/audio.m3u8 and a full segment at /live/audio/0.ts or /live/audio/0.m4s; inspect DASH at /live/audio.mpd, with audio init /live/audio/audio_init.mp4 and media /live/audio/a1.m4s. LL-HLS part_duration controls partial segments, while segment_duration controls completed full segments and defaults to 1.0 seconds. Without video keyframes, the first full segment completes near the configured segment target instead of waiting for source shutdown. DASH media fragments retain one continuous relative decode timeline even when the publisher's first DTS is zero.

Web Console

Open http://localhost:8090/console for the real-time management dashboard. Preview URLs use the active HTTP/WebRTC listener reported by the server. If another process (for example nginx or a local helper) owns 127.0.0.1:8080, RTMP and WHEP can work while HTTP-FLV/HLS/DASH/FMP4 preview requests receive that process's 404; release the port or set http_stream.listen to an unused address.

The tabs, in order, are Streams, GB28181, Config, Cluster, SIP Calls, Storage, and Security. Recent Audit is a surface inside Security, not a separate tab. The visual groups are Workspace (Streams, GB28181, SIP Calls, Storage), Operations (Cluster), and System (Config, Security). When the API listener uses TLS, console login issues the HttpOnly, SameSite=Strict lf_session cookie with Secure; the local plain-HTTP listener leaves Secure unset.

  • Live stream list with state, codecs, bitrate, FPS, and visible on-demand audio transcode tasks (source -> target, state, subscriber count, and bounded errors)
  • GOP Cache visualization with keyframe-driven generation, interleaved video/audio frame counts, and duration; audio-only streams show Not applicable (audio-only). Selected-stream trends retain a continuous 60-second window across ordinary GOP rotations and reset only for a publisher or counter reset
  • Multi-protocol preview player (HTTP-FLV, WS-FLV, HTTP-TS, FMP4, HLS, DASH, WebRTC realtime, and WebRTC Live)
  • WebRTC publish with camera/mic and outbound stats
  • Permission-aware stream kick/delete and runtime config refresh
  • Cluster relay/peer status and SIP call dial/detail/hangup
  • Recording metadata/download/inline-play/delete, DVR session/storage status and online HLS preview, security posture, and bounded audit events
  • Recording stream_pattern matches the complete stream key. Ordinary GB28181 inbound keys are {gb28181.stream_prefix}/{channel_id}, so use gb28181/* (or *) to include them. Recording paths support ${HOME} in trusted config and ~/...; they are resolved before storage creation and the resolved root is exposed in recording status. Named-user tilde paths are rejected, and a zero-codec generation-bound session waits for GB28181 media headers before consuming codec-discovered frames; declared-codec publishers can start before sequence headers arrive.
  • WHEP preview starts asynchronously received media muted when browser autoplay policy requires it and exposes an Unmute/Mute control without dropping the audio track
  • Complete redacted Config document/schema display, read-only Validate, source-aware Apply & Refresh, and writable/read-only status for file, HTTP/HTTPS, Consul, and Redis
  • SIP and GB28181 local protocol Test Lab results, including unavailable-module states; both provider sessions can publish or receive persistent H.264 plus G.711 loopback media, show per-track RTP/RTCP/PS counters, stop cleanly, and preview through the available output protocols

DVR playlist and segment GETs run synchronous subscribe authorization hooks only; they do not emit asynchronous subscribe lifecycle events. Finite DVR playlist and segment responses use a 10-second server write bound. Every admitted success, error, canceled, or timed-out request releases exactly one global connection slot; range requests and ServeContent metadata are unchanged. Recording preview uses the authenticated management API session. DVR preview uses the separate dvr.listen HLS listener with non-credentialed CORS, so its subscribe authorization still applies; the Console does not persist or append bearer tokens.

Configuration

The Console supports English and Chinese, paginated lists, and configuration drafts that survive background refreshes and reauthentication. Config offers common controls alongside the full YAML editor, a redacted comparison, and history/rollback. Apply detects concurrent edits with If-Match and returns409 without overwriting a changed document. History is limited to32 documents/16MiB in the current process and resets on restart. Audit supports filters and NDJSON export and optional bounded private disk persistence on Linux and macOS via api.audit.path. Common server.name is read-only; the diff labels immutable, restart-required, and hot-reload changes. Drafts and credentials are not stored in browser storage; only the language preference persists. See runtime config operations and the bounded regression gate.

LiveForge uses a bootstrap YAML configuration plus an optional runtime source. See configs/liveforge.yaml for the full reference. The Config page displays the complete redacted effective/desired document and schema, validates candidates, and applies them through file, HTTP/HTTPS, Consul, or Redis when the source is writable; read-only sources return 409. See docs/recipes/runtime-config-sources.md.

The checked-in sample is for local development only: it disables TLS and authentication and uses admin/admin. Never expose it publicly unchanged.

Key sections:

Section Purpose
rtmp RTMP ingest/playback (default :1935)
rtsp RTSP ingest/playback with TCP + UDP (default :8554)
http_stream HLS, LL-HLS, DASH, HTTP-FLV, HTTP-TS, FMP4, WebSocket (default :8080); http_stream.llhls.segment_duration controls completed LL-HLS segments
webrtc WHIP/WHEP with ICE servers and UDP port range (default :8443)
srt SRT ingest/playback with AES encryption (default :6000)
sip SIP signaling server and local SIP Gateway lab (default :5060)
gb28181 GB28181 device management, RTP port range, keepalive, auto-invite
audio_codec Enable/disable on-demand audio transcoding
api REST API and web console (default :8090)
auth JWT and HTTP callback authentication
record FLV/FMP4/MP4/TS/HLS recording, segmentation, completion callback
dvr Time-shift segments, retention window, storage and session status
notify HTTP webhook and WebSocket notifications
cluster Multi-protocol forwarding and origin pull with scheduler
metrics Prometheus metrics endpoint (default :9090)
limits Global connection, stream, and subscriber limits
tls TLS certificate and key for HTTPS/secure protocols
stream GOP cache and per-GOP frame/duration/byte bounds, ring buffer, idle timeout, slow consumer, feedback; WHIP RID Simulcast and WHEP session-time layer selection
runtime Background configuration refresh source: file, HTTP/HTTPS, Consul, or Redis

Trusted bootstrap/runtime source loading supports environment variable expansion such as ${API_TOKEN} and ${AUTH_JWT_SECRET}. Viewer-facing Config Validate never expands the server process environment: it treats references literally, accepts exactly one YAML/JSON document, and rejects unknown root or nested typed fields. Config Apply and trusted runtime source loading remain permissive for source fields not mapped by the typed runtime struct.

Runtime configuration refresh

The bootstrap file is loaded once. A background manager then polls the selected runtime.source and atomically publishes validated snapshots. Application reads use the in-memory snapshot only, so they never block on file or network I/O. Source loads, Config Apply writes, and source close are serialized; Apply waits for the source write before returning 202 with written_and_refresh_scheduled and schedules parsing/application/publication asynchronously. Every file, HTTP/HTTPS, Consul, and Redis source defaults to a 4 MiB complete-document/materialization limit, configurable with runtime.<source>.max_bytes; Redis hash reads prefer HSCAN NOVALUES and use bounded HKEYS fallback only for older servers. Flattened Consul/Redis leaves infer only safe booleans, nulls, canonical decimal integers, and finite decimal/exponent floats; leading-zero identifiers, durations, out-of-range values, and YAML-looking strings remain strings. Dotted/slashed flattened paths are canonicalized and sorted; duplicate paths and scalar/container prefix collisions fail closed deterministically. The Config page shows the complete versioned JSON Schema and retains raw desired source YAML, including comments and fields not represented by the typed runtime struct. Its redacted document preserves collection shape: opaque structured sensitive values retain only explicit stable identity fields such as id, name, username, channel_id, and device_id; valid absolute hierarchical URL scalars are recognized by value even under unmapped non-URL-shaped keys and retain safe scheme/host/port identity while replacing every non-root path with a stable opaque digest marker and removing userinfo/query/fragment. URL-shaped keys retain TURN/opaque, malformed/hostless fail-closed, and plain-address handling; ordinary strings, durations, IDs, and bare host/address values remain unchanged outside that key policy. Ambiguous restoration fails closed. Source failures retain the last valid snapshot. For HTTP sources, the selected http or https source must match the URL scheme, redirects are disabled, and ETag/Last-Modified validators advance only after a document is accepted; X-Config-Version is separate version metadata. Consul KV GET and PUT also reject redirects without dispatching to the target, so X-Consul-Token is never forwarded. SIGHUP and POST /api/v1/server/config/refresh schedule asynchronous refresh; listener/module/TLS/port changes are reported as restart-required and are not partially applied. Status and Prometheus expose accepted, rejected, application-failed, callback-failed, coalesced callback, and pending-restart state. See docs/recipes/runtime-config-sources.md for file, HTTP, HTTPS, Consul, Redis, Config Validate, and Config Apply examples.

File Apply creates new targets with private mode 0600 and preserves the existing file's permission bits during atomic replacement. Redis Apply writes the document and optional version increment in one MULTI/EXEC transaction; transaction errors are returned rather than producing a false success. The refresh response is 202 with status: scheduled, while a successful Apply is 202 with status: written_and_refresh_scheduled. The Console tracks a monotonic editor revision so a newer local edit cannot be overwritten by a stale desired snapshot after Apply.

After a successful Apply write, the server keeps a pending desired overlay in memory until the source refresh accepts the matching content. GET /api/v1/server/config/document therefore shows the submitted desired document across an immediate page reload while the effective document remains the last applied snapshot.

Operators can inspect the redacted loader state at GET /api/v1/server/config (protected by the normal API authentication rules).

Testing Tools

lf-test CLI

A comprehensive integration testing tool (tools/lf-test) for validating all server features:

# Push test (supports: rtmp, rtsp, srt, whip, gb28181)
go run ./tools/lf-test push --protocol rtmp --target rtmp://localhost:1935/live/test --realtime

# Play test (supports: rtmp, rtsp, srt, whep, httpflv, wsflv, hls, llhls, dash)
go run ./tools/lf-test play --protocol hls --url http://localhost:8080/live/test.m3u8

# Cluster topology test (auto-launches multi-node cluster)
go run ./tools/lf-test cluster \
  --topology origin-edge \
  --relay-protocol srt \
  --push-protocol rtmp \
  --play-protocol hls

# Auth test
go run ./tools/lf-test auth --target rtmp://localhost:1935/live/test --token <jwt>

All commands support --assert expressions and --output json for CI integration.

gb28181-sim

See GB28181 Device Simulator above.

Project Structure

liveforge/
├── cmd/liveforge/       # Entry point
├── config/              # YAML config loader
├── core/                # Server, Stream, EventBus, StreamHub, MuxerManager, TranscodeManager
├── module/
│   ├── api/             # REST API + web console
│   ├── auth/            # JWT / HTTP callback auth
│   ├── cluster/         # Multi-protocol forwarding + origin pull (RTMP/SRT/RTSP/RTP/GB28181)
│   ├── gb28181/         # GB28181 protocol (SIP signaling, device registry, invite, PTZ, playback, alarm)
│   ├── httpstream/      # HLS, LL-HLS, DASH, HTTP-FLV, HTTP-TS, FMP4, WebSocket
│   ├── metrics/         # Prometheus metrics endpoint
│   ├── notify/          # HTTP webhook + WebSocket notifications
│   ├── dvr/             # Time-shift segment storage and playback
│   ├── record/          # FLV/FMP4/MP4/TS/HLS recording and storage management
│   ├── rtmp/            # RTMP protocol (handshake, chunks, AMF0)
│   ├── rtsp/            # RTSP protocol (TCP + UDP transport)
│   ├── sip/             # SIP transport layer (used by GB28181)
│   ├── sipgateway/      # Inbound/outbound SIP media gateway and call control
│   ├── srt/             # SRT protocol (via datarhei/gosrt)
│   └── webrtc/          # WebRTC WHIP/WHEP + GCC (via pion/webrtc)
├── pkg/
│   ├── audiocodec/      # Audio transcode: FFmpeg-backed decode/encode/resample (AAC, Opus, G.711, MP3)
│   ├── avframe/         # Audio/video frame types
│   ├── codec/           # H.264, H.265, AAC, AV1, Opus, MP3 parsers
│   ├── logger/          # Structured logging
│   ├── muxer/           # FLV, TS, FMP4, MPEG-PS muxers and demuxers
│   ├── portalloc/       # Port range allocator for RTP
│   ├── ratelimit/       # Per-IP token bucket rate limiter
│   ├── rtp/             # Full RTP/RTCP stack with 12+ codec packetizers
│   ├── sdp/             # SDP parser and builder
│   └── util/            # Lock-free SPMC ring buffer
├── tools/
│   ├── gb28181-sim/     # GB28181 device simulator
│   ├── lf-test/         # Integration test CLI (push, play, auth, cluster)
│   └── testkit/         # Reusable test components (push, play, cluster, analyzer, report)
└── test/integration/    # End-to-end integration tests

Testing

The repository has a quick package check and a complete FFmpeg-backed test suite:

go test ./...
CGO_ENABLED=1 go test -tags audiocodec -race -coverprofile=coverage.out -covermode=atomic ./...

The first command skips FFmpeg-tagged transcoding integration tests. The second command is the complete suite and requires Go 1.26 and FFmpeg development libraries.

Comparison

Feature LiveForge MediaMTX SRS Monibuca
Language Go Go C++ Go
RTMP Yes Yes Yes Yes
RTSP Yes (TCP+UDP) Yes Yes Plugin
SRT Yes (pure Go) Yes Yes Plugin
WebRTC WHIP/WHEP Yes Yes Yes Plugin
HLS/DASH Yes Yes Yes Plugin
LL-HLS Yes (fMP4 + blocking reload) No Yes No
HTTP-FLV Yes No Yes Plugin
FMP4 streaming Yes No No No
GB28181 Yes (full SIP + live/playback/PTZ) No Yes Plugin
Audio transcoding Optional (tagged FFmpeg build) No Yes Plugin
Cluster relay Yes (RTMP/SRT/RTSP/RTP/GB28181) No Yes Plugin
Web console Yes (built-in) No Yes Yes
Browser publish Yes (WHIP) No No No
Auth (JWT + callback) Yes Yes Yes Plugin
Recording Yes (FLV/FMP4/MP4/TS/HLS) Yes Yes Plugin
Webhooks Yes (HMAC-signed) No Yes No
ICE Lite Yes No No No
Prometheus metrics Yes No Yes Plugin
GCC congestion control Yes No No No
Testing tools Yes (lf-test CLI + GB28181 sim) No No No
Single binary Yes Yes Yes No
License MIT MIT MIT MIT

Documentation

📖 Full documentation is on the GitHub Wiki.

For coding agents, start with AGENTS.md, agent-manifest.json, and llms.txt. The API contract, configuration schema, and runnable recipes are kept in docs/ and checked by CI.

Operational recipes: runtime config, authentication/TLS, recording/DVR, SIP Gateway, SIP/GB28181 protocol test lab, cluster relay, RBAC/audit, and release verification.

Topic EN 中文
Home Wiki Home Wiki 首页
Audio Transcoding Audio Transcoding 音频转码
GB28181 Guide GB28181 GB28181 指南
Cluster Deployment Cluster Deployment 集群部署
LL-HLS LL-HLS 低延迟 HLS
Testing Tools Testing Tools 测试工具
Configuration Reference Configuration 配置参考
REST API REST API REST API

Roadmap

  • TLS / HTTPS
  • SRT protocol
  • Multi-protocol cluster relay (RTMP, SRT, RTSP, RTP, GB28181)
  • WebRTC ICE Lite
  • WebSocket notifications
  • Prometheus metrics
  • LL-HLS (partial segments + blocking reload)
  • Slow consumer protection (EWMA frame dropping)
  • GCC congestion control for WebRTC
  • Rate limiting
  • GB28181 (SIP + live + playback + PTZ + alarm)
  • Optional audio transcoding (AAC, Opus, G.711, MP3; tagged FFmpeg build)
  • SIP gateway
  • Permission-aware seven-view management console
  • Recording/DVR, cluster, security, and audit management APIs, including Storage online preview
  • WHIP Simulcast with up to three isolated RID layers, WHEP session-time selection, and local pause of unused noncanonical video processing; see configuration and boundaries

License

MIT — Copyright (c) 2026 Pingos

About

High-performance multi-protocol live streaming server in Go — RTMP, RTSP, SRT, WebRTC (WHIP/WHEP), HLS, LL-HLS, DASH, HTTP-FLV, WebSocket-FLV, FMP4, with built-in web console

Topics

Resources

Stars

19 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages