-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: add per-monitor websocket transport #4830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 10 commits
ebfa640
9923c36
c7c6bd3
463af64
bb32d4b
7c38528
fcbf129
0ae10ba
40dd3fa
cd41ea4
e7ff2e6
d9e8211
dd8732d
da423c1
b9f1f32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| Monitor Websocket API | ||
| ===================== | ||
|
|
||
| ZoneMinder can expose live monitor data directly from ``zmc`` over a websocket | ||
| connection. | ||
|
|
||
| Overview | ||
| ^^^^^^^^ | ||
|
|
||
| Each monitor listens on: | ||
|
|
||
| :: | ||
|
|
||
| MIN_STREAMING_PORT + MonitorId | ||
|
|
||
| For example, if ``MIN_STREAMING_PORT`` is ``30000`` and the monitor id is | ||
| ``5``, the websocket endpoint is: | ||
|
|
||
| :: | ||
|
|
||
| ws://your-server:30005/ | ||
|
|
||
| This requires ``Options -> Network -> MIN_STREAMING_PORT`` to be configured and | ||
| the web server or reverse proxy to allow those ports. | ||
|
|
||
| .. warning:: | ||
|
|
||
| The monitor websocket endpoint can expose live camera data to any client | ||
| that can reach the monitor's streaming port. This transport does not provide | ||
| access control by itself, so do not expose these ports directly to | ||
| untrusted networks. Restrict access with firewall rules and/or place the | ||
| endpoint behind a reverse proxy that enforces authentication and | ||
| authorization. | ||
|
|
||
| Connection model | ||
| ^^^^^^^^^^^^^^^^ | ||
|
|
||
| The websocket server is created by ``zmc`` and runs independently per monitor. | ||
|
|
||
| Clients may: | ||
|
|
||
| * request one response | ||
| * subscribe to repeated updates | ||
| * unsubscribe later | ||
|
|
||
| Text frames carry JSON control and metadata messages. Binary frames carry the | ||
| requested ``jpeg``, ``raw``, or ``h264`` payload bytes. | ||
|
|
||
| Commands | ||
| ^^^^^^^^ | ||
|
|
||
| One-shot status request: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"status","request_id":"optional-id"} | ||
|
|
||
| One-shot image request: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"image","format":"jpeg","request_id":"optional-id"} | ||
|
|
||
| Supported image formats are: | ||
|
|
||
| * ``jpeg`` | ||
| * ``raw`` | ||
| * ``h264`` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really sure what an h264 image would be. image should be limited to actual image formats. I would be tempted to say long term that we should be able to return any image format that ffmpeg supports. Realistically raw needs to be either rgba, yuv etc. For now, because I intend to deprecate 24bit support, let's say yuv420p (which is most likely what we will be getting from a decoder), or rgba. Honeslty no one is going to want anything other than jpeg. |
||
|
|
||
| Status subscription: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"subscribe","topic":"status","interval_ms":1000} | ||
|
|
||
| Event subscription: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"subscribe","topic":"events"} | ||
|
|
||
| Image subscription: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"subscribe","topic":"image","format":"jpeg","interval_ms":1000} | ||
|
|
||
| Unsubscribe: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"unsubscribe","topic":"status"} | ||
|
|
||
| or: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"unsubscribe","topic":"events"} | ||
|
|
||
| or: | ||
|
|
||
| :: | ||
|
|
||
| {"command":"unsubscribe","topic":"image"} | ||
|
|
||
| Status messages | ||
| ^^^^^^^^^^^^^^^ | ||
|
|
||
| Status replies are JSON text frames with fields such as: | ||
|
|
||
| * ``monitor_id`` | ||
| * ``monitor_name`` | ||
| * ``connected`` | ||
| * ``shm_valid`` | ||
| * ``state`` / ``state_id`` | ||
| * ``capture_fps`` | ||
| * ``analysis_fps`` | ||
| * ``capture_bandwidth`` | ||
| * ``image_count`` | ||
| * ``signal`` | ||
| * ``last_event_id`` | ||
|
|
||
| Event messages | ||
| ^^^^^^^^^^^^^^ | ||
|
|
||
| Event subscriptions receive JSON text frames with: | ||
|
|
||
| * ``type = "event"`` | ||
| * ``monitor_id`` | ||
| * ``event`` | ||
| * ``message`` | ||
|
|
||
| These are queue-based notifications generated from capture-side failures and | ||
| recovery transitions so the capture loop does not block on websocket clients. | ||
|
|
||
| Image metadata and binary payloads | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
|
||
| Every image or video payload is sent as two websocket frames: | ||
|
|
||
| 1. A JSON text metadata frame | ||
| 2. A binary frame containing the payload bytes | ||
|
|
||
| The metadata frame includes: | ||
|
|
||
| * ``type = "image"`` | ||
| * ``request_id`` | ||
| * ``format`` | ||
| * ``content_type`` | ||
| * ``monitor_id`` | ||
| * ``width`` | ||
| * ``height`` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. due to alignment/padding, the raw data may have lines longer than width. Will need to commmunicate that as well. |
||
| * ``colours`` | ||
| * ``subpixel_order`` | ||
| * ``image_count`` | ||
| * ``sequence`` | ||
| * ``keyframe`` | ||
| * ``payload_bytes`` | ||
|
|
||
| JPEG and raw image behavior | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
|
||
| ``jpeg`` and ``raw`` payloads are generated from the latest image in the | ||
| monitor shared-memory ring buffer. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For simplicity sake, you should be able to use the packetqueue for these as well. Everything in shm should also be in packetqueue. |
||
|
|
||
| For subscription mode, ``interval_ms`` controls how often the server checks for | ||
| and sends a newer frame. | ||
|
|
||
| H264 behavior | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should be careful here using h264 to really mean a video stream =. I think we need clear separation between asking for an image, and asking for a video stream. Please note that I would choose to use mjpeg when we are talking about a stream of jpegs. So we are talking about a video stream, with some codec. We already will need support for h265 and av1. While ideally we should be able to ask for anything (and have it transcode) 99% of the time we will want to simply request the passthrough video data in whatever codec it happens to be in. |
||
| ^^^^^^^^^^^^^ | ||
|
|
||
| ``h264`` delivery uses the monitor packet queue, not the shared-memory image | ||
| buffer. | ||
|
|
||
| One-shot ``h264`` requests return a decodable packet snapshot: | ||
|
|
||
| * the payload starts at the latest available queued H.264 keyframe | ||
| * codec extradata is prepended before the keyframe packet bytes | ||
|
|
||
| ``h264`` subscriptions stream queued packets in order starting from the latest | ||
| available keyframe in the queue. This gives new subscribers a decodable start | ||
| point and avoids dropping interdependent packets. | ||
|
|
||
| For ``h264`` subscriptions: | ||
|
|
||
| * packets are pushed in queue order | ||
| * ``interval_ms`` is ignored | ||
| * ``sequence`` tracks the packet queue order | ||
| * ``keyframe`` indicates whether the payload begins a new decodable segment | ||
|
|
||
| Errors | ||
| ^^^^^^ | ||
|
|
||
| Protocol errors are returned as JSON text frames: | ||
|
|
||
| :: | ||
|
|
||
| {"type":"error","message":"..."} | ||
|
|
||
| Unsupported payload formats, unavailable monitor data, or malformed commands | ||
| return an error frame instead of a binary payload. | ||
Uh oh!
There was an error while loading. Please reload this page.