You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix media pipelines, recording lifecycle, signaling, and service installation
Preserve cross-platform capture and encoder fallbacks, validate configuration and signaling inputs, clean up failed recording setup, and improve operational guides. Fix portable HLS serving and room NDI codec dispatch. Validated with 149 application tests, 218 external regressions, and focused Pi 3/Pi 5 checks.
-`--v4l2sink` accepts a numeric index (`0`) or a full path (`/dev/video2`).
814
+
- Select a writable video output such as a configured `v4l2loopback` device.
815
+
Raspberry Pi ISP and codec nodes are internal processing devices, not virtual
816
+
cameras, and are excluded from output discovery. If no suitable output exists,
817
+
configure a loopback device first and check it with `v4l2-ctl -d /dev/video2 -D`.
814
818
- If the specified device is not writable, the first writable `/dev/video*` is used.
815
819
- When no remote video is available, a blue frame is output to keep the device alive.
816
820
- The V4L2 sink path drops upstream allocation queries before `v4l2sink` to avoid buffer-pool issues with some `v4l2loopback` versions. The default V4L2 sink I/O mode is GStreamer's auto mode (`--v4l2sink-io-mode 0`); use `--v4l2sink-io-mode 1` to force read/write mode.
Copy file name to clipboardExpand all lines: docs/operations-guide.md
+153Lines changed: 153 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,15 @@ python3 -u publish.py \
58
58
59
59
VP9 is software-heavy on small boards. Validate it at a low resolution before increasing load.
60
60
61
+
## Multiple viewers and stalled connections
62
+
63
+
Use `--multiviewer` to share one encoded stream with multiple viewers. Each
64
+
viewer has bounded audio and video queues. If one viewer stops accepting media,
65
+
its queues drop old packets when full so the other viewers can keep receiving.
66
+
The affected viewer may have audio gaps or need the next video keyframe when it
67
+
recovers. This does not increase the publisher's available upload bandwidth;
68
+
choose a bitrate that leaves room for all viewers.
69
+
61
70
## Publish a USB camera or HDMI capture device
62
71
63
72
List devices and the modes of the intended capture node:
@@ -131,6 +140,80 @@ The default preserves aspect ratio on whatever mode the display advertises. Add
131
140
132
141
The receiver remains available while the sender is absent. Automatic retry defaults to a short sequence followed by a longer interval. Use `--no-auto-retry` only for a supervised diagnostic run.
133
142
143
+
After a detected disconnect, `--viewer-retry-initial` waits 15 seconds by default
144
+
before the first play request. The next request waits `--viewer-retry-short`
145
+
(45 seconds), and later requests use `--viewer-retry-long` (180 seconds).
146
+
Set `--viewer-retry-initial 0` for an immediate first retry. These intervals govern
147
+
viewer play requests, separately from reconnecting to the signaling server.
148
+
If a request cannot be scheduled because the signaling loop is unavailable, it
149
+
does not advance the retry count; another attempt is scheduled after the long
150
+
delay. Successful peer creation resets the retry sequence.
151
+
152
+
## Save settings in a JSON configuration
153
+
154
+
Create `sender.json` with JSON booleans and numbers (without quotes):
155
+
156
+
```json
157
+
{
158
+
"streamid": "my-camera",
159
+
"password": "replace-with-your-shared-password",
160
+
"test": true,
161
+
"h264": true,
162
+
"noaudio": true,
163
+
"width": 640,
164
+
"height": 360,
165
+
"framerate": 15,
166
+
"bitrate": 500
167
+
}
168
+
```
169
+
170
+
Run `python3 publish.py --config sender.json`. Use the same stream ID and
171
+
password in the viewer. Protect files containing passwords with
172
+
`chmod 600 sender.json`; do not post them in bug reports.
173
+
174
+
Keys normally use argument destination names: `streamid`, `noaudio`, and
175
+
`video_pipeline`, for example. Installer-style `stream_id` is also accepted.
176
+
If both names are present, `streamid` takes precedence over `stream_id` regardless
177
+
of JSON key order. Likewise, `noaudio` takes precedence over legacy
178
+
`audio_enabled`. Explicit command-line options still take precedence over the file.
179
+
Legacy `video_source` accepts `test`, `libcamera`, `v4l2`, or `custom`.
180
+
`custom` requires a non-empty `custom_video_pipeline`; `v4l2` uses `/dev/video0`
181
+
when `video_device` is omitted, but rejects an explicitly empty or null device.
182
+
Invalid source selections stop startup instead of silently falling back to a camera.
183
+
Save as UTF-8; files with a UTF-8 byte-order mark are supported. A missing file,
184
+
invalid JSON, or a root value other than an object stops startup with an error.
185
+
Boolean flags require `true` or `false`; integer settings such as `bitrate`
such as `"500"` and boolean strings such as `"false"` are rejected rather than
188
+
interpreted as flags or passed into media setup. Options with a fixed set of
189
+
choices, such as `ice_transport_policy`, use the same choices as the CLI.
190
+
Text settings, including stream IDs, passwords, device paths, and custom
191
+
pipelines, require JSON strings. To disable the password, use `"password": "false"`
192
+
(a string); `"noaudio": false` is a boolean flag. Optional text settings whose
193
+
default is unset also accept `null`.
194
+
195
+
Explicit command-line values override saved settings, even if the value equals
196
+
the built-in default. For example, `--config sender.json --bitrate 2500` uses
197
+
2500 kbps. Unique long-option abbreviations follow the same rule, but use full
198
+
option names in scripts so future options cannot make an abbreviation ambiguous.
199
+
200
+
An explicit codec or encoder flag also replaces saved codec-selection flags.
201
+
For example, `--config sender.json --x264` selects H.264 even if the file enables
202
+
VP8 or AV1. Other saved settings, including bitrate and platform hints, still
203
+
apply. Without an explicit codec flag, the saved codec selection is used.
204
+
205
+
Likewise, `--alsa`, `--pulse`, `--audio-pipeline`, or `--noaudio` replaces saved
206
+
audio-source and audio-enable settings. For example, an explicit `--alsa DEVICE`
207
+
enables that source even if the file contains `"noaudio": true`. Saved audio
208
+
bitrate and other unrelated options still apply.
209
+
210
+
Choosing a video source on the command line also suppresses saved video-source
211
+
flags. For example, `--config sender.json --v4l2 /dev/video2` replaces the saved
212
+
test source with that camera. Conversely, `--test` replaces a saved camera source.
213
+
Other settings, including resolution, codec, bitrate, and password, still apply;
214
+
ensure they are suitable for the replacement source. This changes only the current
215
+
invocation, not the JSON file or an already-running service.
216
+
134
217
## Run unattended with systemd
135
218
136
219
First prove the exact command interactively. Then use `tools/install_unattended.py` to create a validated receiver or sender unit whose user and working directory match the installed clone. Complete examples are in the [Pi Zero 2 W guide](pi-zero-2-w-unattended-webrtc.md#8-make-the-receiver-start-on-boot).
The helper uses `Restart=always`, a small `RestartSec`, unbuffered Python output, and `network-online.target`. It stores credentials in a restricted JSON config instead of the unit command. Running the installer again validates the replacement unit and restarts the existing service so new settings take effect.
148
231
232
+
`--service-name` accepts up to 247 letters, digits, underscores, dots, hyphens,
233
+
or `@` characters before the generated `.service` suffix. It must not start with
234
+
`-` or `@`. Use a concrete instance such as `camera@front`, rather than `camera@`.
235
+
236
+
For senders, omit `--audio-device` to disable audio; an empty value is invalid.
237
+
`--camera` requires a non-empty device path. `--allow-missing-device` permits an
238
+
unplugged device but does not allow an empty path or a directory.
239
+
240
+
Configuration writes use a private temporary file in the destination directory,
241
+
then replace the destination after setting its permissions and ownership. The shared
242
+
config directory is root-owned with mode `0711` (traversal without listing), and
243
+
each config uses mode `0640` with its service user's group. Installing another
244
+
service under a different user therefore preserves access to existing configs. If that
245
+
write fails, the temporary file is removed and the previous destination remains
246
+
intact. This protects the file update; it does not guarantee that newly selected
247
+
camera or network settings will work. Check the service status and journal after
248
+
each reconfiguration. `--dry-run` prints the proposed configuration, including its
249
+
password, so keep that output private.
250
+
251
+
If writing or verifying the generated files fails before systemd is reloaded,
252
+
the installer restores replaced files atomically, including their previous
253
+
permissions and ownership, and restores the config directory's metadata.
254
+
An incomplete rollback is reported explicitly. This rollback does not cover
255
+
failures during the later service reload, enable, or restart steps.
256
+
257
+
Relative `--python` and `--camera` paths become absolute from the installer's working
258
+
directory without resolving virtual-environment or stable device symlinks. The generated service treats paths
259
+
literally, including spaces, percent signs, and dollar signs. Paths containing line breaks or NUL
260
+
bytes are rejected. Use `--dry-run` to inspect paths before installation.
261
+
262
+
## Record while publishing to RTMP
263
+
264
+
For RTMP publishing, `--save` also writes a local timestamped `.mkv` recording.
265
+
The RTMP output stays connected to its muxer even if `--multiviewer` is present;
266
+
that flag's dynamic viewer branches apply to WebRTC publishing. Verify recording
267
+
output and available disk space before leaving a sender unattended.
268
+
269
+
For a V4L2 camera that advertises H.264, `--v4l2 /dev/videoN --format H264`
270
+
can send the camera's encoded video directly to RTMP without decoding and
0 commit comments