> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Input Connector

> Capture JPEG frames from a camera, RTSP or HTTP stream, or file with TYPE VIDEO_INPUT and FFmpeg

## Publish camera frames to MQTT

Video Input opens a camera, network stream, or file on the broker host and publishes each frame as a JPEG data-URI. Dashboards and Coreflux HUB can drop that payload straight into an image tag. You start and stop capture with MQTT events.

> [!TIP]
>
> **Like a photo booth on a timer.** You press start, and a new picture lands on the topic at the rate you chose — not a video file, a series of JPEGs.

### When to use this

Use `TYPE VIDEO_INPUT` when a dashboard, barcode decode, or action needs still frames from a local camera, an RTSP or HTTP(S) camera, or a video file. FFmpeg must be on the broker host (or in the slim image). The connector does not talk to camera drivers itself.

> [!NOTE]
>
> Do not set `WITH FORMAT`. That keyword belongs to models. Every frame this connector publishes is a JPEG data-URI (`data:image/jpeg;base64,…`).

***

Pick the source that matches the camera. Each tab is a full connector: a config block plus start and stop events.

## Quick Start

**IP camera**

Store the RTSP URL in an environment variable so the password never sits in LoT:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE EntranceCamera WITH TYPE VIDEO_INPUT
    ADD VIDEO_INPUT_CONFIG
        WITH SOURCE GET ENV "CAMERA_RTSP_URL"
        WITH FRAME_RATE "10"
        WITH WIDTH "640"
        WITH HEIGHT "480"
        WITH QUALITY "5"
        WITH DESTINATION_TOPIC "video/entrance/frames"
    ADD EVENT StartCapture
        WITH QUERY "START"
        WITH SOURCE_TOPIC "video/entrance/control/start"
    ADD EVENT StopCapture
        WITH QUERY "STOP"
        WITH SOURCE_TOPIC "video/entrance/control/stop"
```

**Local camera**

On macOS and Linux, `webcam:0` is the first camera. On Linux that is `/dev/video0`:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE LocalCamera WITH TYPE VIDEO_INPUT
    ADD VIDEO_INPUT_CONFIG
        WITH SOURCE "webcam:0"
        WITH FRAME_RATE "5"
        WITH WIDTH "640"
        WITH HEIGHT "480"
        WITH QUALITY "5"
        WITH DESTINATION_TOPIC "video/local/frames"
    ADD EVENT StartCapture
        WITH QUERY "START"
        WITH SOURCE_TOPIC "video/local/control/start"
    ADD EVENT StopCapture
        WITH QUERY "STOP"
        WITH SOURCE_TOPIC "video/local/control/stop"
    ADD EVENT TakeSnapshot
        WITH QUERY "CAPTURE 1 FRAME"
        WITH SOURCE_TOPIC "video/local/control/snapshot"
        WITH DESTINATION_TOPIC "video/local/picture"
```

**Windows camera**

Copy the video device name from the DirectShow list. The `video=` prefix is required:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE WindowsCamera WITH TYPE VIDEO_INPUT
    ADD VIDEO_INPUT_CONFIG
        WITH SOURCE "video=Integrated Webcam"
        WITH FRAME_RATE "5"
        WITH WIDTH "640"
        WITH HEIGHT "480"
        WITH QUALITY "5"
        WITH DESTINATION_TOPIC "video/windows/frames"
    ADD EVENT StartCapture
        WITH QUERY "START"
        WITH SOURCE_TOPIC "video/windows/control/start"
    ADD EVENT StopCapture
        WITH QUERY "STOP"
        WITH SOURCE_TOPIC "video/windows/control/stop"
```

**File**

Play a file on the broker host. On Windows, use a path FFmpeg accepts, such as `C:/media/sample.mp4`:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE FileCamera WITH TYPE VIDEO_INPUT
    ADD VIDEO_INPUT_CONFIG
        WITH SOURCE "/var/media/sample.mp4"
        WITH FRAME_RATE "5"
        WITH DESTINATION_TOPIC "video/file/frames"
    ADD EVENT StartCapture
        WITH QUERY "START"
        WITH SOURCE_TOPIC "video/file/control/start"
    ADD EVENT StopCapture
        WITH QUERY "STOP"
        WITH SOURCE_TOPIC "video/file/control/stop"
```

Publish any payload to the start topic to begin. Frames then arrive on `DESTINATION_TOPIC` until you publish to the stop topic.

***

## Configuration

