🎥 Cameras
A camera entry says where frames come from and what the bridge is allowed to produce from them. At most 50 cameras live in one file.
cameras:
front:
source:
kind: ros
topic: /camera/image_raw
type: sensor_msgs/msg/Image
width: 1280
height: 720
fps: 15
bitrate_kbps: 2000
snapshot_interval_seconds: 5
description: Forward-facing camera on the mast.
| field | type | bounds | absent means |
|---|---|---|---|
source.kind |
ros, rtsp, mjpeg or v4l2 |
— | required |
width |
integer | 1 to 7680 pixels |
required |
height |
integer | 1 to 4320 pixels |
required |
fps |
integer | 1 to 60 |
required |
bitrate_kbps |
integer | 1 to 50000 |
required |
snapshot_interval_seconds |
integer | 1 to 3600 |
required |
description |
text | 1 to 2000 characters | offered as description: null |
kind fixes which other fields the source may carry, so an impossible camera
is unrepresentable rather than merely invalid. There is no way to write an
RTSP camera with a ROS topic.
kind |
fields |
|---|---|
ros |
topic (ROS graph name), type (sensor_msgs/msg/Image or sensor_msgs/msg/CompressedImage) |
rtsp |
url (rtsp:// or rtsps://, at most 2048 characters), optional transport (tcp or udp, default tcp), optional credentials |
mjpeg |
url (http:// or https://, at most 2048 characters), optional credentials |
v4l2 |
device (a path under /dev/, at most 128 characters) |
What the four numbers bound
width, height, fps and bitrate_kbps are the resolution and load the
bridge produces before sending, not the camera’s own. Together they are
your control over the robot’s bandwidth, which is why they live in the
configuration and not in a viewer’s request: a viewer must never be able to
make a robot send more.
fps is a ceiling and not a clock, so a camera delivering ten frames a second
stays at ten. bitrate_kbps bounds the live encoding only. Raising the
resolution or the frame rate against a fixed bitrate buys blur, not detail.
A snapshot can arrive smaller than width and height. Its JPEG has a
byte ceiling, and the bridge gives up quality first and then resolution to fit
it, reporting the size it actually encoded.
Snapshots always run
snapshot_interval_seconds is how often a still frame is captured, and it
runs whether or not anyone is watching. The cloud holds the one frame and
serves every reader from it, so a hundred pollers cost the robot one image per
interval.
Live streaming is the opposite: the cloud counts viewers, the first one starts
the stream and the last one ends it. In
low-bandwidth mode a running
stream is re-encoded at camera_bitrate_kbps or ended outright, and a new one
is refused with the code low_bandwidth.
The four sources
ros— the only source the bridge subscribes to rather than opens, so it needs no URL, no device and nobody to authenticate to.rtsp— a network camera the robot itself can reach.udploses frames on a congested link and loses them silently, which is why an omittedtransportmeanstcp.mjpeg— one JPEG after another over HTTP. There is no transport to choose, and credentials travel as HTTP Basic.v4l2— a capture device attached to the robot, such as a USB camera. A/dev/v4l/by-id/...symlink survives a reboot that renumbers/dev/video0.
The scheme restrictions and the /dev/ requirement are not a formality.
The bridge opens these values with libraries that would as happily serve
file: or an ordinary video file, so without the restriction a configuration
document would be arbitrary file access on the robot. The bridge re-derives
the same constraints itself rather than trusting the wire, and a device may
hold no .. segment and may not end in a slash.
Credentials
source:
kind: rtsp
url: rtsp://cam-1.plant.local/stream1
credentials:
username: ops
password: "..."
Both fields are 1 to 128 characters and both are optional. For MJPEG the
bridge sends a real Authorization: Basic header and leaves the URL
untouched. RTSP offers no such channel, so there the name and password go
inside a connect URL built fresh for that one call and never written back into
the stored document.
Credentials may also sit in the URL itself, as
rtsp://user:pass@host/stream. Where both are present, the explicit block
wins.
There is no secret store behind this. A password written here is in every published version, and those are immutable. It cannot be removed from history and cannot be rotated without republishing, and anyone who may read a robot's configuration history may read every camera password that was ever in it.
One rule bounds that: the publish audit event carries the version number and never the document body.
Plain http:// is permitted for an MJPEG camera, because these usually sit on
the robot’s own network. Basic credentials on such a URL then travel in the
clear, which is a choice this format lets you make and does not pretend is
free.
A camera slug shares one namespace with every datapoint, action, service and publisher in the file. The grammar and the reserved names are on the overview.