---
title: Camera Feed in C++
description: Start a live camera feed app on WendyOS using the C++ camera template
language: cpp
---

## 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
- A WendyOS device with a USB camera connected

## Setting Up Your Project

<Steps>
  <Step>
    ### Initialize the Project

    ```bash
    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-camera
    ```

    <CliShot flow="init" step="wizard" alt="wendy init scaffolding the C++ camera-feed project" />

    The 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.
  </Step>

  <Step>
    ### Run on WendyOS

    ```bash
    wendy run
    ```

    <CliShot flow="run" step="deploy" alt="wendy run deploying the C++ camera-feed container" />

    Wendy will build the app, ask you to select a device if one is not already configured, deploy the container, and print the app URL.
  </Step>
</Steps>

## Code Breakdown

### Generated Project

The template creates a small Drogon service with GStreamer camera capture:

```text
cpp-camera/
├── CMakeLists.txt
├── Dockerfile
├── index.html
├── main.cpp
├── assets/
│   └── wendy-logo.svg
└── wendy.json
```

`wendy.json` declares the runtime capabilities the app needs:

- `network` in `host` mode so the browser can reach the Drogon server on the WendyOS device
- `camera` so the container can access `/dev/video*`
- `gpu` so the app can use accelerated media paths when available
- `readiness.tcpSocket` so `wendy run` waits until the HTTP server is reachable
- a `postStart` hook 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`:

```text
v4l2src device=/dev/video0
  ! image/jpeg
  ! jpegdec
  ! jpegenc quality=85
  ! appsink name=sink emit-signals=true max-buffers=2 drop=true sync=false
```

`max-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 /` from `index.html`
- `GET /cameras` to list available V4L2 devices with `v4l2-ctl --list-devices`
- `WS /stream` to 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.