**SOURCE** `string` *required*

Camera, URL, or file. `webcam:N` on macOS and Linux. `video=Device Name` on Windows. An `rtsp://`, `http://`, or `https://` URL, or a file path, on any host. Prefer `GET ENV` or `GET SECRET` when the URL contains a password.

**DESTINATION\_TOPIC** `string`

MQTT topic for JPEG frames. A `START` event uses this topic. A `CAPTURE` event can override it.

**FRAME\_RATE** `string`

How many frames to publish per second, from `1` to `60`. On a local camera this is the publish rate. Many cameras only capture at 15 or 30 fps in hardware; `5` still works as the rate on the topic.

**WIDTH** `string`

Output width in pixels. Set it together with `HEIGHT`.

**HEIGHT** `string`

Output height in pixels. Set it together with `WIDTH`.

**QUALITY** `string`

JPEG quantizer from `1` to `31`. Lower numbers keep more detail and publish larger payloads. `5` is a balanced start. This is not a 1–100 percentage.

`WIDTH`, `HEIGHT`, and `QUALITY` apply to the JPEG on MQTT. They do not reconfigure the camera's own encoder.

Broker logs redact `user:pass@` in RTSP and HTTP URLs. HTTP(S) sources use a 10 second timeout and the user-agent `CorefluxVideoInput/1.0`. RTSP uses TCP with the same timeout.

## Capture sources

| `SOURCE` | Broker host | What opens |
| - | - | - |
| `webcam:0` | macOS | First camera in the system list (built-in on many Macs) |
| `webcam:1` | macOS | Second camera, often USB |
| `webcam:0` | Linux | `/dev/video0` |
| `video=Integrated Webcam` | Windows | DirectShow device. The name must match exactly |
| `rtsp://…`, `http://…`, `https://…` | Any | Network stream. One attempt, no local-camera retry |
| File path | Any | File input |

`webcam:N` is rejected on Windows. Device indexes are not stable in DirectShow, so use `video=` plus the exact name. A raw `/dev/video0`, or a Windows name without `video=`, is treated as a file path and skips the local-camera retries.

On start, a local camera tries several input profiles until one stays open. Network URLs and files are a single attempt.

### List devices on the broker host

| Host | Command |
| - | - |
| Linux | `ls -l /dev/video*` — `webcam:0` is `/dev/video0` |
| macOS | `ffmpeg -f avfoundation -list_devices true -i ""` |
| Windows | `ffmpeg -list_devices true -f dshow -i dummy` — copy the **video** name into `video=…` |

If macOS capture fails immediately, allow Camera access for the process that started the broker.

## Events

**QUERY** `string` *required*

`START`, `STOP`, `CAPTURE <N> FRAMES`, `CAPTURE <N> SECONDS`, or `CAPTURE <N> MINUTES`. `CAPTURE 1` and `CAPTURE 1 FRAME` take one picture.

**SOURCE\_TOPIC** `string` *required*

Topic that runs the event. Keep control topics separate from the frame topic.

**DESTINATION\_TOPIC** `string`

Override for a snapshot or a timed clip. Continuous `START` uses the topic on `VIDEO_INPUT_CONFIG`.

| Query | Result |
| - | - |
| `START` | Capture until `STOP` |
| `STOP` | Stop every capture on this connector |
| `CAPTURE 1 FRAME` | One JPEG |
| `CAPTURE <N> FRAMES` | That many frames |
| `CAPTURE <N> SECONDS` | Frames for N seconds |
| `CAPTURE <N> MINUTES` | Frames for N minutes |

There is no motion or per-frame callback on the connector. Every decoded frame goes to `DESTINATION_TOPIC`. Failures show up in the broker log and on `$SYS/Coreflux/Routes/{RouteName}/status`. You can also publish `-checkRouteConnection <RouteName>` to `$SYS/Coreflux/Command`.

An action can start a named event without a control publish:

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION StartEntranceCamera
ON TOPIC "control/video/start" DO
    TRIGGER "EntranceCamera" TO PAYLOAD FOR "StartCapture"
