Media clock
Это содержимое пока не доступно на вашем языке.
What the engine publishes
Section titled “What the engine publishes”Events.Subscribe(topics: ["clock"]) streams EngineEvent { clock: MediaClock }:
| Field | Meaning |
|---|---|
layer_id |
the playback layer the audio follows (the media server maps cues → layers) |
media_time_s |
position in the layer’s media (seconds, after loop / in / out) at ptp_time_ns |
ptp_time_ns |
engine clock time of that position: the due time of a frame on the shared time base (below) |
rate |
media seconds per clock second from this pair on (LayerParams.rate; 1.0 = normal speed) |
playing |
false = the position holds at media_time_s (paused / stopped / ended / ready) |
EngineEvent.ptp_time_ns equals clock.ptp_time_ns for these events.
Each event is an exact pair of the layer’s transport: for any PTP time t after it (until the next event of a
transport change), media(t) = media_time_s + rate × (t − ptp_time_ns) / 1e9 while playing, else media_time_s.
The engine derives video frames from the same function, so audio scheduled
from the pairs stays on the picture.
- Every transport change, immediately: Load, Play, Pause, Stop, Seek, end of a non-looping layer, and changes of
rate,loop,in_s,out_s. Itsptp_time_nsis the frame the change takes effect on — the next frame for immediate commands, the first frame at or afterApplyAt.ptp_time_nsfor scheduled ones (so within one frame of the requested time). The event can arrive up to ~1 frame + 2 ms before itsptp_time_ns(the director applies commands one frame ahead of presentation): it is a schedule, not a report. - ≥ 10 Hz while playing: at most 100 ms of PTP time between two events of a layer (every 5 frames at 59.94 / 60 Hz). These periodic events repeat the current pair at a later frame; they let a late subscriber start and let the audio engine verify it has not drifted. Nothing is sent for layers that are not playing apart from transport changes.
Time base
Section titled “Time base”[sync] source |
ptp_time_ns is |
Frame grid | SyncStatus.quality |
|---|---|---|---|
ptp (Linux, ptp4l / phc2sys) |
TAI ns (PTP timescale) | frame n due at n / fps since the TAI epoch — identical on all nodes |
LOCKED / UNLOCKED |
ntp (Windows / macOS) |
UTC ns (system time) | n / fps since the Unix epoch |
COARSE (≤ 5 ms) |
local (single node) |
CLOCK_MONOTONIC ns |
from the engine start | LOCKED (no sync events) |
The audio engines read the same PTP time base (MediaTime.ptp_ns: PipeWire / AES67 PTP clock, or
CLOCK_TAI disciplined by phc2sys), so a pair can be used directly: a sample due at media time m plays at
ptp_time_ns + (m − media_time_s) / rate plus the venue’s latency compensation (A/V offset target ≤ 10 ms).
Subscribe to sync as well: while quality = UNLOCKED the engine free-runs on its last drift model; after a re-lock
the engine clock is corrected at the next layer cut or within 2 s in ±1-frame steps (SyncStatus.corrections
increments) — the pairs stay exact in engine time, so an audio engine following the pairs follows the picture.
Rules for consumers
Section titled “Rules for consumers”- Treat the newest event of a layer as authoritative; recompute the schedule on every event (they are cheap).
- Use
seqgap detection: on a gap, re-subscribe; the first events of a subscription are a state + layers snapshot, the next periodic MediaClock (≤ 100 ms) restores the pairs. - Do not extrapolate a paused layer (
playing = false): the position holds. - Compare
media_time_sof consecutive events with the extrapolation; a difference above 1 µs means a transport change (seek / loop wrap atout_s), never jitter.