Camera Feed in C++
Start a live camera feed app on WendyOS using the C++ camera template
Live Camera Feed with C++
Use the Wendy camera template when you want a browser-viewable camera stream without wiring the project from scratch. The template includes wendy.json, camera entitlements, a Dockerfile, C++ backend code, and the browser UI.
Prerequisites
- Wendy CLI installed on your development machine
- CMake 3.16 or later installed
- A C++ compiler
- A WendyOS device with a USB camera connected
Setting Up Your Project
Initialize the Project
wendy init cpp-camera --target wendyos --language cpp --template camera-feed --var APP_ID=cpp-camera --var PORT=7003 --assistant skip --git-init no
cd cpp-cameraThe generated project is ready to run. After the first run, use the sections below as a code breakdown when you want to understand or customize camera handling, routes, or UI behavior.
Run on WendyOS
wendy runWendy will build the app, ask you to select a device if one is not already configured, deploy the container, and print the app URL.
Code Breakdown
Generated Project
The template creates a small Drogon service with GStreamer camera capture:
cpp-camera/
├── CMakeLists.txt
├── Dockerfile
├── index.html
├── main.cpp
├── assets/
│ └── wendy-logo.svg
└── wendy.jsonwendy.json declares the runtime capabilities the app needs:
networkinhostmode so the browser can reach the Drogon server on the WendyOS devicecameraso the container can access/dev/video*gpuso the app can use accelerated media paths when availablereadiness.tcpSocketsowendy runwaits until the HTTP server is reachable- a
postStarthook that opens the app URL from the Wendy CLI when possible
C++ Backend
main.cpp starts a Drogon HTTP server and a singleton MJPEGCamera that owns the GStreamer pipeline. The camera starts lazily when the first WebSocket client connects and stops after the last client disconnects, so the app does not hold camera resources when nobody is watching.
The capture pipeline reads from the selected V4L2 device, keeps the stream as MJPEG, and publishes frames through an appsink:
v4l2src device=/dev/video0
! image/jpeg
! jpegdec
! jpegenc quality=85
! appsink name=sink emit-signals=true max-buffers=2 drop=true sync=falsemax-buffers=2 and drop=true keep latency low by dropping stale frames if the browser cannot keep up.
Routes and Streaming
The generated server exposes:
GET /fromindex.htmlGET /camerasto list available V4L2 devices withv4l2-ctl --list-devicesWS /streamto send binary JPEG frames to the browser
When the browser sends a message like {"switch_camera": "/dev/video2"}, the WebSocket handler restarts the GStreamer pipeline against that device.
Frontend
index.html connects to /stream, receives binary JPEG frames, converts each frame to a data URL, and updates the full-screen image element. It also calls /cameras to populate the camera picker, shows connection state, displays FPS, and provides a fullscreen button.
Open the app URL from the Wendy run output to view the stream.