Перейти к содержимому

Media clock

Это содержимое пока не доступно на вашем языке.

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. Its ptp_time_ns is the frame the change takes effect on — the next frame for immediate commands, the first frame at or after ApplyAt.ptp_time_ns for scheduled ones (so within one frame of the requested time). The event can arrive up to ~1 frame + 2 ms before its ptp_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.
[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.

  1. Treat the newest event of a layer as authoritative; recompute the schedule on every event (they are cheap).
  2. Use seq gap 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.
  3. Do not extrapolate a paused layer (playing = false): the position holds.
  4. Compare media_time_s of consecutive events with the extrapolation; a difference above 1 µs means a transport change (seek / loop wrap at out_s), never jitter.