Add OBS Studio Workflow blocks: OBS Connection, OBS Action, Detections Class Router, RTSP Stream Watcher - #2968
Tyler-Odenthal wants to merge 1 commit into
Conversation
|
👋 Thanks for the pull request! Here is how automated Claude review works here, so you spend credits (and reviewer time) wisely. 🚧 Right now this is a draft, so automated Claude review is paused — nothing is being spent yet. Mark it Ready for review to trigger it. Warning 💸 The Claude reviewer bills in credits, not vibesAutomated review spins up a real agent that reads real code and spends real credits on every pass. It is glad to help — but it is not a rubber duck, a linter you poke in a loop, or a substitute for reading the contributing guide. Treat it like an expensive senior reviewer whose time you booked, and show up prepared. Draft when unsure, Ready when you mean it:
However you get there, arrive prepared:
Reviews are not free. A draft costs nothing to review; a Ready PR is a promise that it is worth reviewing.
|
61f4c1a to
2cd8bf8
Compare
Adds two sink blocks that let a Workflow drive a running OBS Studio instance over obs-websocket, so model predictions can switch scenes, toggle sources and filters, update on-screen text, control recording and the virtual camera, and move sources around the canvas. `roboflow_core/obs_connection@v1` holds the websocket address and emits a connection descriptor consumed by action blocks. When no password is given and OBS runs on the same machine, it reads the password from OBS Studio's own config file, so a local OBS needs no configuration at all; discovery is skipped for non-local hosts, where this machine's credential would be wrong, and the block reports where the credential came from in its `message`. Credentials are held in an in-process registry, never in the descriptor, so wiring the connection to a Workflow output cannot leak the password. `roboflow_core/obs_action@v1` performs one OBS operation per execution: - set_scene, set_source_visibility, set_text, toggle_filter, trigger_hotkey - start/stop virtual camera and recording - set_source_transform: place and size a source at explicit canvas coordinates - move_source_to_detection: move a source to the highest-confidence detection's bounding box (optionally offset beside it), mapping image coordinates onto the OBS canvas, hiding the source when nothing is detected Triggering is left to existing flow-control blocks rather than duplicated in block configuration. Every conditional field declares `relevant_for` so the builder shows only the selected action's fields. `skip_if_unchanged` makes state-setting actions cost no round trip when re-applying the same value, so a video workflow no longer repeats set_scene every frame, and when OBS rejects a scene or source name the error lists what OBS actually has. Also adds `roboflow_core/detections_class_router@v1`, a formatter that maps the most confident detection among a routed set of classes to a value (e.g. class -> scene name). It replaces the five-step filter/count/gate/extract/ expression chain every "when class X is seen" workflow needed, handles empty frames internally instead of crashing SequenceAggregate, and cannot be outranked by an unrouted class. With no default it emits None, which the OBS Action treats as "no target this frame" and holds state. Also adds `roboflow_core/rtsp_stream_watcher@v1`, a source block that keeps a shared background reader on a second RTSP camera and emits its newest frame on every run, so a Workflow driven by one camera can react to what another sees - a person on a yard camera switching an OBS scene - without any process outside the Workflow. Readers are shared per URL, drain the stream continuously so frames never lag, reconnect on failure, close after an idle timeout, report a status, and emit a blank frame when the stream is down or stale so downstream logic holds rather than acting on a frozen picture. Credentials in stream URLs are redacted from logs. Both OBS blocks declare HARD runtime restrictions for Hosted Serverless, Dedicated Deployments and remote step execution, none of which can reach a local OBS instance, plus the soft cooldown restriction for HTTP runtimes. Connections are pooled per (host, port) with a per-connection lock and shared across every action in a Workflow; a dead socket reconnects once transparently, while a request OBS actively rejects fails immediately. Verification and password discovery run once per block instance, not once per frame. obsws-python logs the websocket password at INFO on connect, so that logger is raised to WARNING before the first connection. Adds the `obs_connection` kind, registers all four blocks, and pins obsws-python. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99b25ba to
4a15dfe
Compare
Summary
Adds Workflow blocks that let a Workflow drive OBS Studio over obs-websocket, plus two supporting blocks that make the "when the model sees X, do Y in OBS" pattern composable without custom code:
roboflow_core/obs_connection@v1obs_connectionother blocks consumeroboflow_core/obs_action@v1set_scene,set_source_visibility,set_text,toggle_filter,trigger_hotkey,set_source_transform,move_source_to_detection, start/stop virtual camera, start/stop recordingroboflow_core/detections_class_router@v1{class: value}table (e.g.{"cup": "Coffee", "cat": "Cat"}); empty → default /Noneroboflow_core/rtsp_stream_watcher@v1frame+predictions; costs the primary video path no frame rateNew kind:
obs_connection.Why
Customers asked for a way to react to what a model sees by switching OBS scenes, moving overlays, or toggling filters during a stream or call. Today that needs a script outside Workflows. With these blocks the whole thing is a Workflow:
webcam → detector → Class Router → OBS Action(set_scene), with extra cameras via the RTSP Stream Watcher andFirst Non Empty Or Defaultfor priority.Design notes
obs-websocket/config.json(macOS / Windows / Linux / Flatpak paths) when OBS is local, so the common case needs no password field. It is only attempted whenALLOW_WORKFLOW_BLOCKS_ACCESSING_LOCAL_STORAGEpermits blocks to read the host filesystem (the same gate the Local File sink honours); with it off the block never touches the disk and says so inmessage. The block'smessagealways reports where the credential came from, never the credential.skip_if_unchanged(default on) dedups identical calls; aNonetarget (nothing routed this frame) is a skip, not an error; scene-item ids and canvas size are cached; failures are logged and reported inmessage, never raised into the pipeline. Unknown scene/source names report the available names.check_interval_seconds, and runsmodel_idon the sample itself through theModelsProviderport;run()returns the latest detections instantly. Measured on an M5 Max: workflow throughput identical with 0 or 2 extra cameras (4.1 fps both), vs 2.9 fps when camera detectors sat on the request path. Readers are daemon threads shared per (URL, model), reconnect on failure, stop when idle, and strip credentials from logs.supervision.Detectionsor the tensor-nativeinference_modelsDetections(ENABLE_TENSOR_DATA_REPRESENTATION), and the watcher emits whichever the workflow runs with; the integration tests pass in both CI variants.inferenceserver package and no direct environment reads, per the decontamination lint and the isolation probe.REMOTEstep execution. OBS Connection honoursdisable_sinks.Testing
tests/workflows/unit_tests/core_steps/sinks/test_obs.py(60) and.../transformations/test_rtsp_stream_watcher.py(12), fake OBS clients / fake captures, no network.tests/workflows/integration_tests/execution/test_workflow_with_obs_blocks.pyruns two full workflows through the Execution Engine (scene switch on a detected class incl. dedup and the hold-on-nothing case; RTSP watcher with a background model feeding a Class Router) and registers both in the workflows gallery.Dependencies
obsws-python~=1.8.0added torequirements/_requirements.txt.Not in this PR
Keypoint-driven avatar actions were prototyped and removed to keep the initial release small.
🤖 Generated with Claude Code