```

## Docker and FFmpeg

Install FFmpeg on the broker host and confirm `ffmpeg -version` there. The slim broker image includes FFmpeg on amd64 and arm64. The minimal image does not, and arm/v7 does not.

RTSP and HTTP work in Docker with no camera hardware. A local camera needs the device node inside the container.

| Where the broker runs | Local camera | Without a device mount |
| - | - | - |
| Linux container | Pass `--device /dev/video0` (or the matching node). Inside the image, `webcam:0` is `/dev/video0` | RTSP, HTTP, or a file |
| macOS or Windows Docker Desktop | The camera stays on the desktop host, not in the Linux VM | RTSP, HTTP, or a file |

## Payload size

JPEG on MQTT is much larger than sensor telemetry. Start at 5 fps, 640×480, and `QUALITY` `5`. If subscribers or the HUB fall behind, lower `FRAME_RATE` or raise `QUALITY` (a larger quantizer) before you increase width and height.

For a live camera, shed frames instead of blocking publishers:

```lot wrap focus={8-9} theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE EntranceCamera WITH TYPE VIDEO_INPUT
    ADD VIDEO_INPUT_CONFIG
        WITH SOURCE GET ENV "CAMERA_RTSP_URL"
        WITH FRAME_RATE "5"
        WITH WIDTH "640"
        WITH HEIGHT "480"
        WITH QUALITY "5"
        WITH DESTINATION_TOPIC "video/entrance/frames"
    ADD ROUTE_CONFIG
        WITH OVERLOAD_POLICY "realtime"
    ADD EVENT StartCapture
        WITH QUERY "START"
        WITH SOURCE_TOPIC "video/entrance/control/start"
    ADD EVENT StopCapture
        WITH QUERY "STOP"
        WITH SOURCE_TOPIC "video/entrance/control/stop"
```

See [Connector overload](../store-and-forward).

## Pair with Video Output

`TYPE VIDEO_OUTPUT` serves those JPEGs as an HTTP MJPEG stream. Set its source topic to this connector's frame topic.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE OperatorView WITH TYPE VIDEO_OUTPUT
    ADD VIDEO_OUTPUT_CONFIG
        WITH SOURCE_TOPIC "video/entrance/frames"
        WITH OUTPUT_PROTOCOL "HTTP"
        WITH OUTPUT_PATH ":8080/entrance"
    ADD EVENT StartView
        WITH QUERY "START"
```

Operators open `http://<broker-host>:8080/entrance`.

The same frame topic can feed a [Barcode](./barcode) decode event. [Audio Input](./audio-input) is the microphone equivalent: raw PCM chunks instead of JPEGs, also through FFmpeg.

## Best Practices

> **Keep secrets out of LoT**
>
> `WITH SOURCE GET ENV "CAMERA_RTSP_URL"` or `GET SECRET`. Do not paste `rtsp://user:password@…` into the connector.

> **Match the host**
>
> `webcam:N` on macOS and Linux. `video=Exact Name` on Windows. A missing `video=` prefix is opened as a file.

> **Separate control from frames**
>
> Put `START` and `STOP` on `…/control/…` topics. Publishing `START` on the frame topic mixes commands with JPEG payloads.

> **Prefer a network camera in Docker Desktop**
>
> USB cameras rarely appear inside macOS or Windows Docker Desktop. Use RTSP or HTTP unless the broker runs on Linux with a device mount.

## Troubleshooting

> **FFmpeg is missing**
>
> `ffmpeg -version` on the broker host. Install FFmpeg there, or switch to the slim image.

> **webcam:0 opens as a file**
>
> Use `webcam:0` on macOS and Linux. On Windows use `video=` plus the DirectShow name.

> **Linux device missing**
>
> `ls -l /dev/video*`. Plug in the camera or pass `--device /dev/video0` into the container.

> **Windows rejects webcam:N**
>
> List devices with `ffmpeg -list_devices true -f dshow -i dummy` and set `WITH SOURCE "video=…"`.

> **macOS permission or a busy camera**
>
> Grant Camera access to the process that launched the broker. Close other apps using the camera. Try `webcam:1` for a USB camera.

> **RTSP or HTTP never starts**
>
> The timeout is 10 seconds. From the broker host, confirm the URL is reachable and that RTSP allows TCP. Put the full URL in `GET ENV`.

> **Dashboards stall**
>
> Lower `FRAME_RATE`, shrink `WIDTH` and `HEIGHT`, or raise `QUALITY`. Add `ADD ROUTE_CONFIG` with `OVERLOAD_POLICY` `realtime`.

## Next Steps

> **[Audio Input](./audio-input)**
>
> Publish microphone PCM the same way, through FFmpeg on the broker host.

> **[Barcode Connector](./barcode)**
>
> Decode a symbol from the JPEG topic.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.