Skip to content

Latest commit

 

History

History
106 lines (94 loc) · 6.32 KB

synchronization.md

File metadata and controls

106 lines (94 loc) · 6.32 KB

Software-sync request selection

Camera.captureSyncRequestAsync(options) and captureSyncRequest(options) select the first qualifying request with explicitly true SyncReady metadata. They provide Picamera2's capture_sync_request capability through the existing Bun capture queue. A returned Frame is an ordinary retained owner; use using or release it explicitly. All its main/raw/lores streams belong to that request.

import { Camera, Control } from '@zig-utils/camera'

const camera = new Camera({ role: 'video' })
try {
  camera.setControls([{
    id: Control.frameDurationLimits,
    type: 'int',
    values: [33333, 33333],
  }])
  camera.setSynchronization('server', { frames: 120 })
  using request = await camera.captureSyncRequest({ timeoutMs: 30000 })
  console.log(request.synchronization(), request.metadata())
  request.saveFile('synchronized.jpg')
} finally {
  await camera.close()
}

Set matching fixed frame rates on all participants and configure the deployed libcamera Raspberry Pi sync algorithm/network appropriately. Select client on each client camera. setSynchronization('off') disables it. These controls configure the camera backend's algorithm; zig-camera does not create a separate UDP synchronization protocol, estimate offsets from host timestamps, or generate readiness itself for real cameras. The Raspberry Pi control specification defines SyncMode, SyncFrames, SyncReady and the signed-microsecond SyncTimer. The pinned Picamera2 source selects requests using SyncReady, rather than a timestamp proximity heuristic.

setSynchronization(mode, { frames? }) resolves advertised scalar int32 controls by exact name and backend vendor (rpi/libcamera, or unavailable provenance on older headers). The mode values are off=0, server=1, client=2. A supplied positive int32 frames is server-only and is committed atomically with the mode. Omission keeps the backend's existing frame setting. Invalid, unavailable, ambiguous, wrong-type or read-only controls reject without a partial update. Native range validation remains authoritative. Settings participate in existing typed control snapshots/replay across camera mode transactions; they do not transfer a previous session's ready state into a newly started algorithm.

Frame.synchronization() returns an immutable owned observation: { ready?: boolean, timerUs?: bigint }. Missing fields remain absent, distinct from false or zero. Timer values retain signed 64-bit precision, including negative values after the ready time. Neither the timer nor matching host timestamps substitute for true SyncReady. metadataCatalog() also retains the backend's complete typed values. Pre/post request callback views expose the same synchronization query for their scoped request.

The query reads the exact leased request, without serializing a full catalog, copying pixels or allocating in the native path. A retained pending request does not change when a later request becomes ready or controls change. Its metadata remains readable after stop, until final release; stale queries fail. C ABI v1 layouts remain unchanged, with an additive 24-byte zc_frame_sync_state query. valid & 1 indicates ready and valid & 2 indicates timer_us. Failure clears the output. The libcamera bridge uses request-local names/types, supporting older headers without references to missing rpi control constants. Malformed scalar types/duplicate values report an error rather than guessing readiness.

All ordinary CaptureOptions additionally accept syncReady: true, including owned RGB/stream/metadata/image and focus-locked captures. The existing native filter combines readiness, distinct dropFrames and inclusive flush timing. Its default path stays unchanged when no predicate is requested. The named sync request helpers always require readiness, even if a caller passes syncReady=false. False or absent readiness discards that candidate for this job and waits for a later request; it does not prevent preview, recording or request hooks from using the request. There is one bounded capture queue, with ordinary FIFO admission, abort/deadline, callback and joined cleanup semantics. The normal 5000 ms default capture timeout applies; use a longer timeout for delayed server startup. Stop and close cancel/join pending jobs. Pre-aborted jobs start/acquire nothing.

Focus-locked captures additionally require the paused-focus revision and restore CAF after readiness succeeds, times out or is cancelled. Waiting does not retain a rejected frame. Home video's allocation-free frame_barrier Gate implements the combined policy; Bun adds no second readiness counter or request pool.

Native callers initialize a fresh zc_capture_filter with ZC_CAPTURE_SYNC_READY (4), optionally OR'd with timestamp/exposure flags, then use zc_acquire_filtered. Acceptance is terminal: initialize a new filter for each selection and keep it on one camera completion timeline. Rejected owners are released; query/metadata errors leave filter state unchanged. Zig adds syncCaptureFilter, Camera.acquireSynced and Frame.synchronization over those same primitives.

The explicit virtual backend advertises rpi-named SyncMode/SyncFrames controls for deterministic tests. A virtual server becomes ready after its configured number of generated requests (default 3); its nominal timer derives from frame count and configured duration. A virtual client receives no network server messages and stays pending, with no fabricated timer. Stop preserves leased request snapshots; restart after their final release resets the virtual readiness counter. This tests selection and ownership, not a real network/clock synchronization algorithm.

Tests cover exact FIFO readiness, positive/zero/negative timers, missing metadata, retained snapshots, stale/query failures, combined drops/timing/AF, pre-abort, pending cancellation/stop/close, control admission/replay and simultaneous recording/headless-preview delivery. Physical server/client synchronization, network loss/startup, exposure alignment across sensors, cross-device clock calibration and coordinated multi-camera bundles remain acceptance work. This feature does not establish those hardware results or full Picamera2 parity.