otio_sync_core¶
otio_sync_core is the core library for coordinating OTIO timeline
synchronisation across a network session. It provides the SyncManager
that drives synchronisation, UDP and RabbitMQ network backends, a transparent
proxy for intercepting attribute writes, typed protocol messages, and the
colour-pipeline and annotation codecs.
OTIO Sync core library.
Provides SyncManager for coordinating OTIO timeline synchronisation
across a network session, along with UDP and RabbitMQ network backends and a
transparent proxy for intercepting attribute writes.
manager¶
The SyncManager is the central coordinator: it owns the OTIO timeline,
applies incoming sync events, and emits outgoing ones over the configured
network backend.
Core synchronisation manager for the OTIO Sync protocol.
SyncManager maintains a GUID-indexed map of every OTIO object in the shared
session and coordinates mutations across a pluggable network layer. It implements the
master-election handshake, delta buffering during join, and all broadcast helpers
defined in the OTIO Sync Protocol v1 proposal.
- otio_sync_core.manager.STATE_DISCOVERING = 'DISCOVERING'
Broadcasting
WHO_IS_MASTER; waiting for a response.
- otio_sync_core.manager.STATE_JOINING = 'JOINING'
Master found; waiting for a full state snapshot.
- otio_sync_core.manager.STATE_NONE = 'NONE'
Session has not yet started.
- otio_sync_core.manager.STATE_SYNCED = 'SYNCED'
Snapshot received and applied; fully participating in the session.
- class otio_sync_core.manager.SyncManager(session_id: str = 'default_session', self_guid: str | None = None, network: SyncNetworkProtocol | None = None)[source]
Bases:
objectCoordinates OTIO object synchronisation across a network session.
The manager maintains two complementary data structures:
_object_map— a flat{guid: otio_object}index for O(1) lookup by GUID._timelines— a{guid: Timeline}map of every registered top-level timeline.
All mutations (inserts, removals, property changes) are applied locally and broadcast to peers via the injected network backend. Incoming messages are applied through
apply_patch(), which also fires registered observer callbacks so that the host application (e.g. the RV plugin) can react to remote changes.Session lifecycle
Call
start_session()— status transitions toSTATE_DISCOVERING.The caller polls
receive_and_apply_all()until amaster_foundaction is returned, then callsrequest_state().Status transitions to
STATE_JOINING; incoming non-session messages are buffered in_delta_buffer.When a
state_snapshot_receivedaction is returned, the caller invokesapply_snapshot()which applies the full state and replays buffered deltas before transitioning toSTATE_SYNCED.
If no master responds within the discovery timeout (implemented in the caller), the caller elects itself master and calls
broadcast_master_response().- Parameters:
session_id – Logical session identifier; scopes all network messages.
self_guid – Stable GUID for this peer; auto-generated when not provided.
network – Network backend satisfying
SyncNetworkProtocol. May be set or replaced after construction.
- property active_clip_guid: str | None
Sequence clip GUID if the active timeline is a single-clip timeline, else
None.- Return type:
str or None
- annotation_clip_guid_at(clip_guid: str, frame: int) str | None[source]
Return the sync GUID of the annotation clip at (clip_guid, frame).
Convenience wrapper around
annotation_track_guid_for_clip()and_find_annotation_clip_at()that returns the clip’s own GUID rather than the object itself.- Parameters:
clip_guid – GUID of the media clip being annotated.
frame – 0-indexed clip-local frame number.
- Returns:
Annotation clip GUID, or
Noneif not found.- Return type:
str or None
- annotation_track_guid_for_clip(clip_guid: str, preferred_timeline_guid: str | None = None) str | None[source]
Return the GUID of the Annotations track in the same timeline as clip_guid.
Searches every non-annotation track for clip_guid, then returns the first track whose name contains
"annotation"(case-insensitive) from that same timeline.When preferred_timeline_guid is provided (e.g. the current
active_timeline_guid), that timeline is searched first. This ensures that annotations are written to the clip timeline’s annotation track while in clip mode, rather than the sequence timeline’s track.- Parameters:
clip_guid – Sync GUID of the media clip.
preferred_timeline_guid – GUID of the timeline to search first; falls back to all timelines if not found there.
- Returns:
Annotation track GUID, or
Noneif not found.- Return type:
str or None
- apply_patch(payload: dict[str, Any]) tuple[str, Any] | None[source]
Apply a single incoming message from the network.
Dispatches on
command_schemaandeventfields. Returns an(action, data)tuple when the caller needs to act (e.g. to update RV state), orNonewhen the message was fully handled internally.Messages from
self_guidare silently discarded. Messages arriving duringSTATE_JOININGare buffered (except session messages) and replayed byapply_snapshot().- Parameters:
payload – Parsed message envelope received from the network.
- Returns:
(action_name, action_data)orNone.
- apply_snapshot(snapshot_data: dict[str, Any]) list[tuple[str, Any]][source]
Replace local state with a full snapshot and replay buffered deltas.
Clears
_object_mapand_timelines, deserialises the timelines from snapshot_data, then replays any buffered messages whosesync_timestampis newer than the snapshot. TransitionsstatustoSTATE_SYNCED.- Parameters:
snapshot_data –
payloaddict from aSTATE_SNAPSHOTmessage.- Returns:
List of
(action, data)tuples produced by replaying buffered deltas; to be handled by the caller in the same way as the return value ofreceive_and_apply_all().
- broadcast_add_annotation(annotation_track_guid: str, clip_guid: str, clip_local_time: RationalTime, events: list[dict[str, Any]]) str | None[source]
Build an annotation clip and insert it via the standard patch path.
Annotations are expressed as
insert_childpatches so that all peers apply them through the same code path as any other timeline mutation.The annotation track mirrors the structure produced by
ORIAnnotations.ReviewItem._export_otio_media(): each annotated frame is a 1-frameClipand the gaps between annotated frames areGapobjects whose duration isframe − track_endframes. A second stroke on an already-annotated frame merges its commands into the existing clip rather than inserting a duplicate.- Parameters:
annotation_track_guid – GUID of the target Annotations track.
clip_guid – GUID of the media clip being annotated.
clip_local_time – 0-indexed time within the clip’s source range.
events – Serialised OTIO SyncEvent dicts (
PaintStart.1,PaintPoints.1) as produced byotio.adapters.write_to_string.
- Returns:
The sync GUID of the annotation clip that was created or merged into, or
Noneif the operation could not be completed.- Return type:
str or None
- broadcast_add_timeline(tl_guid: str) None[source]
Broadcast a timeline to all peers so they can register it.
Works for both sequence timelines (new playlist / new sequence) and single-clip annotation timelines. Call once immediately after
register_timeline()to propagate a locally-created timeline to all connected peers. Peers that already hold the same GUID silently ignore the message.- Parameters:
tl_guid – GUID of the timeline to broadcast.
- broadcast_clip_timeline(tl_guid: str) None[source]
Broadcast a clip timeline to all peers so they can register its annotation track.
Should be called once per clip timeline, immediately after
get_or_create_clip_timeline()returns a new GUID. Peers that already have the timeline (same deterministic GUID) will skip theADD_TIMELINEmessage.Delegates to
broadcast_add_timeline().- Parameters:
tl_guid – GUID of the clip timeline to broadcast.
- broadcast_display_state(state_dict: dict[str, Any]) None[source]
Broadcast the current display state to all peers and persist it.
Expected keys in state_dict:
pan—[x, y]normalised pan offset.zoom— zoom multiplier (1.0= no zoom).exposure— exposure adjustment in stops (0.0= no change).channel— active channel string:"RGBA","R","G","B", or"A".
The state is also written into the active timeline’s
metadata["display_settings"]so it survives a full session teardown if the OTIO file is saved to disk.- Parameters:
state_dict – Display state fields as listed above.
- broadcast_master_discovery() None[source]
Broadcast a
WHO_IS_MASTERsession message.
- broadcast_master_response() None[source]
Broadcast an
I_AM_MASTERsession message.Called after self-election (discovery timeout) or when an existing master receives a
WHO_IS_MASTERit should answer.
- broadcast_move_child(parent_uuid: str, child_uuid: str, to_index: int) None[source]
Move child_uuid to to_index within its parent and broadcast the change.
Applies the reorder locally before broadcasting so the local OTIO model stays consistent regardless of network round-trip time.
- Parameters:
parent_uuid – GUID of the parent container.
child_uuid – GUID of the child to move.
to_index – Target position in the parent’s child list.
- broadcast_partial_annotation(clip_guid: str, frame: float, fps: float, events: list) None[source]
Broadcast a mid-stroke partial annotation to peers (visual only, no timeline persistence).
Called periodically while the user is drawing a stroke, before pen-up. Peers render the stroke visually but do not write it to the OTIO timeline — that happens on pen-up via
broadcast_add_annotation().- Parameters:
clip_guid – Sync GUID of the media clip being annotated.
frame – 0-indexed clip-local frame number.
fps – Frame rate used to interpret frame.
events – Serialised SyncEvent dicts (
PaintStart.1,PaintPoints.1).
- broadcast_playback_state(state_dict: dict[str, Any], timeline_guid: str | None = None) None[source]
Broadcast the current playback state to all peers.
- Parameters:
state_dict – Playback state fields (
playing,current_time,looping, etc.) as defined by the protocol.timeline_guid – GUID of the timeline being played; falls back to
active_timeline_guid.
- broadcast_remove_child(parent_uuid: str, child_uuid: str) None[source]
Remove child_uuid from its parent and broadcast the change.
The child is removed from both the parent container and
_object_map.- Parameters:
parent_uuid – GUID of the parent container.
child_uuid – GUID of the child to remove.
- broadcast_replace_annotation_commands(annotation_clip_guid: str, events: list) None[source]
Replace all annotation_commands on an existing clip and broadcast to peers.
Used when the user edits text in an annotation in place — the command count stays the same but the text content changes. Sends a
REPLACE_ANNOTATION_COMMANDSmessage so peers replace the full command list rather than appending a delta.- Parameters:
annotation_clip_guid – Sync GUID of the annotation clip to update.
events – Full replacement list of SyncEvent objects (strokes + captions) representing the current annotation state.
- broadcast_selection(clip_guid: str, view_mode: str = 'source') None[source]
Broadcast the selected clip GUID to all peers.
- Parameters:
clip_guid – OTIO sync GUID of the selected clip. Receivers map this back to their local representation (RV source group, xStudio playlist position, etc.) before applying.
view_mode (str) – View mode string (“source” or “sequence”).
- broadcast_timeline_rename(tl_guid: str, new_name: str) None[source]
Rename a timeline locally and broadcast the change to all peers.
Updates the timeline’s
nameattribute in_timelinesimmediately, then sends aRENAME_TIMELINEmessage so all connected peers apply the same rename.- Parameters:
tl_guid – GUID of the timeline to rename.
new_name – New display name for the timeline.
- close() None[source]
Stop the network backend and release all resources.
- count_annotation_commands(clip_guid: str, frame: int) tuple[int, int][source]
Return
(n_strokes, n_captions)already committed for (clip_guid, frame).Counts
PaintStartevents (strokes) andTextAnnotationevents (captions) in the annotation track. Accumulates across all matching clips at the same frame so that old snapshots containing per-stroke clips are handled correctly.- Parameters:
clip_guid – GUID of the media clip being annotated.
frame – 0-indexed clip-local frame number.
- Returns:
(n_strokes, n_captions)already in the annotation track.- Return type:
tuple
- display_state: dict[str, Any]
Last received display state dict; empty until the first display message. Keys:
pan([x, y] normalised),zoom(float),exposure(stops),channel("RGBA","R","G","B", or"A").
- get_or_create_clip_timeline(clip_guid: str) str | None[source]
Return the GUID of the single-clip timeline for clip_guid, creating it lazily.
All peers independently derive the same GUIDs via
_derive_guid(), so no coordination message is required before clips can be used across peers. Callers should broadcast the timeline viabroadcast_clip_timeline()the first time it is created so that peers without local creation can register the annotation track in their_object_map(required for receiving annotationINSERT_CHILDpatches).The clip copy inside the clip timeline shares the same sync GUID as the sequence clip.
_traverse_and_map_preserve()ensures the sequence clip remains canonical in_object_mapso thatrange_in_parent()returns the sequence-level position.- Parameters:
clip_guid – Sync GUID of the target sequence clip.
- Returns:
GUID of the clip timeline, or
Noneif clip_guid is not a knownClip.- Return type:
str or None
- insert_child(parent_uuid: str, child_obj: SerializableObject, index: int = -1) None[source]
Insert child_obj into the parent container and broadcast the change.
A GUID is assigned to child_obj if it does not already have one. Use
index=-1to append.- Parameters:
parent_uuid – GUID of the parent container (Track or Stack).
child_obj – OTIO object to insert.
index – Position at which to insert;
-1appends.
- property is_syncing: bool
Truewhile a snapshot or incoming delta is being applied locally.Callers can read this to suppress outgoing broadcasts that would echo changes back to their source.
- Return type:
bool
- property object_map: dict[str, SerializableObject]
Read-only view of the flat GUID → OTIO object index.
- on_display_changed(callback: Callable[[dict[str, Any]], None]) Callable[[dict[str, Any]], None][source]
Register a callback fired whenever a display-state message arrives.
The callback receives the raw display state dict (same structure as
display_state). Also usable as a decorator.- Parameters:
callback – Callable receiving the display state dict.
- Returns:
The callback unchanged (decorator-compatible).
- on_hierarchy_changed(callback: Callable[[str, str, str], None]) Callable[[str, str, str], None][source]
Register a callback for hierarchy change events.
Fires for both locally-initiated and remotely-applied structural changes. May be used as a decorator.
- Parameters:
callback – Callable receiving
(parent_uuid, action, child_uuid)where action is one of"insert_child"or"remove_child".- Returns:
The callback unchanged (decorator-compatible).
- on_playback_changed(callback: Callable[[dict[str, Any]], None]) Callable[[dict[str, Any]], None][source]
Register a callback fired whenever a playback-state message arrives.
The callback receives the raw playback state dict (same structure as
playback_state). Also usable as a decorator.- Parameters:
callback – Callable receiving the playback state dict.
- Returns:
The callback unchanged (decorator-compatible).
- on_property_changed(callback: Callable[[str, str, Any], None]) Callable[[str, str, Any], None][source]
Register a callback for property change events.
Fires for both locally-initiated and remotely-applied property changes. May be used as a decorator.
- Parameters:
callback – Callable receiving
(target_uuid, path, new_value).- Returns:
The callback unchanged (decorator-compatible).
- on_status_changed(callback: Callable[[str], None]) Callable[[str], None][source]
Register a callback fired whenever
statustransitions.- Parameters:
callback – Callable receiving the new status string.
- Returns:
The callback unchanged (decorator-compatible).
- on_synced(callback: Callable[[], None]) Callable[[], None][source]
Register a callback fired once when the session reaches
STATE_SYNCED.Fires both when this peer self-elects as master and when it finishes joining an existing master. Also usable as a decorator.
- Parameters:
callback – Zero-argument callable.
- Returns:
The callback unchanged (decorator-compatible).
- playback_state: dict[str, Any]
Last received playback state dict; empty until the first playback message.
- receive_and_apply_all() list[tuple[str, Any]][source]
Drain the network and apply every pending message.
- Returns:
List of
(action, data)tuples for messages that require a response from the caller (e.g. to update RV state). Empty when all messages were handled internally or no messages were waiting.
- register_timeline(timeline: Timeline) OTIOSyncProxy[source]
Register a timeline, assign GUIDs to all its objects, and index them.
Sets
active_timeline_guidto the new timeline’s GUID if no active timeline exists yet.- Parameters:
timeline – The
Timelineto register.- Returns:
An
OTIOSyncProxywrapping timeline so that attribute writes are automatically broadcast.
- request_state() None[source]
Send a
STATE_REQUESTto the master and enterSTATE_JOINING.Non-session messages received while joining are buffered in
_delta_bufferand replayed byapply_snapshot().
- reset_timelines() None[source]
Clear all registered timelines, the object map, and the active GUID.
Used during master re-initialisation when the timeline data must be rebuilt from scratch (e.g. after the RV node graph settles).
- property root_timeline: Timeline | None
The active timeline, or the first registered timeline when none is active.
- Returns:
Active
Timeline, orNoneif no timelines have been registered.
- selected_clip_guid: str | None
GUID of the clip most recently selected by a remote peer via a
SELECTIONbroadcast.Nonewhen the selection is cleared.
- send_state_snapshot(target_guid: str, playback_state: dict[str, Any] | None = None) None[source]
Serialise all registered timelines and send a full snapshot to a joiner.
Only the master should call this method. The snapshot is broadcast to the whole session (not unicast), but only the peer whose GUID matches target_guid will act on it.
- Parameters:
target_guid – GUID of the requesting peer.
playback_state – Optional current playback state dict to include so the joiner can immediately seek to the right position.
- property sequence_timeline_guid: str | None
GUID of the first registered timeline that is not a clip timeline.
- Return type:
str or None
- set_property(target_uuid: str, path: str, value: Any) None[source]
Set property path to value on object target_uuid and broadcast.
Property paths are either plain attributes (e.g.
"name") or metadata sub-paths starting with"metadata/"(e.g."metadata/annotations").- Parameters:
target_uuid – GUID of the target object.
path – Target property or metadata sub-key path.
value – New value; must be a primitive type.
- start_session() None[source]
Begin the join process by broadcasting a master-discovery message.
Transitions
statustoSTATE_DISCOVERING. The caller is responsible for timing out and calling the appropriate method if no master responds (see class docstring for the full lifecycle).
- tick() list[tuple[str, Any]][source]
Poll the network and auto-advance the session handshake.
This is the recommended entry point for new client integrations. It wraps
receive_and_apply_all()and handles the session state machine automatically:master_found→ callsrequest_state()internally.state_snapshot_received→ callsapply_snapshot()internally.state_request_received→ returned to caller; the master must respond by callingsend_state_snapshot().
Application-level events (
playback_settings,selection_changed,annotation_*,insert_child, …) are returned so the caller can react to them. Playback updates are also delivered through theon_playback_changed()callback if one is registered.Compare with
receive_and_apply_all(), which returns every raw action tuple and leaves the handshake entirely to the caller.- Returns:
List of
(action, data)tuples requiring application action (subset of whatreceive_and_apply_all()would return).
- property timelines: dict[str, Timeline]
Read-only view of all registered timelines, keyed by sync GUID.
- otio_sync_core.manager.sync_event_schema(cmd: Any) str[source]
Return the OTIO schema name for a SyncEvent object or a serialised dict.
Centralises the
hasattr(cmd, "schema_name") / isinstance(cmd, dict)pattern that appears throughout annotation-handling code.- Parameters:
cmd – A deserialised SyncEvent object or a raw
dictwhose"OTIO_SCHEMA"key carries the schema name.- Returns:
Schema name string (e.g.
"PaintStart.1"), or""if cmd is neither.- Return type:
str
protocol_messages¶
Typed dataclasses describing the wire-format messages exchanged between sync participants.
Typed protocol message definitions for the OTIO Sync transport layer.
Each message class defined here is the single source of truth for one
transport-layer message: its command_schema, its event name, and the
shape of its payload. This mirrors how SyncEvent is the source of truth
for the OTIO message layer, and lets a documentation generator describe the
protocol directly from these classes (see docs/ generator).
Design constraints (see the typed-protocol-messages change design doc):
Messages are pure data — handler logic lives in the manager/patcher, not on the message classes — so the classes stay importable in isolation for documentation.
Registration is explicit via the
register()decorator, keyed on(SCHEMA, EVENT), so the receive-side dispatch registry cannot drift from the definitions.Serialization is explicit:
to_payload()builds a plaindictwithout reflective whole-object walking (nodataclasses.asdict()) and without per-messageisinstancevalidation, so hot-path messages (PartialAnnotation,PlaybackSettingsSet) stay cheap.The settings messages declare their known fields for documentation but tolerate unknown fields (carried in
extras) for forward-compatibility with independent producers.
- class otio_sync_core.protocol_messages.AddTimeline(timeline_guid: str, timeline: Any, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageRegisters a new timeline (sequence or single-clip) with all peers.
- EVENT: ClassVar[str] = 'ADD_TIMELINE'
- SCHEMA: ClassVar[str] = 'TIMELINE_1.0'
- as_otio() Any[source]
Return the OTIO timeline, deserializing if still in wire form.
- classmethod from_payload(data: dict[str, Any]) AddTimeline[source]
Reconstruct a message instance from a received
command.payload.
- sync_timestamp: float | None = None
- timeline: Any
- timeline_guid: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.DisplaySettingsSet(pan: list | None = None, zoom: float | None = None, exposure: float | None = None, channel: str | None = None, sync_timestamp: float | None = None, extras: dict = <factory>)[source]
Bases:
ProtocolMessageDisplay state broadcast (pan/zoom/exposure/channel).
Known fields are declared for documentation; additional producer fields are preserved in
extras.- EVENT: ClassVar[str] = 'SET'
- SCHEMA: ClassVar[str] = 'DISPLAY_SETTINGS_1.0'
- channel: str | None = None
- exposure: float | None = None
- extras: dict
- classmethod from_payload(data: dict[str, Any]) DisplaySettingsSet[source]
Reconstruct a message instance from a received
command.payload.
- pan: list | None = None
- sync_timestamp: float | None = None
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- zoom: float | None = None
- class otio_sync_core.protocol_messages.IAmMaster(master_guid: str)[source]
Bases:
ProtocolMessageMaster’s response to discovery, announcing itself as session master.
- ENVELOPE_SCHEMA: ClassVar[str | None] = 'SYNC_REVIEW_1.0'
Legacy top-level envelope schema preserved for older peers.
- EVENT: ClassVar[str] = 'I_AM_MASTER'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) IAmMaster[source]
Reconstruct a message instance from a received
command.payload.
- master_guid: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.InsertChild(parent_uuid: str, child_data: Any, index: int = -1, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageInserts a child object into a parent container.
- EVENT: ClassVar[str] = 'INSERT_CHILD'
- SCHEMA: ClassVar[str] = 'OTIO_SESSION_1.0'
- as_otio() Any[source]
Return the OTIO child object, deserializing if still in wire form.
- child_data: Any
- classmethod from_payload(data: dict[str, Any]) InsertChild[source]
Reconstruct a message instance from a received
command.payload.
- index: int = -1
- parent_uuid: str
- sync_timestamp: float | None = None
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.MoveChild(parent_uuid: str, child_uuid: str, to_index: int = 0, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageMoves a child to a new index within its parent container.
- EVENT: ClassVar[str] = 'MOVE_CHILD'
- SCHEMA: ClassVar[str] = 'OTIO_SESSION_1.0'
- child_uuid: str
- classmethod from_payload(data: dict[str, Any]) MoveChild[source]
Reconstruct a message instance from a received
command.payload.
- parent_uuid: str
- sync_timestamp: float | None = None
- to_index: int = 0
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.NewParticipant[source]
Bases:
ProtocolMessageAnnounces that a new participant has joined the sync review.
- EVENT: ClassVar[str] = 'NEW_PARTICIPANT'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) NewParticipant[source]
Reconstruct a message instance from a received
command.payload.
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.NewPresenter(presenter_hash: str)[source]
Bases:
ProtocolMessageAnnounces that a peer has become the session presenter.
- EVENT: ClassVar[str] = 'NEW_PRESENTER'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) NewPresenter[source]
Reconstruct a message instance from a received
command.payload.
- presenter_hash: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.PartialAnnotation(clip_guid: str, frame: float, fps: float, events: list = <factory>)[source]
Bases:
ProtocolMessageMid-stroke partial annotation (visual preview, not persisted).
Hot path: fires repeatedly while a stroke is being drawn. No validation or reflective serialization is performed.
- EVENT: ClassVar[str] = 'PARTIAL'
- SCHEMA: ClassVar[str] = 'Annotation.1'
- clip_guid: str
- events: list
- fps: float
- frame: float
- classmethod from_payload(data: dict[str, Any]) PartialAnnotation[source]
Reconstruct a message instance from a received
command.payload.
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.PlaybackSettingsSet(playing: bool | None = None, current_time: dict | None = None, looping: bool | None = None, timeline_guid: str | None = None, sync_timestamp: float | None = None, extras: dict = <factory>)[source]
Bases:
ProtocolMessagePlayback state broadcast.
Hot path: fires on frame change during playback/scrubbing. Known fields are declared for documentation; any additional producer fields are preserved in
extrasand round-tripped unchanged.- EVENT: ClassVar[str] = 'SET'
- SCHEMA: ClassVar[str] = 'PLAYBACK_SETTINGS_1.0'
- current_time: dict | None = None
- extras: dict
- classmethod from_payload(data: dict[str, Any]) PlaybackSettingsSet[source]
Reconstruct a message instance from a received
command.payload.
- looping: bool | None = None
- playing: bool | None = None
- sync_timestamp: float | None = None
- timeline_guid: str | None = None
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.ProtocolMessage[source]
Bases:
objectBase class for all transport-layer protocol messages.
Subclasses are
@dataclass-decorated and@register-ed. They set the class-levelSCHEMAandEVENTconstants and implementto_payload()/from_payload()explicitly.- Variables:
SCHEMA – The envelope
command_schemafor this message.EVENT – The envelope
command.eventfor this message.ENVELOPE_SCHEMA – Optional top-level
schemakey written on the envelope (onlyIAmMasteruses this for legacy compatibility).
- ENVELOPE_SCHEMA: ClassVar[str | None] = None
- EVENT: ClassVar[str] = ''
- SCHEMA: ClassVar[str] = ''
- classmethod doc_fields() list[tuple[str, str, str]][source]
Return
(name, type, description)triples for documentation.Default implementation reads the dataclass fields, skipping the
extrascatch-all used by tolerant messages.- Returns:
List of
(field_name, type_name, doc)tuples.
- classmethod from_payload(data: dict[str, Any]) ProtocolMessage[source]
Reconstruct a message instance from a received
command.payload.
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.RemoveChild(parent_uuid: str, child_uuid: str, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageRemoves a child from its parent container.
- EVENT: ClassVar[str] = 'REMOVE_CHILD'
- SCHEMA: ClassVar[str] = 'OTIO_SESSION_1.0'
- child_uuid: str
- classmethod from_payload(data: dict[str, Any]) RemoveChild[source]
Reconstruct a message instance from a received
command.payload.
- parent_uuid: str
- sync_timestamp: float | None = None
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.RenameTimeline(timeline_guid: str, name: str, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageRenames an existing timeline on all peers.
- EVENT: ClassVar[str] = 'RENAME_TIMELINE'
- SCHEMA: ClassVar[str] = 'TIMELINE_1.0'
- classmethod from_payload(data: dict[str, Any]) RenameTimeline[source]
Reconstruct a message instance from a received
command.payload.
- name: str
- sync_timestamp: float | None = None
- timeline_guid: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.ReplaceAnnotationCommands(annotation_clip_guid: str, commands: list = <factory>, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageReplaces the full annotation-command list on an annotation clip.
- EVENT: ClassVar[str] = 'REPLACE_ANNOTATION_COMMANDS'
- SCHEMA: ClassVar[str] = 'OTIO_SESSION_1.0'
- annotation_clip_guid: str
- as_otio() list[source]
Return the OTIO SyncEvent list, deserializing any wire-form entries.
- commands: list
- classmethod from_payload(data: dict[str, Any]) ReplaceAnnotationCommands[source]
Reconstruct a message instance from a received
command.payload.
- sync_timestamp: float | None = None
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.SelectionSet(clip_guid: str, view_mode: str = 'source', sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageBroadcasts the clip the master has selected and the active view mode.
- EVENT: ClassVar[str] = 'SET'
- SCHEMA: ClassVar[str] = 'SELECTION_1.0'
- clip_guid: str
- classmethod from_payload(data: dict[str, Any]) SelectionSet[source]
Reconstruct a message instance from a received
command.payload.
- sync_timestamp: float | None = None
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- view_mode: str = 'source'
- class otio_sync_core.protocol_messages.SetProperty(target_uuid: str, path: str, value: Any, sync_timestamp: float | None = None)[source]
Bases:
ProtocolMessageSets a property or metadata path on an object.
- EVENT: ClassVar[str] = 'SET_PROPERTY'
- SCHEMA: ClassVar[str] = 'OTIO_SESSION_1.0'
- classmethod from_payload(data: dict[str, Any]) SetProperty[source]
Reconstruct a message instance from a received
command.payload.
- path: str
- sync_timestamp: float | None = None
- target_uuid: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- value: Any
- class otio_sync_core.protocol_messages.SharedKeyRequest(key: str)[source]
Bases:
ProtocolMessageRequests the session’s shared key from a peer.
- EVENT: ClassVar[str] = 'SHARED_KEY_REQUEST'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) SharedKeyRequest[source]
Reconstruct a message instance from a received
command.payload.
- key: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.SharedKeyResponse(key: str)[source]
Bases:
ProtocolMessageResponds to a shared-key request with the session’s shared key.
- EVENT: ClassVar[str] = 'SHARED_KEY_RESPONSE'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) SharedKeyResponse[source]
Reconstruct a message instance from a received
command.payload.
- key: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.StateRequest(target_guid: str, requester_guid: str)[source]
Bases:
ProtocolMessageJoiner’s request to the master for a full state snapshot.
- EVENT: ClassVar[str] = 'STATE_REQUEST'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) StateRequest[source]
Reconstruct a message instance from a received
command.payload.
- requester_guid: str
- target_guid: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.StateSnapshot(target_guid: str, timelines: dict = <factory>, active_timeline_guid: str | None = None, snapshot_timestamp: float | None = None, playback_state: dict | None = None, display_state: dict | None = None)[source]
Bases:
ProtocolMessageMaster’s full session snapshot sent in response to a state request.
- EVENT: ClassVar[str] = 'STATE_SNAPSHOT'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- active_timeline_guid: str | None = None
- as_otio() dict[str, Any][source]
Return
{guid: OTIO timeline}, deserializing any wire-form entries.
- display_state: dict | None = None
- classmethod from_payload(data: dict[str, Any]) StateSnapshot[source]
Reconstruct a message instance from a received
command.payload.
- playback_state: dict | None = None
- snapshot_timestamp: float | None = None
- target_guid: str
- timelines: dict
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- class otio_sync_core.protocol_messages.WhoIsMaster(requester_guid: str)[source]
Bases:
ProtocolMessageMaster-discovery broadcast asking any existing master to identify itself.
- EVENT: ClassVar[str] = 'WHO_IS_MASTER'
- SCHEMA: ClassVar[str] = 'LiveSession.1'
- classmethod from_payload(data: dict[str, Any]) WhoIsMaster[source]
Reconstruct a message instance from a received
command.payload.
- requester_guid: str
- to_payload() dict[str, Any][source]
Return the
command.payloaddict for this message.
- otio_sync_core.protocol_messages.doc_field(*, default: ~typing.Any = <dataclasses._MISSING_TYPE object>, default_factory: ~typing.Any = <dataclasses._MISSING_TYPE object>, doc: str = '')[source]
Declare a dataclass field carrying a documentation string in metadata.
The documentation generator reads
field.metadata["doc"]for each field.- Parameters:
default – Default value (mutually exclusive with default_factory).
default_factory – Zero-arg callable producing the default.
doc – Human-readable description of the field.
- otio_sync_core.protocol_messages.message_for(command_schema: str, event: str) type[ProtocolMessage] | None[source]
Return the message class for
(command_schema, event), orNone.- Parameters:
command_schema – Envelope
command_schemavalue.event – Envelope
command.eventvalue.
- Returns:
The registered
ProtocolMessagesubclass, orNonewhen the pair is unknown (caller should ignore the message safely).
- otio_sync_core.protocol_messages.register(cls: type[ProtocolMessage]) type[ProtocolMessage][source]
Register cls in the protocol registry keyed on
(SCHEMA, EVENT).Used as a class decorator. Raises if two classes claim the same
(SCHEMA, EVENT)pair, so collisions surface at import time.- Parameters:
cls – A
ProtocolMessagesubclass withSCHEMA/EVENTset.- Returns:
cls unchanged (decorator-compatible).
- otio_sync_core.protocol_messages.registered_messages() dict[tuple[str, str], type[ProtocolMessage]][source]
Return a copy of the full
(schema, event) -> classregistry.Used by the documentation generator to enumerate every protocol message.
network¶
The UDP network backend and the network-protocol interface implemented by all backends.
UDP broadcast network backend and shared network Protocol for OTIO Sync.
- class otio_sync_core.network.SyncNetworkProtocol(*args, **kwargs)[source]
Bases:
ProtocolStructural interface that all network backends must satisfy.
Both
UDPNetworkandRabbitMQNetworkconform to this protocol, allowingSyncManagerto accept either without a concrete base class.- receive_payloads() list[dict[str, Any]][source]
Return all payloads received since the last call, without blocking.
- send_payload(payload: dict[str, Any]) None[source]
Broadcast payload to all peers in the session.
- stop() None[source]
Shut down the network connection and release resources.
- class otio_sync_core.network.UDPNetwork(port: int = 9999, broadcast_ip: str | None = None, self_guid: str | None = None)[source]
Bases:
objectLAN broadcast network backend using UDP.
Opens a non-blocking receive socket bound to port and a separate send socket with
SO_BROADCASTset. All peers on the same LAN segment that bind to the same port will receive every message.Self-filtering is done via self_guid: any received payload whose
source_guidmatches is silently discarded.- Parameters:
port – UDP port to bind and broadcast on.
broadcast_ip – Explicit broadcast address; auto-detected when
None.self_guid – GUID of the local peer used to filter own messages.
- close() None[source]
Close both sockets immediately.
- receive_payloads() list[dict[str, Any]][source]
Drain all available UDP datagrams and return them as parsed dicts.
Non-blocking; returns an empty list when no data is waiting. Own messages (matched by
source_guid) are silently dropped.- Returns:
List of received payload dicts.
- send_payload(payload: dict[str, Any]) None[source]
Broadcast payload as JSON to the LAN.
Injects
source_guidinto the payload if not already present.- Parameters:
payload – Message envelope to broadcast.
- stop() None[source]
Alias for
close(); satisfiesSyncNetworkProtocol.
- otio_sync_core.network.get_local_broadcast() str[source]
Derive the LAN broadcast address from the default route interface.
Falls back to
255.255.255.255if the address cannot be determined.- Returns:
Broadcast IP address string, e.g.
"192.168.1.255".
rabbitmq_network¶
A RabbitMQ-backed network transport, an alternative to the UDP backend.
RabbitMQ fanout-exchange network backend for OTIO Sync.
- class otio_sync_core.rabbitmq_network.RabbitMQNetwork(host: str = '127.0.0.1', port: int = 5672, session_id: str = 'otio-sync-default', self_guid: str | None = None)[source]
Bases:
objectRabbitMQ network backend for OTIO Sync.
Uses a fanout exchange so that every peer bound to the same exchange receives every published message. The exchange name is derived from session_id, which implicitly scopes peers to a session without any server-side configuration.
Two dedicated background threads handle I/O so callers are never blocked by pika:
_consumer_thread— owns aBlockingConnectionfor receiving; pushes decoded payloads onto_incoming_queue._publisher_thread— owns a separateBlockingConnectionfor sending; drains_send_queuein a tight loop with automatic reconnection on failure.
send_payloadtherefore never touches a socket directly; it is always non-blocking for the caller.Self-filtering is applied in the consumer callback: any message whose
source_guidmatches self_guid is silently discarded before being enqueued.- Parameters:
host – RabbitMQ broker hostname or IP.
port – RabbitMQ broker AMQP port.
session_id – Logical session name; used to derive the exchange name.
self_guid – GUID of the local peer used to filter own messages. Auto-generated if not provided.
- receive_payloads() list[dict[str, Any]][source]
Drain the internal queue and return all pending payloads.
Non-blocking; returns an empty list when nothing is waiting. Messages are populated by the background consumer thread.
- Returns:
List of received payload dicts.
- send_payload(payload: dict[str, Any]) None[source]
Enqueue payload for publishing to the fanout exchange.
Non-blocking: the actual socket write happens on the publisher thread. Injects
source_guidinto the payload if not already present.- Parameters:
payload – Message envelope to broadcast.
- stop() None[source]
Signal background threads to exit and wait for them to finish.
Blocks for up to 2 seconds per thread.
- wait_until_ready(timeout: float = 5.0) bool[source]
Block until the consumer queue is bound and ready to receive messages.
- Parameters:
timeout – Maximum seconds to wait before returning False.
- Returns:
True if the consumer became ready within timeout, else False.
- Return type:
bool
proxy¶
A transparent proxy that intercepts attribute writes on wrapped OTIO objects so that local edits can be turned into outgoing sync events.
Transparent proxy that intercepts attribute writes and forwards them to OTIOPatcher.
- class otio_sync_core.proxy.OTIOSyncProxy(obj: otio.core.SerializableObject, patcher: OTIOPatcher, parent_path: str = '')[source]
Bases:
objectTransparent proxy around an OTIO
SerializableObject.Attribute reads are passed through to the wrapped object unchanged. Attribute writes are applied to the wrapped object and forwarded to the
OTIOPatcheras aset_propertychange, so that remote peers stay in sync without the caller needing to be aware of the sync layer.Child OTIO objects returned by
__getattr__are themselves wrapped in a newOTIOSyncProxyso that nested attribute writes are also captured.- Parameters:
obj – The OTIO object to wrap.
patcher – The
OTIOPatcher(or compatible) that owns this object.parent_path – Dot-separated path prefix used when constructing the property path for nested objects.
patcher¶
Helpers for converting between OTIO objects and plain dictionaries and for applying patches to an OTIO timeline.
Transport-agnostic patching engine for OpenTimelineIO (OTIO) graphs.
- class otio_sync_core.patcher.OTIOPatcher[source]
Bases:
objectManages the lifecycle of OTIO graph patches.
Tracks object GUIDs, observes mutations, and applies patch events (such as property changes or hierarchy insertions/moves/removals) to the local graph.
- apply_patch(msg: ProtocolMessage) tuple[str, Any] | None[source]
Apply an OTIO-session mutation message to the local graph.
Dispatches on the concrete message type, so the same class that built the payload (in
set_property(),insert_child(), etc.) is the one used to consume it.- Parameters:
msg – A reconstructed OTIO-session
ProtocolMessage:SetProperty,MoveChild,RemoveChild,ReplaceAnnotationCommands, orInsertChild.- Returns:
An
(action_name, action_data)tuple when the caller needs to act, orNone.- Return type:
tuple or None
- ensure_guid_and_map(obj: Any) None[source]
Assign a sync GUID to obj if absent, then add it to
object_map.Non-
SerializableObjectvalues are ignored.- Parameters:
obj – Candidate OTIO object.
- insert_child(parent_uuid: str, child_obj: SerializableObject, index: int = -1) ProtocolMessage | None[source]
Insert child_obj into the parent container locally.
- Parameters:
parent_uuid – GUID of the parent container.
child_obj – The OTIO object to insert.
index – Position at which to insert;
-1appends.
- Returns:
The generated patch payload, or
Noneif parent_uuid is not found.- Return type:
dict or None
- move_child(parent_uuid: str, child_uuid: str, to_index: int) ProtocolMessage | None[source]
Move child_uuid within its parent container locally.
- Parameters:
parent_uuid – GUID of the parent container.
child_uuid – GUID of the child to move.
to_index – Target position in the parent’s child list.
- Returns:
The generated patch payload, or
Noneif parent/child is not found or index is unchanged.- Return type:
dict or None
- on_hierarchy_changed(callback: Callable[[str, str, str], None]) Callable[[str, str, str], None][source]
Register a callback for hierarchy change events.
- Parameters:
callback – Callable receiving
(parent_uuid, action, child_uuid)where action is one of"insert_child","remove_child", or"move_child".- Returns:
The callback unchanged (decorator-compatible).
- Return type:
Callable
- on_property_changed(callback: Callable[[str, str, Any], None]) Callable[[str, str, Any], None][source]
Register a callback for property change events.
- Parameters:
callback – Callable receiving
(target_uuid, path, new_value).- Returns:
The callback unchanged (decorator-compatible).
- Return type:
Callable
- remove_child(parent_uuid: str, child_uuid: str) ProtocolMessage | None[source]
Remove child_uuid from its parent container locally.
- Parameters:
parent_uuid – GUID of the parent container.
child_uuid – GUID of the child to remove.
- Returns:
The generated patch payload, or
Noneif parent or child is not found.- Return type:
dict or None
- set_property(target_uuid: str, path: str, value: Any) ProtocolMessage | None[source]
Set property path to value on object target_uuid locally.
- Parameters:
target_uuid – GUID of the target object.
path – Target property or metadata sub-key path (e.g.
"name"or"metadata/custom").value – New value; must be a primitive type.
- Returns:
The generated patch payload, or
Noneif target_uuid is not found.- Return type:
dict or None
- traverse_and_map(item: SerializableObject) None[source]
Recursively assign GUIDs to all OTIO objects under item and index them.
- Parameters:
item – Root OTIO object to traverse.
- traverse_and_map_preserve(item: SerializableObject) None[source]
Recursively assign GUIDs to all OTIO objects under item without overwriting existing entries.
- Parameters:
item – Root OTIO object to traverse.
color¶
Colour-pipeline metadata helpers, including recognition of the special OCIO colorspace.
Color pipeline metadata schema, vocabulary parsing, and resolution.
This module bridges the OTIO Color Pipeline Model RFC through OTIO metadata
until the native fields land in OTIO core. The metadata keys mirror the RFC
field names verbatim so a future migration to native Timeline.color /
Composable.color_space fields is a key move, not a reshape:
Timeline.metadata["color"]— a config group withconfig,working_spaceandoutput_spacestring entries.Composable.metadata["color_space"]— a clip’s input colorspace as a single vocabulary-prefixed string (e.g."ocio:ACEScg").
The module carries no rendering logic and resolves nothing to a transform — it only parses names, reads the metadata defensively, and applies the hierarchical resolution rule. Host adapters (OpenRV, xStudio) turn the resolved name into an actual color transform against their own OCIO config.
It deliberately does not import opentimelineio so it stays importable in
isolation; it only touches the .metadata mapping of the objects passed in.
- otio_sync_core.color.COLOR_GROUP = 'color'
timeline.metadata[COLOR_GROUP].- Type:
Key of the timeline-level color config group
- otio_sync_core.color.COLOR_SPACE = 'color_space'
clip.metadata[COLOR_SPACE].- Type:
Key of a composable’s input colorspace
- otio_sync_core.color.CONFIG = 'config'
Sub-keys of the timeline color group.
- otio_sync_core.color.DEFAULT_VOCABULARY = 'ocio'
Vocabulary assumed for a bare (unprefixed) name.
- otio_sync_core.color.RESOLVED_VOCABULARIES = ('ocio', 'interop')
Vocabularies actively resolved by host adapters in v1. Other tags (
cicp,resolve,aces,custom, …) are preserved verbatim.
- otio_sync_core.color.is_resolved_vocabulary(value: str) bool[source]
Return whether value’s vocabulary is one a host adapter resolves in v1.
Unknown vocabularies are valid and MUST be preserved verbatim, but host adapters are only expected to resolve
RESOLVED_VOCABULARIES.- Parameters:
value – A colorspace string.
- Returns:
Trueif the vocabulary is inRESOLVED_VOCABULARIES.
- otio_sync_core.color.parse_colorspace(value: str) tuple[str, str][source]
Split a colorspace string into
(vocabulary, name).The text before the first
:is the vocabulary tag iff it is a valid tag (ASCII[a-z0-9_]); the remainder is the name. A string with no colon — or whose leading segment is not a valid tag — is treated as a bare name inDEFAULT_VOCABULARY. This matches the RFC rule that names legitimately containing a colon (e.g."ocio:Utility - Curve - sRGB") must be prefixed, while unprefixed names never are.- Parameters:
value – The colorspace string (e.g.
"ocio:ACEScg","ACEScg").- Returns:
(vocabulary, name).vocabularyis the lowercased tag, orDEFAULT_VOCABULARYfor a bare name.
- otio_sync_core.color.read_color_space(obj: Any) str | None[source]
Return a composable’s
color_spacestring, orNoneif unset.- Parameters:
obj – An OTIO
Composable(or any object with.metadata).- Returns:
The non-empty colorspace string, or
None.
- otio_sync_core.color.read_timeline_color(timeline: Any) dict[source]
Return the timeline color group as a plain
dict(empty if unset).- Parameters:
timeline – An OTIO
Timeline(or any object with.metadata).- Returns:
A dict possibly containing
CONFIG,WORKING_SPACEandOUTPUT_SPACE. Empty when the timeline is unmanaged.
- otio_sync_core.color.resolve_input_colorspace(clip: Any, timeline: Any | None = None, host_default: str | None = None) str | None[source]
Resolve a clip’s effective input colorspace.
Resolution order, per the color-pipeline-sync capability:
the clip’s own
color_spaceif set;otherwise the timeline’s
working_space;otherwise host_default.
No media-reference field or provenance data is consulted.
- Parameters:
clip – The OTIO
Clipwhose input space is being resolved.timeline – The owning
Timeline, consulted forworking_space. Optional; passNoneto skip straight to host_default.host_default – The host’s fallback colorspace name.
- Returns:
The effective colorspace string, or
Nonewhen nothing is set.
xs_annotation_codec¶
Encoding and decoding of xStudio annotations to and from the OTIO sync format.
Bidirectional codec: xStudio pen-stroke dicts ↔ OTIO SyncEvent objects.
Both conversion directions are pure functions with no xStudio SDK dependency.
Callers are responsible for registering the SyncEvent schemadef in
OTIO_PLUGIN_MANIFEST_PATH before importing this module.
Coordinate systems
System |
x range |
y |
origin |
|---|---|---|---|
xStudio native |
|
down |
centre |
OTIO SyncEvent / RV paint |
|
up |
centre |
Scale factor: aspect_half = W / (2 × H).
For 16:9 media: 1920 / (2 × 1080) ≈ 0.8889.
xStudio → OTIO: x_otio = x_xs * aspect_half, y_otio = −y_xs * aspect_half
OTIO → xStudio: x_xs = x_otio / aspect_half, y_xs = −y_otio / aspect_half
- otio_sync_core.xs_annotation_codec.sync_events_to_xs_captions(commands: list, aspect_half: float) list[source]
Convert
TextAnnotationSyncEvent objects to xStudio caption dicts.- Parameters:
commands – Sequence of SyncEvent objects; only
TextAnnotationentries are processed.aspect_half –
W / (2H)coordinate scale factor.
- Returns:
List of xStudio caption dicts suitable for
Bookmark.set_annotation(captions=...).- Return type:
list
- otio_sync_core.xs_annotation_codec.sync_events_to_xs_strokes(commands: list, aspect_half: float) list[source]
Convert a
PaintStart/PaintPointscommand sequence to xStudio stroke dicts.Inverts the H-normalised / Y-up (OTIO/RV) coordinate system back to the W-normalised / Y-down system that xStudio expects:
x_xs = x_otio / aspect_half y_xs = −y_otio / aspect_half
- Parameters:
commands – Sequence of SyncEvent objects from an annotation clip (
PaintStart,PaintPoints, andTextAnnotationentries are all accepted; only the paint entries are processed here).aspect_half –
W / (2H)derived from the target media resolution.
- Returns:
List of xStudio pen-stroke dicts suitable for
Bookmark.set_annotation(strokes=...).- Return type:
list
- otio_sync_core.xs_annotation_codec.xs_captions_to_sync_events(captions: list, aspect_half: float, existing_uuids: List[str] | None = None) list[source]
Convert xStudio captions dicts to a sequence of OTIO SyncEvent objects.
- Parameters:
captions – List of xStudio caption dicts from
Bookmark.annotation_data["Data"]["captions"].aspect_half –
W / (2H)coordinate scale factor.existing_uuids – When provided, reuse these UUID strings (by index) instead of generating fresh ones. Pass the existing UUIDs from the OTIO clip when building a replacement command list so that RV can update text nodes in place.
- Returns:
List of
TextAnnotationSyncEvent objects.- Return type:
list
- otio_sync_core.xs_annotation_codec.xs_strokes_to_sync_events(pen_strokes: list, aspect_half: float, uuid_list: List[str] | None = None) list[source]
Convert xStudio pen_strokes dicts to a sequence of OTIO SyncEvent objects.
Each xStudio stroke dict becomes a
PaintStart+PaintPointspair. Point coordinates are converted from W-normalised / Y-down to H-normalised / Y-up by multiplying x/y byaspect_half/−aspect_half.xStudio V4 stroke dicts use
"colour": [r, g, b]and"type": "Brush"/"Pen"/"Erase"(the legacy V3 keys"r","g","b"and"is_erase_stroke"are automatically upgraded by xStudio’s annotation deserialiser before they reach Python).- Parameters:
pen_strokes – List of xStudio pen-stroke dicts as returned by
Bookmark.annotation_data["Data"]["pen_strokes"].aspect_half –
W / (2H)coordinate scale factor.uuid_list – Optional list of stable UUID strings, one per stroke. When given,
uuid_list[i]is used for stroke i instead of a freshly generated UUID. Pass this from_stroke_uuid_cachewhen repeated partial broadcasts of the same frame must share stable UUIDs.
- Returns:
List of SyncEvent objects (interleaved
PaintStart/PaintPointsentries).- Return type:
list