MJPEG Stream Viewer

Install?

A raw MJPEG stream (multipart/x-mixed-replace, what bench cams, printer cams and camera boxes emit) opened straight in a Hydrogen webview paints at its native size with scrollbars. This viewer proxies the stream through the container so an http camera on the LAN shows inside https Hydrogen, fits the frame to the pane with the aspect kept, caps the frame rate so a 4K 30 fps source does not flood the browser, shows fps and bandwidth, and saves a snapshot to the project. Ships a dock manifest and takes ?url= so a shared Hydrogen workspace can open a camera in a pane.

adom-wiki pkg install adom/mjpeg-stream-viewer

Latest: v0.1.2, published

Contents

README

markdown

MJPEG Stream Viewer

Give it a camera URL and the raw stream turns into a pane that fits.

A bench cam, a printer cam or a camera box usually serves a raw MJPEG stream (multipart/x-mixed-replace). Opened straight in a Hydrogen webview that stream paints at its native size, so a 4K camera shows one corner of the frame behind two scrollbars, and an http camera on the LAN cannot show inside https Hydrogen at all. This viewer fixes both: it proxies the stream through the container (same origin, so the browser is happy), fits the picture to the pane with the aspect kept, caps the frame rate so a 30 fps 4K source does not flood the browser, shows fps, bandwidth and frame size, and saves a snapshot to the project with one click.

The viewer fitting a 4K bench cam into a Hydrogen pane

Use it

adom-wiki pkg install adom/mjpeg-stream-viewer
mjpeg-stream-viewer --ai-thread "<your thread>" show --url http://192.168.0.34:3000/stream

show starts the server if needed and opens the viewer in a Hydrogen webview tab in a work pane (never the VS Code pane). Or start it yourself and open the URL it prints:

mjpeg-stream-viewer --ai-thread "<your thread>" serve --print-url --port auto --url http://<camera>/stream

The page takes ?url=<stream>, ?fps=<n> and ?bare=1. Paste a URL in the bar and press Show; Save source keeps it in the dropdown for next time; Fit keeps the whole frame, Fill crops to the pane; Snapshot writes ~/project/screenshots/mjpeg-<timestamp>.jpg. Bare mode (--bare on show, ?bare=1, the corner button on hover, or the b key) hides the header and the bar so the pane is nothing but the picture; it is remembered per browser.

Bare mode: nothing but the picture

Settings: frame-rate cap, default view, idle shutdown

What it does to the stream

  • Proxies it: GET /stream?url=<camera>&fps=<n> fetches the camera from the container and re-serves it same-origin. Only http and https sources; the viewer refuses to proxy itself.
  • Caps the rate: frames above the cap are dropped in the proxy, so the browser sees at most fps frames per second. Default 10; a 4K source at 30 fps is about 11 MB/s raw, and 5 fps of it is about 2 MB/s.
  • Reads the frame size from the JPEG itself and shows it in the status pills, with a hint when the source is larger than the pane can use.
  • GET /snapshot.jpg?url=<camera> returns one frame; POST /api/snapshot { aiThread, url } saves one.
  • A camera that serves a still image instead of a stream works too: the image is fetched once per show.

Status, settings, lifecycle

GET /api/status is the dock's LED source: source, frames, size, each with a state and a hint. Settings persist on the server (GET/POST /settings: maxFps, fit, ttlHours, sources). State-changing calls need the ai-thread name (--ai-thread on the CLI, aiThread in a raw body); anonymous ones are refused with caller_identity_required. The server stops itself after 24 hours idle (--ttl <hours>, --keep-alive), registers itself under ~/.adom/instances/mjpeg-stream-viewer/, and ls lists live instances; stop shuts one down politely.

In a shared workspace

The page ships dockbar.json, so it launches from the dock and a shared Hydrogen workspace can open a camera in a pane:

{ "panel": "app", "app": "adom/mjpeg-stream-viewer", "displayName": "Bench cam", "args": { "url": "http://192.168.0.34:3000/stream", "fps": "5" } }

Limits

A viewer, not a recorder. One source per pane. MJPEG and stills only (no HLS, no WebRTC). The camera must be reachable from the container: true on the desktop and in the office, not true for a cloud container on another network, and the status pill says so.