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.
Files changed (68) hide show
  1. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/PKG-INFO +19 -13
  2. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/README.md +18 -12
  3. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/pyproject.toml +1 -1
  4. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/api/models.py +31 -0
  5. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/api/routes.py +422 -14
  6. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/pipelines/operators.py +13 -4
  7. toposync_ext_streaming-0.4.6/src/toposync_ext_streaming/static/703.js +2 -0
  8. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/resize.py +39 -14
  9. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/wizard/pipeline_builder.py +15 -5
  10. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/settings/StreamingSettingsPanel.tsx +194 -6
  11. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/translations.ts +32 -0
  12. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/types.ts +18 -0
  13. toposync_ext_streaming-0.4.4/src/toposync_ext_streaming/static/703.js +0 -2
  14. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/.gitignore +0 -0
  15. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/LICENSE +0 -0
  16. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/LICENSE.ffmpeg +0 -0
  17. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/LICENSE.mediamtx +0 -0
  18. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/__init__.py +0 -0
  19. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/api/__init__.py +0 -0
  20. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/bin/ffmpeg/LICENSE +0 -0
  21. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/bin/mediamtx/LICENSE +0 -0
  22. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/extension.json +0 -0
  23. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/pipelines/__init__.py +0 -0
  24. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/plugin.py +0 -0
  25. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/326.js +0 -0
  26. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/326.js.LICENSE.txt +0 -0
  27. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/387.js +0 -0
  28. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/4.js +0 -0
  29. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/4.js.LICENSE.txt +0 -0
  30. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/623.js +0 -0
  31. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/623.js.LICENSE.txt +0 -0
  32. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/703.js.LICENSE.txt +0 -0
  33. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/main.js +0 -0
  34. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/main.js.LICENSE.txt +0 -0
  35. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/static/remoteEntry.js +0 -0
  36. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/__init__.py +0 -0
  37. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/arbitration.py +0 -0
  38. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/camera_ingest.py +0 -0
  39. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/distributed_sync.py +0 -0
  40. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/encoder_state.py +0 -0
  41. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/engine_manager.py +0 -0
  42. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/ffmpeg_binary.py +0 -0
  43. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/go2rtc_binary.py +0 -0
  44. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/go2rtc_config.py +0 -0
  45. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/go2rtc_manager.py +0 -0
  46. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/ingest_auth.py +0 -0
  47. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/ingest_resolver.py +0 -0
  48. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/jsmpeg_manager.py +0 -0
  49. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_api_client.py +0 -0
  50. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_binary.py +0 -0
  51. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_config.py +0 -0
  52. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/mediamtx_processes.py +0 -0
  53. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/placeholder.py +0 -0
  54. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/platform.py +0 -0
  55. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/playback_events.py +0 -0
  56. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/publisher_manager.py +0 -0
  57. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/runtime_state.py +0 -0
  58. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/streaming/writer_bridge.py +0 -0
  59. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/src/toposync_ext_streaming/wizard/__init__.py +0 -0
  60. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/package.json +0 -0
  61. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/activate.tsx +0 -0
  62. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/api/streamingApi.ts +0 -0
  63. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/constants.ts +0 -0
  64. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/entry.ts +0 -0
  65. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/settings/SubModal.tsx +0 -0
  66. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/src/settings/WizardCreatePipelineFromTransmission.tsx +0 -0
  67. {toposync_ext_streaming-0.4.4 → toposync_ext_streaming-0.4.6}/ui/tsconfig.json +0 -0
  68. {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.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 `Transmitir esta fonte` intent.
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/toposync-streaming-dossier-solid-priorities.md`](../../docs/toposync-streaming-dossier-solid-priorities.md#00-principios-permanentes-de-streaming).
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 "Principal", "Baixa resolução", "Zoom", or custom names, not internal output ids.
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 `Transmitir esta fonte` checkbox;
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 Principal, Baixa resolução, Zoom, or custom names.
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": "Baixa resolução",
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 `Transmitir esta fonte` enabled on the desired video sources.
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": "Baixa resolução"}'
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 `Transmitir esta fonte` enabled.
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
- Nenhum fluxo está alimentando esta transmissão.
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 `Transmitir esta fonte` intent.
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/toposync-streaming-dossier-solid-priorities.md`](../../docs/toposync-streaming-dossier-solid-priorities.md#00-principios-permanentes-de-streaming).
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 "Principal", "Baixa resolução", "Zoom", or custom names, not internal output ids.
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 `Transmitir esta fonte` checkbox;
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 Principal, Baixa resolução, Zoom, or custom names.
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": "Baixa resolução",
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 `Transmitir esta fonte` enabled on the desired video sources.
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": "Baixa resolução"}'
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 `Transmitir esta fonte` enabled.
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
- Nenhum fluxo está alimentando esta transmissão.
1004
+ No pipeline is feeding this transmission.
999
1005
  ```
1000
1006
 
1001
1007
  ### HLS plays but WebRTC warning appears
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "toposync-ext-streaming"
3
- version = "0.4.4"
3
+ version = "0.4.6"
4
4
  description = "Toposync first-party extension: streaming settings, API surface, and pipeline sink bootstrap."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -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