pi-webrtc

Encoding

pi-webrtc encodes the video for each WebRTC connection, so each connection can follow its own network. This page shows which encoder it uses, and how the frames move from the camera to the encoder.

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. See Video & Audio for the sources and their formats.

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 Jetson OrinH264, AV1
NVIDIA Jetson Orin NanoNone

The client selects the codec during SDP negotiation. On a Jetson Orin, both H264 and AV1 are hardware-encoded.

Recording re-encodes the frames the camera delivers, using the same hardware encoder when available and OpenH264 otherwise. To keep a camera's own h264 stream untouched, record it outside pi-webrtc, for example through a MediaMTX proxy.

With --hw-accel, each hardware decoder, scaler, and encoder that the board lacks falls back to its software counterpart on its own, with a warning in the log. On a Pi 5, for example, encoding is done in software.

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)
B --yuv420--> F(hw encoder) -- h264--> G(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 recorder runs its own hardware encoder instance on the decoded frames.

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, with the camera compressing to mjpeg instead of h264.

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

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

Without a hardware decoder, libavcodec decodes the h264 stream in software. A Pi 5 takes about 6 ms per 1080p frame on one core.

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.

RTSP Input

An RTSP stream is already compressed. pi-webrtc decodes it, then encodes it again for WebRTC. This is what lets it change the bitrate and the resolution for each connection.

graph LR
A(RTSP stream) -- h264 / h265 / mjpeg --> B(decoder) -- yuv420 --> C(scaler) --yuv420--> D(encoder) --> E(webrtc client)
PlatformHardware decoding (--hw-accel)Software decoding
Raspberry Pi 3 / 4 / Zero 2H.264 and MJPEG, up to 1920×1088H.265, and larger streams
Raspberry Pi 5NoneH.264 and H.265 with libavcodec, MJPEG with libyuv
NVIDIA JetsonH.264, H.265 and MJPEGUsed only when the hardware decoder is not available

See RTSP cameras for the setup.

Edit on GitHub

On this page