View on GitHub

raspberry_ninja

Publish or capture VDO.Ninja streams with Python (Raspberry Pi, Linux, Mac, Windows WSL)

Recording guide

Recording is a receive operation. --record and --record-room pull remote VDO.Ninja streams to disk; they do not publish the local camera.

Single-stream recording

python3 -u publish.py \
  --record STREAM_ID \
  --password false

Audio is recorded by default. Add --noaudio for video only.

The file container follows the negotiated codec:

Incoming media Normal recording output Processing
H.264 video .ts MPEG-TS Depayload and remux
VP8 or VP9 video .webm WebM WebM-compatible path
Opus audio separate _audio.webm WebM Depayload and remux

Filenames include stream identifiers and timestamps. Read the startup log for the exact paths; do not assume that a user-supplied .webm suffix survives when the selected video container is MPEG-TS.

Stop with Ctrl+C and let GStreamer finalize the files. A non-empty file is not proof of a valid recording; validate it as described below.

Room recording

Record every participant in a room:

python3 -u publish.py \
  --room ROOM_NAME \
  --record-room \
  --password false

Limit recording to selected stream IDs:

python3 -u publish.py \
  --room ROOM_NAME \
  --record-room \
  --record-streams "camera-one,camera-two" \
  --password false

Each participant is handled separately. Expect codec-appropriate video files and separate audio files rather than one mixed room file.

HLS recording

The validated compatibility path is the splitmux backend:

python3 -u publish.py \
  --record STREAM_ID \
  --hls --hls-splitmux \
  --password false

This produces an .m3u8 playlist and numbered .ts MPEG-TS segments. H.264 is used for video and AAC for audio; non-H.264 input is transcoded and can be expensive on small boards.

Serve the current recording directory with the built-in server:

python3 -u publish.py \
  --record STREAM_ID \
  --hls --hls-splitmux \
  --webserver 8080 \
  --password false

Open the playlist named in the log through port 8080. Browser-native HLS support varies; the included tools/play_hls.html can be used with the helper server when needed.

--hls without --hls-splitmux keeps the older manual backend for platform compatibility. It produced empty segments in testing on GStreamer 1.18, so it is not the recommended general-purpose path. Do not remove it without testing the platforms that still depend on it.

Validate recordings

Identify the real container:

gst-typefind-1.0 recording.ts
ffprobe -hide_banner recording.ts
ffprobe -hide_banner recording_audio.webm

Decode without requiring a display or speaker:

gst-launch-1.0 -q filesrc location=recording.ts ! decodebin ! fakesink
gst-launch-1.0 -q filesrc location=recording_audio.webm ! decodebin ! fakesink

For HLS, confirm all three layers:

test -s recording.m3u8
grep -v '^#' recording.m3u8
gst-typefind-1.0 recording_00000.ts
gst-launch-1.0 -q filesrc location=recording_00000.ts ! decodebin ! fakesink

The segment must type-find as MPEG-TS and the playlist must reference existing, non-empty segments.

Combine separate audio and video

Install FFmpeg, then combine a known pair:

python3 tools/combine_recordings.py video_file audio_file combined.mp4

With no arguments, the tool scans the current directory for timestamp-matched pairs. It recognizes current H.264 .ts, VP8/VP9 .webm/.mkv, and _audio.webm outputs, plus legacy _audio.wav and _audio.ts files:

python3 tools/combine_recordings.py

The tool re-encodes video to H.264 and audio to AAC while compensating for differing stream start timestamps. Keep the originals until the combined file has been inspected and decoded successfully.

Resource planning

Direct H.264/Opus remuxing is much lighter than decoding and transcoding. HLS conversion, VP8/VP9 re-encoding, several room participants, or simultaneous publishing can exceed a Pi Zero 2 W’s practical memory and CPU budget. Test one stream first and monitor:

watch -n 2 'free -h; ps -o pid,rss,%cpu,%mem,etime,cmd -C python3'

Also monitor disk space and write rate. An 8 GB card is suitable for the runtime and short tests, not unattended long-term recording.