Skip to content

FFmpeg & GStreamer

Once the grabber is detected as /dev/video0, you can do almost anything with it from the command line using FFmpeg or GStreamer. No GUI, no configuration — straight command pipes.

Installation

sudo pacman -S ffmpeg gst-plugins-good gst-plugins-bad gst-plugins-ugly \
                gst-libav v4l-utils x264 x265
sudo apt install ffmpeg gstreamer1.0-plugins-good \
                gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
                gstreamer1.0-libav v4l-utils x264 x265
sudo dnf install ffmpeg gstreamer1-plugins-good \
                gstreamer1-plugins-bad-free gstreamer1-plugins-ugly \
                gstreamer1-libav v4l-utils x264 x265

FFmpeg: the four things everyone wants

1. Live preview (no recording)

ffplay -f v4l2 -input_format mjpeg -framerate 60 -video_size 1920x1080 \
       /dev/video0

A window pops up. ESC to quit.

2. Record to MP4

ffmpeg -hide_banner \
       -f v4l2 -input_format mjpeg -framerate 60 -video_size 1920x1080 \
       -i /dev/video0 \
       -f alsa -ac 2 -i hw:1,0 \
       -c:v libx264 -preset veryfast -crf 18 \
       -c:a aac -b:a 192k \
       -t 00:00:30 \
       recording.mp4

-t 00:00:30 records 30 seconds. Drop it for infinite recording (CTRL-C to stop).

hw:1,0 is the Pengo audio card — see Audio capture → for how to find the right number.

3. Stream to RTMP (YouTube / Twitch / custom server)

ffmpeg -hide_banner \
       -f v4l2 -input_format mjpeg -framerate 60 -video_size 1920x1080 \
       -i /dev/video0 \
       -f alsa -ac 2 -i hw:1,0 \
       -c:v libx264 -preset veryfast -b:v 6000k -maxrate 6000k -bufsize 12000k \
       -g 60 -keyint_min 60 -sc_threshold 0 \
       -c:a aac -b:a 160k -ar 44100 \
       -f flv rtmp://a.rtmp.youtube.com/live2/YOUR-STREAM-KEY

Replace the RTMP URL with your destination. -g 60 means a keyframe every 2 s at 30 fps (or every 1 s at 60 fps), which most platforms prefer.

4. Re-encode to a more compatible MP4 (e.g. for editing in DaVinci)

ffmpeg -hide_banner \
       -f v4l2 -input_format mjpeg -framerate 60 -video_size 1920x1080 \
       -i /dev/video0 \
       -c:v libx264 -preset slow -crf 16 -pix_fmt yuv420p \
       -c:a copy \
       editing_master.mp4

-pix_fmt yuv420p is the most-compatible pixel format for editing tools and players.

FFmpeg: useful options reference

Flag Meaning
-f v4l2 Use the V4L2 input backend (Linux video devices).
-input_format mjpeg Tell the device to output MJPG (compressed) instead of YUYV (raw). MJPG = stable 60 fps.
-framerate 60 Ask for 60 fps from the device.
-video_size 1920x1080 1080p. The grabber only supports this one USB resolution.
-thread_queue_size 512 Buffer more frames in the input queue — helps when the disk or network is slow.
-fflags nobuffer Reduce latency for live previews.
-preset ultrafast-preset veryslow x264 speed/quality tradeoff. ultrafast is ~real-time, slow gives best quality for the same bitrate.

Live low-latency preview

ffplay -fflags nobuffer -flags low_delay -f v4l2 -input_format mjpeg \
       -framerate 60 -video_size 1920x1080 /dev/video0

GStreamer

GStreamer is the underlying pipeline that GNOME, KDE, PipeWire, OBS-on-Linux and a lot of embedded media apps use. If you want to build composite pipelines (overlay text, mix with another source, route through custom filters), GStreamer is the way.

Live preview

gst-launch-1.0 -v v4l2src device=/dev/video0 ! \
    image/jpeg,width=1920,height=1080,framerate=60/1 ! \
    jpegdec ! videoconvert ! autovideosink

Record to MP4

gst-launch-1.0 -e \
    v4l2src device=/dev/video0 num-buffers=900 ! \
        image/jpeg,width=1920,height=1080,framerate=60/1 ! jpegdec ! \
        videoconvert ! x264enc tune=zerolatency bitrate=6000 ! \
        mux.video \
    pulsesrc device=alsa_input.usb-Pengo_Pengo_HDMI_Grabber-02.analog-stereo ! \
        audioconvert ! aacenc ! mux.audio \
    mp4mux name=mux ! filesink location=recording.mp4

Composite: Pengo over a still image (e.g. lower-third for a stream)

gst-launch-1.0 -e \
    v4l2src device=/dev/video0 ! image/jpeg,width=1920,height=1080,framerate=60/1 \
        ! jpegdec ! videoconvert \
    filesrc location=overlay.png ! pngdec ! imagefreeze ! videoconvert \
    compositor name=comp sink_0::zorder=0 sink_1::zorder=1 \
        ! videoconvert ! x264enc tune=zerolatency bitrate=6000 \
        ! mp4mux ! filesink location=overlay.mp4

ffmpeg vs GStreamer — which to pick?

FFmpeg GStreamer
Best for Recording, transcoding, RTMP streaming Custom composite pipelines, integration with desktop frameworks
CLI simplicity Shortest commands, one-liners Pipeline graphs (more verbose but more powerful)
Used by Standalone scripts, OBS, most streaming tools PipeWire, OBS-on-Linux internals, GNOME/KDE media, custom apps
Learning curve Shallow Medium

For 95 % of uses, FFmpeg is enough. Reach for GStreamer when you need to build a non-trivial pipeline (PiP, alpha blends, motion detection, custom filters).

Common gotchas

Frame drops

If you see dropped frames in ffmpeg's output (drop= field), try: - Switch from -input_format yuyv422-input_format mjpeg - Bump -thread_queue_size to 1024 or higher - Confirm your USB port is actually USB 3.0 - Stop competing USB 3.0 devices (external SSDs, etc.) that may share the same bus

Audio is out of sync

Add -async 1 to the FFmpeg command, or use -use_wallclock_as_timestamps 1 if you need frame-perfect A/V sync against wall-clock time.

Black frame

Almost always HDCP-protected content, or a 1080i source, or no actual HDMI signal at all. Verify with a TV plugged into the loop-out — if the TV shows the source correctly but the USB stream is black, it's a grabber issue. If the TV is also black, it's the source.


Next: OBS Studio →, or Audio capture →.