Streaming a webcam with GStreamer
GStreamer is an easy way to send real video through NanoPing. On the sender, GStreamer captures the webcam and sends it as RTP over UDP to the sender pipeline. On the receiver, NanoPing delivers the stream to a local port, where a second GStreamer command shows it in a window.
The commands below use the addresses of the example pipeline files and the Getting Started guide, which you also enter as Application In and Application Out in the UDP Video Streaming template:
| Side | GStreamer | NanoPing |
|---|---|---|
| Sender | Sends to 127.0.0.1:5000 | UDP Local listens on 127.0.0.1:5000 (Application In) |
| Receiver | Listens on port 6000 | Peer Writer delivers to 127.0.0.1:6000 (Application Out) |
If your pipelines use other addresses, change the port in the commands to match.
Install GStreamer
On Linux (Ubuntu 24.04 LTS or newer):
sudo apt-get install gstreamer1.0-tools gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-x v4l-utils
On macOS, with Homebrew:
brew install gstreamer
To find your camera on Linux, run v4l2-ctl --list-devices; the first camera is usually /dev/video0. On macOS, cameras are numbered from device-index=0.
Start the receiver
Start this first, on the receiver. It works on Linux and macOS:
gst-launch-1.0 udpsrc port=6000 \
caps="application/x-rtp, media=video, encoding-name=H264, payload=96" ! \
rtph264depay ! \
avdec_h264 ! \
videoconvert ! \
textoverlay text="NanoPing" valignment=top halignment=center font-desc="Sans, 36" ! \
autovideosink
It waits for video and opens a window, labelled NanoPing, when the first frames arrive.
To record to a file instead of showing a window, use this. The -e flag finishes the file cleanly when you press Ctrl+C:
gst-launch-1.0 -e udpsrc port=6000 \
caps="application/x-rtp, media=video, encoding-name=H264, payload=96" ! \
rtph264depay ! \
h264parse ! \
mp4mux ! \
filesink location=received.mp4
Start the sender
Test pattern. Check the path first with GStreamer's built-in test pattern, no camera needed:
gst-launch-1.0 videotestsrc is-live=true pattern=ball ! \
video/x-raw,width=640,height=480 ! \
videoconvert ! \
x264enc tune=zerolatency ! \
rtph264pay config-interval=1 pt=96 ! \
multiudpsink clients=127.0.0.1:5000
Webcam on Linux:
gst-launch-1.0 v4l2src device=/dev/video0 ! \
video/x-raw,width=640,height=480 ! \
videoconvert ! \
x264enc tune=zerolatency ! \
rtph264pay config-interval=1 pt=96 ! \
multiudpsink clients=127.0.0.1:5000
Webcam on macOS:
gst-launch-1.0 avfvideosrc device-index=0 ! \
queue ! \
video/x-raw,width=640,height=480,framerate=30/1 ! \
videoconvert ! \
x264enc speed-preset=ultrafast tune=zerolatency intra-refresh=true ! \
h264parse ! \
rtph264pay config-interval=1 pt=96 mtu=1100 ! \
multiudpsink clients=127.0.0.1:5000
Webcam with built-in H.264 (Linux). Many USB webcams can encode H.264 themselves, so you can skip the encoder:
gst-launch-1.0 v4l2src device=/dev/video0 do-timestamp=true ! \
video/x-h264,width=1920,height=1080,framerate=30/1 ! \
h264parse config-interval=-1 ! \
rtph264pay config-interval=1 pt=96 mtu=1300 ! \
multiudpsink clients=127.0.0.1:5000 sync=false
What the main settings do:
tune=zerolatencyturns off encoder features that add delay, such as look-ahead and B-frames.config-interval=1repeats the stream headers every second, so the receiver can join at any time.- On macOS,
speed-preset=ultrafastencodes faster,intra-refresh=truespreads keyframe data over many frames so a lost packet does not freeze the picture, andmtu=1100sends smaller packets.
To send to several destinations at once, list them in multiudpsink clients=, separated by commas. The demo pipelines use this to send the same video through NanoPing and as plain UDP.
Over a VPN tunnel
If NanoPing runs as a VPN with a TUN interface, send straight to the receiver's TUN IP address instead of 127.0.0.1, and use the same port on the receiver.
Lifecycle handler configuration
The example pipeline files often start GStreamer for you, through the pipeline's lifecycle_handler. The commands in its pre_start list run in the background when the pipeline starts. Open the pipeline's Code tab to edit them, and check two things.
1. The GStreamer path
Each cmd starts with the full path to gst-launch-1.0. Change it to match your system:
| System | Path |
|---|---|
| macOS (Apple Silicon) | /opt/homebrew/bin/gst-launch-1.0 |
| Linux (Ubuntu/Debian) | /usr/bin/gst-launch-1.0 |
Avoid version-specific Homebrew paths such as /opt/homebrew/Cellar/gstreamer/<version>/bin/…. They stop working when Homebrew updates GStreamer.
2. The camera source
The command that captures the camera starts with a camera source, which depends on the system:
| System | Camera source |
|---|---|
| macOS | avfvideosrc device-index=0 |
| Linux | v4l2src device=/dev/video0 |
Example: Lifecycle Handler JSON
Below is the section of the JSON you need to edit. Note the cmd fields where the path and source are defined:
"lifecycle_handler": {
"pre_start": [
{
"comment": "Video Source (Camera) -> Sender",
"cmd": "/PATH/TO/gst-launch-1.0 avfvideosrc device-index=0 ! video/x-raw,width=640,height=480 ! videoconvert ! x264enc tune=zerolatency ! rtph264pay config-interval=1 pt=96 ! multiudpsink clients=127.0.0.1:5000,127.0.0.1:5001",
"policy": "RUN_IN_BACKGROUND"
},
]
}
In JSON, each command is a single line, and quotes inside it must be escaped as \".