Stream video end-to-end
This guide covers sending live video from a device via the Python SDK and consuming it through the WebSocket API — device to browser.
Step 1 — Send frames from the device
Install the video extras (OpenCV headless + Pillow + NumPy):
pip install "plexus-python[video]==0.7.1"Frame by frame
Use send_video_frame() when you need frame-level control — preprocessing, annotation, or custom capture logic:
import cv2
from plexus import Plexus
px = Plexus(api_key="YOUR_API_KEY", source_id="robot-01")
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if ret:
px.send_video_frame(frame=frame, camera_id="front")From a URL
Use stream_camera() to pull from an RTSP feed, HLS stream, or any URL OpenCV supports. Requires FFmpeg on $PATH:
stop = px.stream_camera(url="rtsp://192.168.1.10/stream", camera_id="front")
# ... do other work ...
stop.set() # stop streamingBoth methods JPEG-encode at quality=85 by default. Frames over ~750 KB are adaptively re-encoded; the gateway hard limit is 1 MB.
Step 2 — Open the video stream
Connect to the video WebSocket, send an auth frame, then receive frames:
const ws = new WebSocket(
"wss://api.plexus.company/v1/sources/robot-01/video/stream?camera_id=front"
);
ws.onopen = () => {
ws.send(JSON.stringify({ type: "auth", api_key: "plx_..." }));
};
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "init") {
// lists which cameras are currently online
console.log(msg.online_sources);
}
if (msg.type === "video_frame") {
// msg.frame — base64 JPEG
// msg.camera_id, msg.width, msg.height, msg.timestamp
}
};The server sends one init frame immediately after auth, then video_frame frames as they arrive. The auth frame must be the first frame you send — the socket closes with 4001 if it doesn’t arrive within 10 seconds.
Step 3 — Render in a browser
Draw each frame onto a <canvas>:
<canvas id="feed"></canvas>const canvas = document.getElementById("feed");
const ctx = canvas.getContext("2d");
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "video_frame") {
const img = new Image();
img.onload = () => {
canvas.width = msg.width;
canvas.height = msg.height;
ctx.drawImage(img, 0, 0);
};
img.src = `data:image/jpeg;base64,${msg.frame}`;
}
};Notes
- Live relay only — no history, no replay. Frames are dropped silently if the consumer can’t keep up.
- Multiple cameras — repeat the param:
?camera_id=front&camera_id=rear. - Timeout — add
?timeout=60to have the server close after 60 s (code4008). Reconnect to continue.
See Live streams for the full frame reference and close codes.