Real-time control
Real-time control
Section titled “Real-time control”Everything on this page changes a system that is already running, without restarting anything.
Three kinds of topic, which are easy to confuse
Section titled “Three kinds of topic, which are easy to confuse”Getting these mixed up is the main way people go wrong here, so it is worth being explicit:
| Kind | Retained? | Means |
|---|---|---|
| Configuration | Yes | ”This is the configuration.” A late subscriber gets the current value. Replaces what came before. |
| Command | No | ”Do this, now.” An imperative that happens once. A late subscriber gets nothing, and should not. |
| State | Yes | ”This is what is actually true.” Published by the service, read by everyone else. |
A command must not be retained. A retained command is re-delivered to every new subscriber, so a service restarting would replay the last cut — which is precisely the kind of fault that looks like a ghost in the system.
Player transport
Section titled “Player transport”Topic: phrame/tams-player/{instance}/CMD — state on …/STATUS.
Every command carries an operation field naming what it is.
Set play speed. Speed is a multiplier: 1.0 is normal, 0.5 half, 2.0
double, negative values play backwards, and 0 holds.
{ "operation": "set_play_speed", "speed": -2.0 }Seek. The seek_type field selects how the position is expressed, which is
the useful part — seeking by frame count and seeking by wall-clock time are
genuinely different operations:
seek_type | Extra field | Goes to |
|---|---|---|
start | — | the beginning of the flow |
end | — | the live edge |
end_minus | seconds | that far back from the live edge |
time | origination_time_ns | an absolute origination time |
percentage | percent | that far through the material |
relative | seconds | that far from where it is now |
frames | frames | that many frames from here (negative goes back) |
{ "operation": "seek", "seek_type": "frames", "frames": -10 }Set the master clock. This is the one worth knowing about, because it is what makes several players agree to the frame:
{ "operation": "set_clock", "epoch_ns": 1788350000000000000, "base_frame": 4200 }epoch_ns is a wall-clock instant and base_frame is the flow frame index that
that instant corresponds to. Every player given the same pair computes the same
absolute content target from the current time, so they stay locked together
rather than each drifting from its own start. Publishing epoch_ns: 0 clears
the clock and returns the player to free-running.
State is reported as one of playing, buffering, seeking, stopped or
error.
Switch — taking a cut
Section titled “Switch — taking a cut”A switch selects which of its inputs is live on its output.
The input can be named by URI or by index:
{ "active_stream": "vdi://40f7f177-0c8f-4f47-a305-6848a22c2d76" }Scheduling a cut to a frame. A cut can carry the origination time at which it should take effect:
{ "active_stream": "vdi://40f7f177-0c8f-4f47-a305-6848a22c2d76", "at_origination_time_ns": 1788350000000000000}Without that field the switch cuts as soon as the message arrives — which means the cut lands wherever network delay and message scheduling put it. With it, the switch holds the cut until the frame carrying that origination time reaches it, and cuts there. That is the difference between a cut that is approximately right and a cut that is frame-exact, and it is the mechanism to use for anything an audience will see.
A scheduled cut whose input cannot be found is abandoned rather than held indefinitely.
Related fields: add_input_stream adds an input to the switch’s set, and
apply_scaler controls whether the switch’s scaler is applied to the outgoing
stream.
Audio mixer
Section titled “Audio mixer”Base topic from the mixer’s own configuration, conventionally AUDIO-MIXERS:
{base_topic}/{mixer_id}/inputs add or modify an input{base_topic}/{mixer_id}/outputs add or modify an output{base_topic}/{mixer_id}/mixes/{mix_id} configure one mix{base_topic}/{mixer_id}/party_lines/{id} configure one party lineInputs and outputs are addressed by id and carry the bus stream they map to:
{ "id": 43, "uri": "vdi://40f7f177-0c8f-4f47-a305-6848a22c2d76" }Publishing the same id again modifies that input rather than adding a second — so these replace rather than accumulate, and an id is the thing to keep track of.
Controller mode — replacing a service’s whole configuration
Section titled “Controller mode — replacing a service’s whole configuration”input-processor and output-processor can run static (everything from
their config file) or in controller mode, where the live configuration
arrives over MQTT and the service publishes what it is doing:
phrame/config/platforms/{platform}/services/input-processor/{uuid} configuration (retained)phrame/input-processor/{uuid}/state state (retained)The configuration topic carries the same JSON as the config file — see the input-processor and output-processor field references. It is a replacement, not a patch: what you publish is the whole configuration from that moment.
Because the topic is retained, a service that restarts picks up the current configuration immediately without anything having to notice it restarted. This is the same mechanism the platform uses to distribute configuration from Git — see Configuration & GitOps.
Changing the live configuration ref
Section titled “Changing the live configuration ref”One further retained topic worth knowing, because it moves the whole platform’s configuration at once:
{prefix}/system/gitlab/ref {"ref":"<branch|tag|sha>"}Covered under Configuration & GitOps, including why pointing it at a tag locks the deployment.
