A Python 3 script that invokes gstreamer-1.0 to read from a webcam, encode the video stream, and send it to a destination host over RTP/UDP. Supports H264, H265, VP8, and AV1. Works on macOS, Linux, and Windows.
It can also generate a valid SDP file (session.sdp) that receivers like VLC can open directly.
Run with --list-codecs to check which codecs are installed, or --debug for full GStreamer output when troubleshooting.
$ python gstreamcam.py -h
usage: gstreamcam.py [-h] [--sdp] [--debug] [--port PORT]
[--codec {h264,openh264,h265,vp8,av1}]
[--format FORMAT] [--camera CAMERA]
[--resolution RESOLUTION] [--no-convert]
[--list-cameras] [--list-codecs]
[--timeout TIMEOUT] [hostname]
| Option | Short | Default | Description |
|---|---|---|---|
hostname |
required | Destination hostname or IP address | |
--sdp |
off | Generate a session.sdp file for the stream |
|
--debug |
off | Print the GStreamer command being executed | |
--port |
-p |
5000 | UDP destination port |
--codec |
-c |
h264 | Video codec (see codecs) |
--camera |
0 | Camera device index | |
--format |
-f |
auto | Pixel format (see pixel formats); omit to let GStreamer negotiate |
--resolution |
-r |
native | Request resolution from camera as WIDTHxHEIGHT (e.g., 1280x720) |
--no-convert |
off | Fail instead of adding converters; shows native camera capabilities | |
--list-cameras |
List cameras with supported formats and resolutions | ||
--list-codecs |
List available codecs and exit | ||
--timeout |
60 | Timeout in seconds for codec/camera probes |
When --resolution is omitted the camera negotiates its native resolution.
Before building the pipeline, the script queries both the camera's native capabilities (via gst-device-monitor-1.0) and the encoder's accepted formats (via gst-inspect-1.0). It then builds the cleanest possible pipeline:
- Direct — camera outputs a format the encoder accepts at the requested resolution. No extra elements.
- videoconvert — format mismatch between camera and encoder. Adds format conversion.
- videoscale — resolution not natively supported by camera. Adds scaling.
- Both — neither format nor resolution match. Adds both converters.
In all fallback cases the script warns you and suggests formats that both the camera and encoder support natively:
WARNING: Camera supports NV12 at 1920x1080 but encoder doesn't accept it. Adding videoconvert.
INFO: Formats supported by both camera and encoder at 1920x1080: I420, Y42B.
Try one with --format for zero-conversion.
Use --no-convert to enforce a direct pipeline — the script will fail with suggestions rather than silently adding converters.
Stream from the default webcam to localhost
python gstreamcam.py 127.0.0.1
Stream using a camera-native format for zero-conversion
python gstreamcam.py 127.0.0.1 --format NV12
Stream from the second webcam to localhost
python gstreamcam.py --camera 1 127.0.0.1
Stream at 720p using VP8
python gstreamcam.py 127.0.0.1 -c vp8 -r 1280x720
Enforce a direct pipeline (fail if converters would be needed)
python gstreamcam.py 127.0.0.1 --format NV12 -r 1280x720 --no-convert
Stream and create an SDP file
python gstreamcam.py 127.0.0.1 --sdp
List cameras and what they support
python gstreamcam.py --list-cameras
Check which codecs are installed
python gstreamcam.py --list-codecs
Stream with --sdp and open the generated file:
python gstreamcam.py 127.0.0.1 --sdp
vlc session.sdp
gst-launch-1.0 udpsrc port=5000 \
caps="application/x-rtp, clock-rate=(int)90000, payload=(int)96" \
! rtph264depay ! decodebin ! videoconvert ! autovideosink
gst-launch-1.0 udpsrc port=5000 \
caps="application/x-rtp, clock-rate=(int)90000, payload=(int)96" \
! rtph265depay ! decodebin ! videoconvert ! autovideosink
gst-launch-1.0 udpsrc port=5000 \
caps="application/x-rtp, clock-rate=(int)90000, payload=(int)96" \
! rtpvp8depay ! vp8dec ! videoconvert ! autovideosink
gst-launch-1.0 udpsrc port=5000 \
caps="application/x-rtp, clock-rate=(int)90000, payload=(int)96" \
! rtpav1depay ! av1dec ! videoconvert ! autovideosink
| Codec | Encoder | Payloader | GStreamer Package |
|---|---|---|---|
h264 |
x264enc | rtph264pay | gst-plugins-ugly |
openh264 |
openh264enc | rtph264pay | gst-plugins-bad |
h265 |
x265enc | rtph265pay | gst-plugins-bad |
vp8 |
vp8enc | rtpvp8pay | gst-plugins-good |
av1 |
av1enc | rtpav1pay | gst-plugins-bad |
--format accepts any GStreamer pixel format string. Common choices:
| Format | Chroma | Notes |
|---|---|---|
I420 |
4:2:0 | Accepted by all encoders. Rarely native to cameras. |
NV12 |
4:2:0 | Common camera-native format. Works with h264, openh264. |
UYVY |
4:2:2 | Common camera-native format. Needs videoconvert for most encoders. |
YV12 |
4:2:0 | Works with h264, av1. |
Y42B |
4:2:2 | Higher chroma quality. Works with h264, h265, av1. |
Y444 |
4:4:4 | Full chroma. Works with h264, h265, av1. |
When --format is omitted, GStreamer negotiates the best format between camera and encoder automatically. The script queries your camera and tells you which formats are shared. Use --format only when you want to force a specific format.
Not all codecs may be available on your system. Run python gstreamcam.py --list-codecs to see which ones are installed. If a codec is missing, install the corresponding GStreamer package.
You need Python 3.10+ and GStreamer 1.0 with the plugins for your chosen codec.
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly
sudo apt install gstreamer1.0-tools gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly
sudo dnf install gstreamer1-tools gstreamer1-plugins-base \
gstreamer1-plugins-good gstreamer1-plugins-bad-free gstreamer1-plugins-ugly-free
Download and run the official installer from gstreamer.freedesktop.org. Make sure gst-launch-1.0 is on your PATH after installation.
Check which codecs are installed:
python gstreamcam.py --list-codecs
Run with --debug to see the full GStreamer command and stderr output.
Common issues:
- "codec not available" — the encoder plugin isn't installed. Check the codecs table for the required package and install it.
- "Adding videoconvert" — the camera doesn't output a format the encoder accepts natively. The script handles this automatically but suggests faster alternatives. Use
--formatwith a suggested native format. - Camera not accessible — on Linux, make sure your user is in the
videogroup. On macOS, grant camera permissions when prompted. - "gst-launch-1.0 not found" — GStreamer isn't installed or not on PATH. See installation.
- Codec probe timeout — first run can be slow while GStreamer scans its plugin registry. Try again, or increase
--timeout.