A small MJPEG streaming server for a USB (UVC) webcam, using the FFmpeg libraries directly for capture.
USB webcam → libavformat/libavdevice (v4l2 demux) → [libavcodec decode → libswscale → libavcodec encode, if --reencode] → HTTP multipart MJPEG → Browser
- Pure C++17. Capture is done by linking directly against
libavformat,libavcodec,libavdevice,libavutilandlibswscale- there is noffmpegsubprocess anymore, no argv-building, no pipe-parsing. - By default it asks the webcam for MJPEG directly (demuxer option
input_format=mjpeg), so each packet libavformat hands back is already a complete JPEG frame - it's forwarded straight to the HTTP server with no decode/encode step, the same "zero conversion" philosophy as the original libcamera version. Cameras that don't support MJPEG natively can fall back to--reencode. - With
--reencode, the camera is asked for rawYUYV422. Frames are decoded, converted withlibswscale(YUYV422 → YUVJ422P), and encoded to MJPEG with libavcodec's own MJPEG encoder. - Multiple browsers can connect at once; each gets its own thread and the
latest frame (identical
HttpServerto the original project, unchanged).
usb-camera-web/
├── CMakeLists.txt
├── main.cpp # wires WebcamCapture -> HttpServer together
├── WebcamCapture.h/.cpp # demux/decode/scale/encode via libav* APIs
├── HttpServer.h/.cpp # multipart/x-mixed-replace HTTP server (unchanged)
└── README.md
This now needs the FFmpeg development headers/libraries (so pkg-config
can find libavformat, libavcodec, libavdevice, libavutil and
libswscale), not the ffmpeg command-line binary - ffmpeg itself
doesn't need to be installed or on PATH anymore.
Raspberry Pi OS:
sudo apt update
sudo apt install -y build-essential cmake pkg-config v4l-utils \
libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-devAfter installing, verify pkg-config can actually see the libraries
before running cmake - this is the single most common setup problem:
pkg-config --modversion libavdeviceIf that prints a version number, you're good. If it errors with
Package 'libavdevice' ... not found, cmake will fail the same way at
the pkg_check_modules step in CMakeLists.txt. That almost always means
one of:
- You installed the plain
ffmpegpackage but not the-dev/-develheaders (the runtime binary doesn't ship the.pcfiles pkg-config needs) - install the-dev/-develpackages above. - On Debian/Ubuntu, some minimal images split
ffmpegintouniverse/multiverse- make sure those repos are enabled (sudo add-apt-repository universe && sudo apt update) before installing. - You built/installed FFmpeg from source into a non-standard prefix
(e.g.
/usr/local) andPKG_CONFIG_PATHdoesn't include it:then re-runexport PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
cmake ...
Check that your webcam is visible and see what formats it supports:
v4l2-ctl --list-devices
v4l2-ctl -d /dev/video0 --list-formats-extLook for MJPG in that output - if your camera lists it, you can use the
default (fast, no re-encode) mode below. If it only lists YUYV, use
--reencode.
mkdir build && cd build
cmake ..
make -j$(nproc)./ffmpeg-stream [device] [width] [height] [fps] [quality] [port] [--reencode]All arguments are optional. Defaults: /dev/video0 640 480 15 5 8080,
MJPEG passthrough (no re-encode).
./ffmpeg-stream # /dev/video0, 640x480@15fps, port 8080
./ffmpeg-stream /dev/video1 1280 720 10 # different device/resolution
./ffmpeg-stream /dev/video0 640 480 15 8 8080 --reencode
# camera only does YUYV -> software encode, quality 8Then open in a browser:
http://<host-ip>:8080/
/ serves a tiny HTML page with an <img>
pointed at /stream, the raw multipart/x-mixed-replace MJPEG feed.
Stop the server with Ctrl+C - it tears down the libav contexts and
closes all client connections cleanly.
This project is licensed under the GNU General Public License v3.0.