toposync-ext-streaming 0.4.4__tar.gz → 0.4.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/PKG-INFO +19 -13
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/README.md +18 -12
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/pyproject.toml +1 -1
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/api/models.py +31 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/api/routes.py +422 -14
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/pipelines/operators.py +13 -4
- toposync_ext_streaming-0.4.6/src/toposync_ext_streaming/static/703.js +2 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/resize.py +39 -14
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/wizard/pipeline_builder.py +15 -5
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/settings/StreamingSettingsPanel.tsx +194 -6
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/translations.ts +32 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/types.ts +18 -0
- toposync_ext_streaming-0.4.4/src/toposync_ext_streaming/static/703.js +0 -2
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/.gitignore +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/LICENSE +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/LICENSE.ffmpeg +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/LICENSE.mediamtx +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/__init__.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/api/__init__.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/bin/ffmpeg/LICENSE +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/bin/mediamtx/LICENSE +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/extension.json +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/pipelines/__init__.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/plugin.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/326.js +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/326.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/387.js +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/4.js +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/4.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/623.js +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/623.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/703.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/main.js +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/main.js.LICENSE.txt +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/remoteEntry.js +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/__init__.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/arbitration.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/camera_ingest.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/distributed_sync.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/encoder_state.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/engine_manager.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/ffmpeg_binary.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/go2rtc_binary.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/go2rtc_config.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/go2rtc_manager.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/ingest_auth.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/ingest_resolver.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/jsmpeg_manager.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_api_client.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_binary.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_config.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_processes.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/placeholder.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/platform.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/playback_events.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/publisher_manager.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/runtime_state.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/writer_bridge.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/wizard/__init__.py +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/package.json +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/activate.tsx +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/api/streamingApi.ts +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/constants.ts +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/entry.ts +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/settings/SubModal.tsx +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/settings/WizardCreatePipelineFromTransmission.tsx +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/tsconfig.json +0 -0
- {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/webpack.config.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toposync-ext-streaming
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.6
|
|
4
4
|
Summary: Toposync first-party extension: streaming settings, API surface, and pipeline sink bootstrap.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -17,7 +17,7 @@ Extension ID: `com.toposync.streaming`
|
|
|
17
17
|
|
|
18
18
|
This extension provides camera and pipeline video publication in Toposync:
|
|
19
19
|
|
|
20
|
-
- Users normally publish **camera sources** with a `
|
|
20
|
+
- Users normally publish **camera sources** with a `Transmit this source` intent.
|
|
21
21
|
- The extension reconciles generated **CameraLiveView**, **Transmission**, **Outputs**, and implicit continuous pipelines.
|
|
22
22
|
- Advanced pipelines can publish a rendered variant through **`stream.publish_video`**.
|
|
23
23
|
- A local **MediaMTX** engine serves RTSP/HLS/WebRTC (WHEP) URLs.
|
|
@@ -32,7 +32,7 @@ The core design goal is reliability in local-first setups with highly dynamic pi
|
|
|
32
32
|
- Encoding should happen only when there is actual playback demand.
|
|
33
33
|
|
|
34
34
|
The canonical product and engineering principles for streaming are documented in
|
|
35
|
-
[`docs/
|
|
35
|
+
[`docs-site/docs/developers/media-decisions.mdx`](../../docs-site/docs/developers/media-decisions.mdx).
|
|
36
36
|
In short:
|
|
37
37
|
|
|
38
38
|
- User-facing flows deal with publishable sources and variants; `Transmission`, outputs, engine paths, and quality profile IDs are advanced artifacts.
|
|
@@ -60,6 +60,8 @@ In short:
|
|
|
60
60
|
- Demand heartbeat for web, app, PiP, PTZ, and Home Assistant entity playback.
|
|
61
61
|
- Distributed hosting via `Transmission.host_server_id`, plus URL proxying and processing-side settings sync.
|
|
62
62
|
- Dashboard playback using a backend Playback Plan with HLS/WebRTC, MSE through the optional go2rtc sidecar, and JSMpeg as an on-demand emergency visual fallback.
|
|
63
|
+
- Synthetic playback URLs for MSE and JSMpeg, derived from a real backing output instead of persisted as `TransmissionOutput` rows.
|
|
64
|
+
- Media `content_rect` metadata for contain-resized outputs, so spatial video can remove transport letterboxing without asking the user to recalibrate.
|
|
63
65
|
|
|
64
66
|
## Supported protocols (as implemented)
|
|
65
67
|
|
|
@@ -87,12 +89,14 @@ In short:
|
|
|
87
89
|
- The browser never talks to go2rtc directly. Toposync verifies the signed media token and proxies text control messages plus binary fMP4 fragments.
|
|
88
90
|
- Dashboard Auto can prefer MSE for passive web/grid/fullscreen playback when the sidecar is enabled/startable and the backing output is browser-compatible.
|
|
89
91
|
- A stopped go2rtc process is normal when no MSE viewer is connected. Toposync returns a signed MSE URL when the sidecar can be started, then starts/updates go2rtc on the first MSE WebSocket session.
|
|
92
|
+
- MSE is synthetic. It is generated from a real HLS/backing output and is not stored as `TransmissionOutput(protocol="mse")`.
|
|
90
93
|
|
|
91
94
|
### JSMpeg
|
|
92
95
|
- URL format: `ws://<toposync-host>/api/streams/media/jsmpeg/<path>/ws?media_token=...`
|
|
93
96
|
- Intended only as an emergency visual fallback. It is video-only, low resolution/FPS, and does not carry audio.
|
|
94
97
|
- Each browser WebSocket creates one isolated FFmpeg process that converts the selected runtime frame stream to MPEG-TS/MPEG-1. The process is stopped when the WebSocket closes.
|
|
95
98
|
- The source is the selected Toposync Transmission frame, or an explicit placeholder while warming up/offline. It never pulls camera RTSP directly.
|
|
99
|
+
- JSMpeg is synthetic. It is generated from a real backing output and is not stored as `TransmissionOutput(protocol="jsmpeg")`.
|
|
96
100
|
|
|
97
101
|
### Playback Plan candidates
|
|
98
102
|
|
|
@@ -111,11 +115,11 @@ RTSP is not a browser transport. It remains the internal/ecosystem contract for
|
|
|
111
115
|
|
|
112
116
|
Camera source publication:
|
|
113
117
|
|
|
114
|
-
`camera source` -> `StreamPublicationSpec` -> reconciler -> implicit pipeline -> `stream.publish_video` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP)
|
|
118
|
+
`camera source` -> `StreamPublicationSpec` -> reconciler -> implicit pipeline -> `stream.publish_video` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP/MSE) plus Toposync JSMpeg fallback
|
|
115
119
|
|
|
116
120
|
Advanced pipeline publication:
|
|
117
121
|
|
|
118
|
-
`pipeline frames` -> `stream.publish_video` -> generated/manual `Transmission` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP)
|
|
122
|
+
`pipeline frames` -> `stream.publish_video` -> generated/manual `Transmission` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP/MSE) plus Toposync JSMpeg fallback
|
|
119
123
|
|
|
120
124
|
### Components
|
|
121
125
|
|
|
@@ -205,7 +209,7 @@ Context defaults:
|
|
|
205
209
|
- `large` and `fullscreen`: prefer `main`.
|
|
206
210
|
- `ptz`: prefer `zoom`, then `main`, then `sub`.
|
|
207
211
|
|
|
208
|
-
This is intentionally separate from technical quality labels. The dashboard source selector should expose labels such as "
|
|
212
|
+
This is intentionally separate from technical quality labels. The dashboard source selector should expose labels such as "Main", "Low resolution", "Zoom", or custom names, not internal output ids.
|
|
209
213
|
|
|
210
214
|
### Reconciliation rules
|
|
211
215
|
|
|
@@ -264,6 +268,8 @@ Each output is independently configurable:
|
|
|
264
268
|
|
|
265
269
|
Notes:
|
|
266
270
|
- Authentication is applied only when `enabled == true` and both `username` and `password` are present (non-empty).
|
|
271
|
+
- MSE and JSMpeg are not valid persisted output protocols. The API can still return signed MSE/JSMpeg playback URLs when a compatible backing output exists.
|
|
272
|
+
- URL responses include optional `content_rect` metadata. For `resize_mode="contain"`, it identifies the useful non-letterboxed rectangle inside the output frame. Consumers such as `spatial_video` remap UVs through this rectangle instead of asking the user to recalibrate.
|
|
267
273
|
- The API model allows extra fields (`extra="allow"`). Some runtime behavior also reads optional extra fields:
|
|
268
274
|
- `output.path` (override the resolved engine path)
|
|
269
275
|
- `output.resize_mode` (`contain` or `none`; best-effort)
|
|
@@ -614,7 +620,7 @@ Core auth (enforced mode):
|
|
|
614
620
|
|
|
615
621
|
For regular cameras, streaming is configured from the camera source itself:
|
|
616
622
|
|
|
617
|
-
- each video source can show a `
|
|
623
|
+
- each video source can show a `Transmit this source` checkbox;
|
|
618
624
|
- the source role is used as the publication role (`main`, `sub`, `zoom`, `custom`);
|
|
619
625
|
- the visible source label becomes the publication label;
|
|
620
626
|
- ONVIF-discovered video sources can be published by default;
|
|
@@ -640,7 +646,7 @@ The main UI includes a "Streams" rendering mode with:
|
|
|
640
646
|
|
|
641
647
|
- Grid modes `1x1` and `2x2` with pagination.
|
|
642
648
|
- Auto-hide overlay.
|
|
643
|
-
- Source/role selector using camera variants, for example
|
|
649
|
+
- Source/role selector using camera variants, for example Main, Low resolution, Zoom, or custom names.
|
|
644
650
|
- Playback strategy:
|
|
645
651
|
1. Pick the best variant for the visual context.
|
|
646
652
|
2. Request the backend Playback Plan for that transmission/output/context.
|
|
@@ -861,7 +867,7 @@ Camera source publication (`GET /api/streams/publications?camera_id=front`):
|
|
|
861
867
|
"camera_source_id": "sub",
|
|
862
868
|
"enabled": true,
|
|
863
869
|
"role": "sub",
|
|
864
|
-
"label": "
|
|
870
|
+
"label": "Low resolution",
|
|
865
871
|
"host_server_id": "local",
|
|
866
872
|
"quality_policy": {},
|
|
867
873
|
"transport_policy": {}
|
|
@@ -889,7 +895,7 @@ Demand (`GET /api/streams/transmissions/{id}/demand`):
|
|
|
889
895
|
For normal use:
|
|
890
896
|
|
|
891
897
|
1. Add/discover a camera in the Cameras extension.
|
|
892
|
-
2. Keep `
|
|
898
|
+
2. Keep `Transmit this source` enabled on the desired video sources.
|
|
893
899
|
3. Save the camera/source.
|
|
894
900
|
4. The streaming reconciler creates the live view, generated transmissions, outputs, and implicit pipelines.
|
|
895
901
|
5. Open the dashboard and select the camera/source role.
|
|
@@ -901,7 +907,7 @@ curl http://127.0.0.1:8100/api/streams/publications?camera_id=<camera_id>
|
|
|
901
907
|
|
|
902
908
|
curl -X PUT http://127.0.0.1:8100/api/streams/publications/camera-sources/<camera_id>/<source_id> \
|
|
903
909
|
-H 'content-type: application/json' \
|
|
904
|
-
-d '{"enabled": true, "role": "sub", "label": "
|
|
910
|
+
-d '{"enabled": true, "role": "sub", "label": "Low resolution"}'
|
|
905
911
|
|
|
906
912
|
curl -X POST http://127.0.0.1:8100/api/streams/reconcile
|
|
907
913
|
```
|
|
@@ -1000,7 +1006,7 @@ Likely causes:
|
|
|
1000
1006
|
- `stream.publish_video` has not received a frame yet.
|
|
1001
1007
|
|
|
1002
1008
|
Fix:
|
|
1003
|
-
- For camera streams, check the camera source and keep `
|
|
1009
|
+
- For camera streams, check the camera source and keep `Transmit this source` enabled.
|
|
1004
1010
|
- Call `POST /api/streams/reconcile`.
|
|
1005
1011
|
- Check `GET /api/streams/runtime/pipelines` to see which pipeline owns the generated transmission.
|
|
1006
1012
|
- Check `GET /api/streams/runtime/health` for `active_writer_id`, `selected_writer_id`, `fallback_reason`, and frame age.
|
|
@@ -1008,7 +1014,7 @@ Fix:
|
|
|
1008
1014
|
The primary user-facing message for this class is:
|
|
1009
1015
|
|
|
1010
1016
|
```text
|
|
1011
|
-
|
|
1017
|
+
No pipeline is feeding this transmission.
|
|
1012
1018
|
```
|
|
1013
1019
|
|
|
1014
1020
|
### HLS plays but WebRTC warning appears
|
|
@@ -4,7 +4,7 @@ Extension ID: `com.toposync.streaming`
|
|
|
4
4
|
|
|
5
5
|
This extension provides camera and pipeline video publication in Toposync:
|
|
6
6
|
|
|
7
|
-
- Users normally publish **camera sources** with a `
|
|
7
|
+
- Users normally publish **camera sources** with a `Transmit this source` intent.
|
|
8
8
|
- The extension reconciles generated **CameraLiveView**, **Transmission**, **Outputs**, and implicit continuous pipelines.
|
|
9
9
|
- Advanced pipelines can publish a rendered variant through **`stream.publish_video`**.
|
|
10
10
|
- A local **MediaMTX** engine serves RTSP/HLS/WebRTC (WHEP) URLs.
|
|
@@ -19,7 +19,7 @@ The core design goal is reliability in local-first setups with highly dynamic pi
|
|
|
19
19
|
- Encoding should happen only when there is actual playback demand.
|
|
20
20
|
|
|
21
21
|
The canonical product and engineering principles for streaming are documented in
|
|
22
|
-
[`docs/
|
|
22
|
+
[`docs-site/docs/developers/media-decisions.mdx`](../../docs-site/docs/developers/media-decisions.mdx).
|
|
23
23
|
In short:
|
|
24
24
|
|
|
25
25
|
- User-facing flows deal with publishable sources and variants; `Transmission`, outputs, engine paths, and quality profile IDs are advanced artifacts.
|
|
@@ -47,6 +47,8 @@ In short:
|
|
|
47
47
|
- Demand heartbeat for web, app, PiP, PTZ, and Home Assistant entity playback.
|
|
48
48
|
- Distributed hosting via `Transmission.host_server_id`, plus URL proxying and processing-side settings sync.
|
|
49
49
|
- Dashboard playback using a backend Playback Plan with HLS/WebRTC, MSE through the optional go2rtc sidecar, and JSMpeg as an on-demand emergency visual fallback.
|
|
50
|
+
- Synthetic playback URLs for MSE and JSMpeg, derived from a real backing output instead of persisted as `TransmissionOutput` rows.
|
|
51
|
+
- Media `content_rect` metadata for contain-resized outputs, so spatial video can remove transport letterboxing without asking the user to recalibrate.
|
|
50
52
|
|
|
51
53
|
## Supported protocols (as implemented)
|
|
52
54
|
|
|
@@ -74,12 +76,14 @@ In short:
|
|
|
74
76
|
- The browser never talks to go2rtc directly. Toposync verifies the signed media token and proxies text control messages plus binary fMP4 fragments.
|
|
75
77
|
- Dashboard Auto can prefer MSE for passive web/grid/fullscreen playback when the sidecar is enabled/startable and the backing output is browser-compatible.
|
|
76
78
|
- A stopped go2rtc process is normal when no MSE viewer is connected. Toposync returns a signed MSE URL when the sidecar can be started, then starts/updates go2rtc on the first MSE WebSocket session.
|
|
79
|
+
- MSE is synthetic. It is generated from a real HLS/backing output and is not stored as `TransmissionOutput(protocol="mse")`.
|
|
77
80
|
|
|
78
81
|
### JSMpeg
|
|
79
82
|
- URL format: `ws://<toposync-host>/api/streams/media/jsmpeg/<path>/ws?media_token=...`
|
|
80
83
|
- Intended only as an emergency visual fallback. It is video-only, low resolution/FPS, and does not carry audio.
|
|
81
84
|
- Each browser WebSocket creates one isolated FFmpeg process that converts the selected runtime frame stream to MPEG-TS/MPEG-1. The process is stopped when the WebSocket closes.
|
|
82
85
|
- The source is the selected Toposync Transmission frame, or an explicit placeholder while warming up/offline. It never pulls camera RTSP directly.
|
|
86
|
+
- JSMpeg is synthetic. It is generated from a real backing output and is not stored as `TransmissionOutput(protocol="jsmpeg")`.
|
|
83
87
|
|
|
84
88
|
### Playback Plan candidates
|
|
85
89
|
|
|
@@ -98,11 +102,11 @@ RTSP is not a browser transport. It remains the internal/ecosystem contract for
|
|
|
98
102
|
|
|
99
103
|
Camera source publication:
|
|
100
104
|
|
|
101
|
-
`camera source` -> `StreamPublicationSpec` -> reconciler -> implicit pipeline -> `stream.publish_video` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP)
|
|
105
|
+
`camera source` -> `StreamPublicationSpec` -> reconciler -> implicit pipeline -> `stream.publish_video` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP/MSE) plus Toposync JSMpeg fallback
|
|
102
106
|
|
|
103
107
|
Advanced pipeline publication:
|
|
104
108
|
|
|
105
|
-
`pipeline frames` -> `stream.publish_video` -> generated/manual `Transmission` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP)
|
|
109
|
+
`pipeline frames` -> `stream.publish_video` -> generated/manual `Transmission` -> `TransmissionRuntimeState` -> `StreamWriterBridge` -> `FFmpeg publisher` -> `MediaMTX path` -> viewers (RTSP/HLS/WHEP/MSE) plus Toposync JSMpeg fallback
|
|
106
110
|
|
|
107
111
|
### Components
|
|
108
112
|
|
|
@@ -192,7 +196,7 @@ Context defaults:
|
|
|
192
196
|
- `large` and `fullscreen`: prefer `main`.
|
|
193
197
|
- `ptz`: prefer `zoom`, then `main`, then `sub`.
|
|
194
198
|
|
|
195
|
-
This is intentionally separate from technical quality labels. The dashboard source selector should expose labels such as "
|
|
199
|
+
This is intentionally separate from technical quality labels. The dashboard source selector should expose labels such as "Main", "Low resolution", "Zoom", or custom names, not internal output ids.
|
|
196
200
|
|
|
197
201
|
### Reconciliation rules
|
|
198
202
|
|
|
@@ -251,6 +255,8 @@ Each output is independently configurable:
|
|
|
251
255
|
|
|
252
256
|
Notes:
|
|
253
257
|
- Authentication is applied only when `enabled == true` and both `username` and `password` are present (non-empty).
|
|
258
|
+
- MSE and JSMpeg are not valid persisted output protocols. The API can still return signed MSE/JSMpeg playback URLs when a compatible backing output exists.
|
|
259
|
+
- URL responses include optional `content_rect` metadata. For `resize_mode="contain"`, it identifies the useful non-letterboxed rectangle inside the output frame. Consumers such as `spatial_video` remap UVs through this rectangle instead of asking the user to recalibrate.
|
|
254
260
|
- The API model allows extra fields (`extra="allow"`). Some runtime behavior also reads optional extra fields:
|
|
255
261
|
- `output.path` (override the resolved engine path)
|
|
256
262
|
- `output.resize_mode` (`contain` or `none`; best-effort)
|
|
@@ -601,7 +607,7 @@ Core auth (enforced mode):
|
|
|
601
607
|
|
|
602
608
|
For regular cameras, streaming is configured from the camera source itself:
|
|
603
609
|
|
|
604
|
-
- each video source can show a `
|
|
610
|
+
- each video source can show a `Transmit this source` checkbox;
|
|
605
611
|
- the source role is used as the publication role (`main`, `sub`, `zoom`, `custom`);
|
|
606
612
|
- the visible source label becomes the publication label;
|
|
607
613
|
- ONVIF-discovered video sources can be published by default;
|
|
@@ -627,7 +633,7 @@ The main UI includes a "Streams" rendering mode with:
|
|
|
627
633
|
|
|
628
634
|
- Grid modes `1x1` and `2x2` with pagination.
|
|
629
635
|
- Auto-hide overlay.
|
|
630
|
-
- Source/role selector using camera variants, for example
|
|
636
|
+
- Source/role selector using camera variants, for example Main, Low resolution, Zoom, or custom names.
|
|
631
637
|
- Playback strategy:
|
|
632
638
|
1. Pick the best variant for the visual context.
|
|
633
639
|
2. Request the backend Playback Plan for that transmission/output/context.
|
|
@@ -848,7 +854,7 @@ Camera source publication (`GET /api/streams/publications?camera_id=front`):
|
|
|
848
854
|
"camera_source_id": "sub",
|
|
849
855
|
"enabled": true,
|
|
850
856
|
"role": "sub",
|
|
851
|
-
"label": "
|
|
857
|
+
"label": "Low resolution",
|
|
852
858
|
"host_server_id": "local",
|
|
853
859
|
"quality_policy": {},
|
|
854
860
|
"transport_policy": {}
|
|
@@ -876,7 +882,7 @@ Demand (`GET /api/streams/transmissions/{id}/demand`):
|
|
|
876
882
|
For normal use:
|
|
877
883
|
|
|
878
884
|
1. Add/discover a camera in the Cameras extension.
|
|
879
|
-
2. Keep `
|
|
885
|
+
2. Keep `Transmit this source` enabled on the desired video sources.
|
|
880
886
|
3. Save the camera/source.
|
|
881
887
|
4. The streaming reconciler creates the live view, generated transmissions, outputs, and implicit pipelines.
|
|
882
888
|
5. Open the dashboard and select the camera/source role.
|
|
@@ -888,7 +894,7 @@ curl http://127.0.0.1:8100/api/streams/publications?camera_id=<camera_id>
|
|
|
888
894
|
|
|
889
895
|
curl -X PUT http://127.0.0.1:8100/api/streams/publications/camera-sources/<camera_id>/<source_id> \
|
|
890
896
|
-H 'content-type: application/json' \
|
|
891
|
-
-d '{"enabled": true, "role": "sub", "label": "
|
|
897
|
+
-d '{"enabled": true, "role": "sub", "label": "Low resolution"}'
|
|
892
898
|
|
|
893
899
|
curl -X POST http://127.0.0.1:8100/api/streams/reconcile
|
|
894
900
|
```
|
|
@@ -987,7 +993,7 @@ Likely causes:
|
|
|
987
993
|
- `stream.publish_video` has not received a frame yet.
|
|
988
994
|
|
|
989
995
|
Fix:
|
|
990
|
-
- For camera streams, check the camera source and keep `
|
|
996
|
+
- For camera streams, check the camera source and keep `Transmit this source` enabled.
|
|
991
997
|
- Call `POST /api/streams/reconcile`.
|
|
992
998
|
- Check `GET /api/streams/runtime/pipelines` to see which pipeline owns the generated transmission.
|
|
993
999
|
- Check `GET /api/streams/runtime/health` for `active_writer_id`, `selected_writer_id`, `fallback_reason`, and frame age.
|
|
@@ -995,7 +1001,7 @@ Fix:
|
|
|
995
1001
|
The primary user-facing message for this class is:
|
|
996
1002
|
|
|
997
1003
|
```text
|
|
998
|
-
|
|
1004
|
+
No pipeline is feeding this transmission.
|
|
999
1005
|
```
|
|
1000
1006
|
|
|
1001
1007
|
### HLS plays but WebRTC warning appears
|
|
@@ -14,6 +14,7 @@ from ..streaming.mediamtx_config import normalize_path_slug
|
|
|
14
14
|
EXTENSION_ID = "com.toposync.streaming"
|
|
15
15
|
TEST_PATH = "test"
|
|
16
16
|
StreamingRuntimeStatus = Literal["live", "degraded", "stale", "offline"]
|
|
17
|
+
StreamingSummaryStatus = Literal["working", "warming", "action_required"]
|
|
17
18
|
StreamingStreamBehavior = Literal["continuous", "event_gated"]
|
|
18
19
|
StreamingEncoderMode = Literal["auto", "cpu"]
|
|
19
20
|
StreamingOutputEncoderMode = Literal["inherit", "auto", "cpu"]
|
|
@@ -1055,6 +1056,10 @@ class StreamingMseSidecarStatusResponse(BaseModel):
|
|
|
1055
1056
|
stream_count: int = Field(default=0, ge=0)
|
|
1056
1057
|
warnings: list[str] = Field(default_factory=list)
|
|
1057
1058
|
restart_count: int = Field(default=0, ge=0)
|
|
1059
|
+
summary_status: StreamingSummaryStatus = "warming"
|
|
1060
|
+
summary_message: str = ""
|
|
1061
|
+
summary_action: str | None = None
|
|
1062
|
+
technical_status: str = "unknown"
|
|
1058
1063
|
|
|
1059
1064
|
|
|
1060
1065
|
class StreamingJsmpegStatusResponse(BaseModel):
|
|
@@ -1087,6 +1092,15 @@ class TransmissionCreateRequest(BaseModel):
|
|
|
1087
1092
|
outputs: list[TransmissionOutput] = Field(default_factory=list)
|
|
1088
1093
|
|
|
1089
1094
|
|
|
1095
|
+
class MediaContentRect(BaseModel):
|
|
1096
|
+
model_config = ConfigDict(extra="forbid")
|
|
1097
|
+
|
|
1098
|
+
x: float = Field(ge=0.0, le=1.0)
|
|
1099
|
+
y: float = Field(ge=0.0, le=1.0)
|
|
1100
|
+
width: float = Field(ge=0.0, le=1.0)
|
|
1101
|
+
height: float = Field(ge=0.0, le=1.0)
|
|
1102
|
+
|
|
1103
|
+
|
|
1090
1104
|
class TransmissionOutputUrl(BaseModel):
|
|
1091
1105
|
model_config = ConfigDict(extra="forbid")
|
|
1092
1106
|
|
|
@@ -1104,6 +1118,7 @@ class TransmissionOutputUrl(BaseModel):
|
|
|
1104
1118
|
fps_limit: int | None = None
|
|
1105
1119
|
bitrate_kbps: int | None = None
|
|
1106
1120
|
latency_profile: Literal["normal", "low", "ultra_low"] | None = None
|
|
1121
|
+
content_rect: MediaContentRect | None = None
|
|
1107
1122
|
|
|
1108
1123
|
|
|
1109
1124
|
class TransmissionUrlsResponse(BaseModel):
|
|
@@ -1469,6 +1484,10 @@ class StreamingOutputRuntimeStatus(BaseModel):
|
|
|
1469
1484
|
demand_idle: bool = False
|
|
1470
1485
|
classification: StreamingObservabilityClassification = "unknown"
|
|
1471
1486
|
evidence: list[str] = Field(default_factory=list)
|
|
1487
|
+
summary_status: StreamingSummaryStatus = "warming"
|
|
1488
|
+
summary_message: str = ""
|
|
1489
|
+
summary_action: str | None = None
|
|
1490
|
+
technical_status: str = "unknown"
|
|
1472
1491
|
active_playback_session_count: int = Field(default=0, ge=0)
|
|
1473
1492
|
last_playback_event_at_unix: float | None = None
|
|
1474
1493
|
publisher_frames_sent_rate: float | None = None
|
|
@@ -1519,6 +1538,10 @@ class StreamingRuntimeOutputHealth(BaseModel):
|
|
|
1519
1538
|
demand_idle: bool = False
|
|
1520
1539
|
classification: StreamingObservabilityClassification = "unknown"
|
|
1521
1540
|
evidence: list[str] = Field(default_factory=list)
|
|
1541
|
+
summary_status: StreamingSummaryStatus = "warming"
|
|
1542
|
+
summary_message: str = ""
|
|
1543
|
+
summary_action: str | None = None
|
|
1544
|
+
technical_status: str = "unknown"
|
|
1522
1545
|
active_playback_session_count: int = Field(default=0, ge=0)
|
|
1523
1546
|
last_playback_event_at_unix: float | None = None
|
|
1524
1547
|
publisher_frames_sent_rate: float | None = None
|
|
@@ -1548,6 +1571,10 @@ class StreamingRuntimeTransmissionHealth(BaseModel):
|
|
|
1548
1571
|
demand_idle: bool = False
|
|
1549
1572
|
classification: StreamingObservabilityClassification = "unknown"
|
|
1550
1573
|
evidence: list[str] = Field(default_factory=list)
|
|
1574
|
+
summary_status: StreamingSummaryStatus = "warming"
|
|
1575
|
+
summary_message: str = ""
|
|
1576
|
+
summary_action: str | None = None
|
|
1577
|
+
technical_status: str = "unknown"
|
|
1551
1578
|
active_playback_session_count: int = Field(default=0, ge=0)
|
|
1552
1579
|
last_playback_event_at_unix: float | None = None
|
|
1553
1580
|
source_health: StreamingRuntimeSourceHealth | None = None
|
|
@@ -1668,6 +1695,10 @@ class StreamingRuntimeObservabilityItem(BaseModel):
|
|
|
1668
1695
|
output_id: str | None = None
|
|
1669
1696
|
classification: StreamingObservabilityClassification
|
|
1670
1697
|
evidence: list[str] = Field(default_factory=list)
|
|
1698
|
+
summary_status: StreamingSummaryStatus = "warming"
|
|
1699
|
+
summary_message: str = ""
|
|
1700
|
+
summary_action: str | None = None
|
|
1701
|
+
technical_status: str = "unknown"
|
|
1671
1702
|
active_playback_sessions: list[StreamingPlaybackSessionSummary] = Field(default_factory=list)
|
|
1672
1703
|
last_playback_event_at_unix: float | None = None
|
|
1673
1704
|
publisher_frames_sent_rate: float | None = None
|