pi-webrtc

Camera and Encoding

Camera Backends

--camera takes a <backend>:<id> string. Which backends exist depends on the platform the binary was built for, which CMake detects from /etc/nv_tegra_release or /usr/bin/raspi-config.

BackendValueRaspberry PiJetsonNotes
Libcameralibcamera:<id>✅❌The officially recommended way to read a CSI camera on a Pi.
Libarguslibargus:<id>❌✅NVIDIA's CSI camera stack, with EGL output.
V4L2v4l2:<id>✅✅USB cameras, legacy CSI drivers, and V4L2 loopback devices.

Asking for a backend the platform does not have is a startup error, e.g. libcamera:0 on a Jetson tells you to use v4l2:<id> instead.

Libcamera

Keep the default camera_auto_detect=1 in /boot/firmware/config.txt.

Libcamera only produces yuv420 here, so --v4l2-format is ignored. Because yuv420 is uncompressed, the CSI/USB link — not the sensor — is usually what limits resolution and frame rate. Each MIPI lane carries 1.5 Gbps on the Pi 5 and 1 Gbps on earlier models [ref]:

Interface1-lane MIPI (older Pi)2-lane MIPI (Pi 4)4-lane MIPI (Pi 5)USB 2.0USB 3.0
Bandwidth1 Gbps2 Gbps6 Gbps0.48 Gbps5 Gbps

YUV 4:2:0 needs 12 bits per pixel, so 4Kp60 is 3840 × 2160 × 60 × 12 = 5.56 Gbps:

Resolution4Kp604Kp301080p601080p30
Bandwidth5.56 Gbps2.78 Gbps1.39 Gbps0.70 Gbps

Libargus

The CSI camera path on Jetson. Like libcamera it delivers yuv420 and ignores --v4l2-format, so the same bandwidth arithmetic applies. Frames arrive as EGL images and stay on the GPU, which is what lets the encoder — and, in the sponsor build, the detector — read them without a copy through the CPU.

/path/to/pi-webrtc --camera=libargus:0 --fps=30 --width=1920 --height=1080 ...

V4L2

On a modern Raspberry Pi OS, USB cameras are picked up as V4L2 devices with no changes to config.txt. For the legacy CSI driver, see QUICK_START.md.

1. Find the camera index. List the V4L2 devices to get the /dev/videoX node:

v4l2-ctl --list-devices

2. Check what it supports. Query the pixel formats, resolutions, and frame rates, replacing X with the index from step 1:

v4l2-ctl -d /dev/videoX --list-formats-ext

3. Run with those values. For a camera on /dev/video2 doing YUYV 720p60:

/path/to/pi-webrtc --camera=v4l2:2 --v4l2-format=yuyv --fps=60 --width=1280 --height=720 ...

Encoding

Which encoder WebRTC uses depends on --hw-accel and on the codecs the client offers in its SDP. It is worth running v4l2-ctl -d /dev/video0 --list-formats-ext before choosing a source format, since that decides which of the pipelines below you end up on.

Hardware Encoding

With --hw-accel, pi-webrtc uses hardware video encoding when available.

PlatformHardware codecs
Raspberry Pi 3 / 4 / Zero 2H264 (V4L2 M2M)
Raspberry Pi 5None
NVIDIA JetsonH264, AV1

The client selects the codec during SDP negotiation. On Jetson, both H264 and AV1 are hardware-encoded; AV1 requires an Orin-generation module.

Recording uses the same hardware encoder when available. H264 sources can be recorded directly, while other formats are re-encoded as needed. Without hardware encoding, recording falls back to OpenH264.

On platforms without hardware encoding, --hw-accel automatically falls back to software encoding.

h264 camera source

/path/to/pi-webrtc --camera=v4l2:0 --v4l2-format=h264 --fps=30 --width=1280 --height=960 --hw-accel ...
graph LR
A(Camera) -- h264 --> B(hw decoder) -- yuv420 --> C(hw scaler) --yuv420--> D(hw encoder) --h264-->E(webrtc client)
A --h264--> F(mp4)

The h264 stream is taken straight from the camera and decoded to yuv420 in hardware. When WebRTC detects network or device pressure the hardware scaler drops the decoded frame resolution, and raises it again when conditions improve; the encoder is reset to match each time. All frames move between the codecs over DMA, with no copy. If recording is enabled, the camera's h264 packets are copied into the MP4 directly, without re-encoding.

mjpeg camera source

/path/to/pi-webrtc --camera=v4l2:0 --v4l2-format=mjpeg --fps=30 --width=1280 --height=960 --hw-accel ...
graph LR
A(camera) -- mjpeg --> B(hw decoder) -- yuv420 --> C(hw scaler) --yuv420--> D(hw encoder) --h264-->E(webrtc client)
B --yuv420--> F(hw encoder) -- h264--> G(mp4)

Same as above, except that there are no h264 packets to copy, so the recorder runs its own hardware encoder instance on the decoded frames.

i420 camera source

# V4L2 camera
/path/to/pi-webrtc --camera=v4l2:0 --v4l2-format=i420 --fps=30 --width=1280 --height=960 --hw-accel ...

# Libcamera
/path/to/pi-webrtc --camera=libcamera:0 --fps=30 --width=1280 --height=960 --hw-accel ...

# Libargus (Jetson)
/path/to/pi-webrtc --camera=libargus:0 --fps=30 --width=1280 --height=960 --hw-accel ...
graph LR
A(camera) -- yuv420 --> C(hw scaler) --yuv420--> D(hw encoder) --h264-->E(webrtc client)
A --yuv420--> F(hw encoder) -- h264--> G(mp4)

The camera delivers uncompressed yuv420, so check the bandwidth tables before asking for high resolution and frame rate together. This path is useful on a Pi Zero, or when CPU is already spoken for by other services. Recording runs its own hardware encoder instance on the same frames.

Software Encoding

Without --hw-accel, pi-webrtc advertises H264, VP8, VP9, and AV1, and the client's SDP picks the winner. If you need a specific codec, make sure the client offers only that one.

h264 camera source

Not supported — there is no H264 software decoder in pi-webrtc.

mjpeg camera source

/path/to/pi-webrtc --camera=v4l2:0 --v4l2-format=mjpeg --fps=30 --width=1280 --height=960 ...
graph LR
A(camera) -- mjpeg --> B(libyuv) -- yuv420 --> C(libyuv scaler) --yuv420--> D(openh264) --h264-->E(webrtc client)
B --yuv420--> F(openh264) -- h264--> G(mp4)

The usual choice for devices without a V4L2 hardware encoder. libyuv decodes the mjpeg frames to yuv420 and handles downscaling when WebRTC asks for a lower resolution. Recording runs on its own OpenH264 instance.

i420 camera source

# V4L2 camera
/path/to/pi-webrtc --camera=v4l2:0 --v4l2-format=i420 --fps=30 --width=1280 --height=960 ...

# Libcamera
/path/to/pi-webrtc --camera=libcamera:0 --fps=30 --width=1280 --height=960 ...
graph LR
A(camera) -- yuv420 --> C(libyuv scaler) --yuv420--> D(openh264) --h264-->E(webrtc client)
A --yuv420--> F(openh264) -- h264--> G(mp4)

For devices with no hardware encoder but plenty of CSI/USB bandwidth.

Edit on GitHub

On this page