Live stream

Broadcast from browser, OBS, or FFmpeg. Viewers watch via HLS (~5–12s) or WHEP (sub-2s).

1. Create a stream

From your backend:

POST https://kardocloud.com/v1/me/streams
Authorization: Bearer kl_live_xxx
Content-Type: application/json
{
  "name": "Order #1234 walkthrough",
  "mode": "LOW_LATENCY"   // REALTIME | LOW_LATENCY | BROADCAST
}
→ {
  "id": "stream_abc",
  "name": "Order #1234 walkthrough",
  "mode": "LOW_LATENCY",
  "status": "IDLE",
  "keys": [{ "id": "key_x", "key": "kl_a1b2c3...", ... }],
  ...
}

The keys[0].key is your stream key. Anyone with it can publish — guard it.

ModePipelineLatency targetUse case
REALTIMEWebRTC only, no HLS< 300 msInteractive — calls, conferences
LOW_LATENCYWebRTC + LL-HLS~2 s (WHEP), ~3 s (LL-HLS)Live shopping, sports, interactive broadcast
BROADCASTRTMP in + HLS out5–12 sOBS streamers, large audience, archive

2. Publish from OBS

In OBS → Settings → Stream:

Click Start Streaming. After ~5 seconds HLS is up at https://kardocloud.com/live/<key>.m3u8.

3. Publish from FFmpeg

Useful for automated test publishers or backend video processing pipelines:

ffmpeg -re \
  -f lavfi -i "testsrc2=size=1280x720:rate=30,drawtext=text='%{localtime}':fontsize=32:fontcolor=white:x=20:y=20" \
  -f lavfi -i "sine=frequency=1000" \
  -c:v libx264 -preset veryfast -tune zerolatency \
     -profile:v baseline -pix_fmt yuv420p \
     -g 60 -keyint_min 60 -sc_threshold 0 \
     -b:v 2500k -maxrate 2500k -bufsize 5000k \
  -c:a aac -b:a 128k -ar 48000 -ac 2 \
  -f flv rtmps://ingest.kardocloud.com:443/live/<your-stream-key>

4. Publish from browser (WHIP)

Use this when your users go live directly from the web — no OBS needed.

// 1) get a fresh stream key from your backend (skip if you already have one)
const { keys } = await fetch('/api/your-app/streams/' + streamId).then((r) => r.json());
const streamKey = keys[0].key;

// 2) acquire camera + mic
const stream = await navigator.mediaDevices.getUserMedia({
  video: { width: 1280, height: 720, frameRate: 30 },
  audio: { echoCancellation: true, noiseSuppression: true },
});

// 3) PeerConnection — prefer H.264 so the SRS bridge to HLS works
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
stream.getTracks().forEach((t) => pc.addTrack(t, stream));

// IMPORTANT: prefer H.264 — without this, Chrome offers VP8 first and the
// HLS pipeline ends up audio-only (it can't transcode VP8 in real time).
for (const tx of pc.getTransceivers().filter((t) => t.sender.track?.kind === 'video')) {
  const caps = RTCRtpSender.getCapabilities('video');
  if (!caps) continue;
  const h264 = caps.codecs.filter((c) => /H264/i.test(c.mimeType));
  const others = caps.codecs.filter((c) => !/H264/i.test(c.mimeType));
  if (h264.length) tx.setCodecPreferences([...h264, ...others]);
}

// 4) Create offer + WHIP POST
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
await new Promise((r) => {
  if (pc.iceGatheringState === 'complete') return r();
  setTimeout(r, 2000);
  pc.addEventListener('icegatheringstatechange', () => pc.iceGatheringState === 'complete' && r());
});

const r = await fetch(
  `https://kardocloud.com/rtc/v1/whip/?app=live&stream=${encodeURIComponent(streamKey)}`,
  { method: 'POST', headers: { 'Content-Type': 'application/sdp' }, body: pc.localDescription.sdp },
);
if (!r.ok) throw new Error('WHIP ' + r.status);

await pc.setRemoteDescription({ type: 'answer', sdp: await r.text() });
// pc is now publishing. Watch pc.iceConnectionState for 'connected'.

Kardolive ships a ready-made <WhipPublisher streamKey={...} /> React component in the customer-portal source — clone it into your app as-is.

5. Sharing with viewers

Three options ranked by effort:

Option A — public viewer page (no code)

https://kardocloud.com/watch/?key=<streamKey>&title=<optional+title>

Anyone with the URL can watch. No Kardolive account needed. The page shows a friendly "waiting for broadcaster" state when offline and auto-connects when you go live.

Option B — embed iframe

<iframe
  src="https://kardocloud.com/embed/player.html?src=https://kardocloud.com/live/<key>.m3u8"
  width="640" height="360"
  allowfullscreen frameborder="0">
</iframe>

Option C — embed in your own React/JS code

Use the useLiveStream hook from React quickstart, or hls.js directly.

6. Embed in another website

Combined chat + live = use a scenario:

https://kardocloud.com/scenarios/shopping/?room=<streamKey>&hlsKey=<streamKey>
https://kardocloud.com/scenarios/webinar/?room=<streamKey>&hlsKey=<streamKey>
https://kardocloud.com/scenarios/karaoke/?room=<streamKey>&hlsKey=<streamKey>

Drop these in an iframe, or just give people the URL.

7. Watch via HLS

HLS works in any player — Safari natively, hls.js for other browsers, VLC, ffplay, OBS as a source, ExoPlayer on Android, AVPlayer on iOS.

https://kardocloud.com/live/<streamKey>.m3u8

~5–12 second delay. Universal compatibility. CDN-fronted (CloudFront).

8. Watch via WHEP (sub-2s)

For low-latency viewers (live shopping decisions, interactive Q&A):

// browser
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
pc.addTransceiver('audio', { direction: 'recvonly' });
pc.addTransceiver('video', { direction: 'recvonly' });
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

const r = await fetch(
  `https://kardocloud.com/rtc/v1/whep/?app=live&stream=${encodeURIComponent(streamKey)}`,
  { method: 'POST', headers: { 'Content-Type': 'application/sdp' }, body: pc.localDescription.sdp },
);
const answerSdp = await r.text();
await pc.setRemoteDescription({ type: 'answer', sdp: answerSdp });

pc.ontrack = (e) => { videoEl.srcObject = e.streams[0]; };

Kardolive's customer-portal ships a <WhepPlayer streamKey={...} /> React component — copy that into your app.

9. Recording → VOD

Start a server-side recording while the stream is live:

POST https://kardocloud.com/v1/me/streams/<streamId>/recordings/start
Authorization: Bearer kl_live_xxx
→ { "recordingId": "rec_xxx", "status": "PROCESSING", "startedAt": "..." }

Stop it:

POST https://kardocloud.com/v1/me/streams/<streamId>/recordings/<rec>/stop

Once finalized, get the playback URL:

GET https://kardocloud.com/v1/me/recordings/<rec>/playback-url
→ { "ready": true, "url": "https://<signed>...", "format": "mp4", "expiresIn": 900 }

You can also subscribe to the recording.ready webhook to be notified async.

10. Adding live chat alongside the stream

Create a stream-type chat channel and use the stream ID as externalRef. See Chat → stream channels.

// after creating the stream:
POST /v1/me/channels
{ "type": "stream", "name": "Live Q&A", "externalRef": "<streamId>" }

The scenarios automatically connect chat when you pass ?room=<streamId> — they look up the matching channel by externalRef.

Next

Conference → · Calls → · Webhooks →