How to Build Browser Live Streaming for Small Projects with WebRTC and SRS

This article shows how seemingly complex real‑time video requirements can be satisfied cheaply by combining the open‑source WebRTC stack with the lightweight SRS media server, detailing the architecture, step‑by‑step signaling, full JavaScript code for publishing and playing, and the resulting end‑to‑end live‑streaming solution.

xkx's Tech General Store
xkx's Tech General Store
xkx's Tech General Store
How to Build Browser Live Streaming for Small Projects with WebRTC and SRS

Why a lightweight solution works for small projects

Clients often request real‑time video features that appear costly, but by using the open‑source WebRTC stack together with the Simple Realtime Server (SRS) the functionality can be delivered with minimal budget and development time.

WebRTC basics

WebRTC (Web Real‑Time Communication) is a Google‑led open‑source protocol that enables browsers to capture, encode and transmit audio/video without plugins. End‑to‑end latency is typically 50‑300 ms, making it suitable for live streaming and conference‑screen‑sharing scenarios.

SRS overview

SRS (Simple Realtime Server) is a lightweight, high‑performance, open‑source media server widely used in Chinese small‑project live‑streaming cases. It handles media reception, caching, and one‑to‑many distribution, matching the low‑cost, fast‑delivery needs of small projects.

Architecture

The data flow is: Browser (publisher) → SRS server (relay) → Multiple browsers (players). This chain guarantees real‑time delivery while avoiding custom client development.

Publishing workflow

Three WebRTC signaling steps are performed:

Create an SDP Offer from the browser.

Receive the SDP Answer from SRS.

Open a WebRTC data channel and start pushing the media stream.

Publishing code

The core JavaScript function startPublish obtains a screen‑capture stream, creates an RTCPeerConnection, adds tracks, forces H.264, gathers ICE candidates, sends the SDP to SRS via a POST request, sets the remote description and starts streaming.

async function startPublish() {
    // 1. Get media stream (screen capture)
    const stream = await navigator.mediaDevices.getDisplayMedia({
        video: {cursor: "always", width: 1920, height: 1080, frameRate: 30},
        audio: false
    });
    // 2. Create RTCPeerConnection
    const pc = new RTCPeerConnection({iceServers: [], bundlePolicy: "max-bundle", rtcpMuxPolicy: "require"});
    // 3. Add tracks
    stream.getTracks().forEach(track => pc.addTrack(track, stream));
    // 4. Create Offer (send only)
    const offer = await pc.createOffer({offerToReceiveAudio: false, offerToReceiveVideo: false});
    // 5. Prefer H.264 for SRS compatibility
    offer.sdp = preferH264(offer.sdp);
    await pc.setLocalDescription(offer);
    // 6. Wait for ICE gathering
    const completeSdp = await waitForIceGathering(pc);
    // 7. Send signaling to SRS
    const response = await fetch("https://192.168.60.128:1990/rtc/v1/publish/", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({
            api: "https://192.168.60.128:1990/rtc/v1/publish/",
            streamurl: "webrtc://192.168.60.128:1985/live/livestream",
            sdp: completeSdp
        })
    });
    const result = await response.json();
    // 8. Set remote description (Answer)
    await pc.setRemoteDescription({type: "answer", sdp: result.sdp});
    console.log("Publishing connection established");
}

Pulling workflow

The player creates an RTCPeerConnection, adds a recvonly transceiver for video (and audio), creates an Offer, sends it to SRS, receives the Answer, and attaches the incoming stream to a <video> element.

async function startPlay() {
    const pc = new RTCPeerConnection({iceServers: [], bundlePolicy: "max-bundle", rtcpMuxPolicy: "require"});
    pc.addEventListener("track", event => {
        if (event.streams && event.streams[0]) {
            const videoEl = document.getElementById("remoteVideo");
            videoEl.srcObject = event.streams[0];
            videoEl.play();
        }
    });
    pc.addEventListener("connectionstatechange", () => {
        console.log("Connection state:", pc.connectionState);
    });
    pc.addTransceiver("video", {direction: "recvonly"});
    pc.addTransceiver("audio", {direction: "recvonly"});
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    const completeSdp = await waitForIceGathering(pc);
    const response = await fetch("https://192.168.60.128:1990/rtc/v1/play/", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({
            api: "https://192.168.60.128:1990/rtc/v1/play/",
            streamurl: "webrtc://192.168.60.128:1985/live/livestream",
            sdp: completeSdp
        })
    });
    const result = await response.json();
    if (result.code !== 0) throw new Error(`SRS error: ${result.error || result.code}`);
    await pc.setRemoteDescription({type: "answer", sdp: result.sdp});
    console.log("Pulling connection established");
}

HTML player element

<video id="remoteVideo" autoplay playsinline controls></video>

Result

The combined WebRTC + SRS solution provides a complete end‑to‑end pipeline: browser publisher → SRS relay → multiple browser viewers, delivering low‑latency live video without additional client binaries and with minimal deployment effort.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Live StreamingBrowserReal‑time communicationWebRTCSRS
xkx's Tech General Store
Written by

xkx's Tech General Store

Code with the left hand, enjoy with the right; a keystroke sweeps away worries.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.