From the caprid-moq-media package: import moq_media
Pure LOC/MSF format helpers; transport belongs to the application.
The one encoder, moq_media.opus, needs the opus extra and is not imported here.
Functions
Section titled “Functions”annexb_to_avcc
Section titled “annexb_to_avcc”def annexb_to_avcc(data: bytes, *, drop_parameter_sets: bool = True) -> bytesRe-frame an Annex B access unit as 4-byte-length-prefixed NAL units.
avc1 promises parameter sets out of band, so SPS/PPS (and access-unit delimiters) are dropped
by default; they travel in the avcC instead.
avc_codec_string
Section titled “avc_codec_string”def avc_codec_string(avcc: bytes) -> strThe avc1.PPCCLL WebCodecs codec string an avcC declares (RFC 6381 §3.3).
build_avcc
Section titled “build_avcc”def build_avcc(sps: Sequence[bytes], pps: Sequence[bytes]) -> bytesBuild an AVCDecoderConfigurationRecord (avcC) with 4-byte NAL length prefixes.
build_interrupt
Section titled “build_interrupt”def build_interrupt( *, until_us: int, from_us: int | None = None, track: str = AUDIO_TRACK,) -> bytesAn interrupt object’s payload: compact JSON, {"track":…,"fromUs":…,"untilUs":…}, fromUs only if given.
Raises ValueError on an empty track, a timestamp that is not a nonnegative safe integer, or from_us not
before until_us.
build_media_timeline
Section titled “build_media_timeline”def build_media_timeline(entries: Iterable[MediaTimelineEntry]) -> bytesReturn a timeline object’s payload: the records in the order given, as compact JSON.
The same form serves an independent document (every retained record) and an incremental one (only new ones).
build_msf_catalog
Section titled “build_msf_catalog”def build_msf_catalog( namespace: Sequence[str], *, video: VideoTrackInfo | None = None, audio: AudioTrackInfo | None = None, media: Sequence[VideoTrackInfo | AudioTrackInfo] = (), data: Sequence[DataTrackInfo] = (), media_timeline: MediaTimelineTrackInfo | None = None, generated_at_ms: int | None = None,) -> bytesThe independent MSF catalog JSON for a live LOC broadcast, byte-compatible with the Video.js parser.
The catalog leaves isComplete out: msf-01 §5.1.3 forbids it while false, and once present it can
never be removed. End a broadcast with build_msf_end_catalog.
video and audio are shorthand for one-entry media lists, listed first. media lists
further video and audio tracks in order: simulcast layers sharing an alt_group, or several
sources in their own render groups. Every media entry carries its render group (default 1); the
builder sets no target latency or buffer, which §5.2.8 and §5.2.9 require to be identical across
a render group.
build_msf_end_catalog
Section titled “build_msf_end_catalog”def build_msf_end_catalog(*, generated_at_ms: int | None = None) -> bytesThe catalog that ends a broadcast (msf-01 §11.3): isComplete: true and no tracks.
It commits that no track will be added and no content published, so send it only for a broadcast that ends on purpose, once every media track has ended (closing a track sends PUBLISH_DONE Track Ended), and never on a crash or lost connection.
build_transcript_segment
Section titled “build_transcript_segment”def build_transcript_segment( *, seq: int, from_us: int, text: str, until_us: int | None = None, final: bool = False, track: str = AUDIO_TRACK,) -> bytesA transcript object’s payload: compact JSON, {"seq":…,"track":…,"fromUs":…,"untilUs":…,"text":…,"final":…}.
untilUs only if given. Raises ValueError on a number that is not a nonnegative safe integer, an empty
track, text that is not valid Unicode, until_us not after from_us, or a final segment without until_us.
compose_player_url
Section titled “compose_player_url”def compose_player_url( relay_url: str, namespace: Sequence[str], *, token: str | None = None, track_name: str = CATALOG_TRACK,) -> strCompose the moqt:// MSF source URL a Video.js MoQ player takes, the inverse of its parser.
The relay origin may be written as https:// or moqt://; an existing path and query
survive. The token, when given, rides the c4m fragment parameter, which the player moves onto
the connect URL’s jwt query parameter.
encode_name_component
Section titled “encode_name_component”def encode_name_component(value: str) -> strMSF namespace/name string form: [a-zA-Z0-9_] literal, every other UTF-8 byte as .xx lowercase hex.
encode_namespace_name
Section titled “encode_namespace_name”def encode_namespace_name(namespace: Sequence[str], track_name: str) -> strThe ns1-ns2--name identifier that follows #msf: in a source URL (msf-01 §11.1.2).
extract_parameter_sets
Section titled “extract_parameter_sets”def extract_parameter_sets(data: bytes) -> tuple[list[bytes], list[bytes]]SPS and PPS NAL units found in an Annex B byte stream (an encoder’s extradata or a keyframe).
find_replacement_track
Section titled “find_replacement_track”def find_replacement_track( current: MsfCatalogTrack, tracks: Sequence[MsfCatalogTrack],) -> MsfCatalogTrack | NoneFind the track in a newer catalog that replaces current, a track of an older one.
A publisher that replaces a track lists a new name in its place (msf-01 §5.3: a changed track is a
new Namespace|Name tuple), as a voice agent does to interrupt its own reply. While tracks still
lists current’s namespace and name the answer is None. Otherwise it is the first track listed
with current’s namespace, role and render group, or None when there is none (the track was
removed, not replaced).
group_msf_tracks
Section titled “group_msf_tracks”def group_msf_tracks(tracks: Sequence[MsfCatalogTrack]) -> tuple[MsfTrackGroup, ...]Group catalog tracks into the sets a viewer picks one track from, in order of each set’s first track.
Tracks sharing a render group and an alternate group form one set, and every other track is a
set of its own. A viewer plays one track from each set of a render group, and moves between
alternates with want(..., until=...).
iter_annexb_nal_units
Section titled “iter_annexb_nal_units”def iter_annexb_nal_units(data: bytes) -> Iterator[bytes]Split an Annex B byte stream (3- or 4-byte start codes) into NAL units without their start codes.
loc_properties
Section titled “loc_properties”def loc_properties( timestamp_us: int, *, timescale: int = MICROSECONDS_PER_SECOND, video_config: bytes | None = None, audio_config: bytes | None = None,) -> list[KeyValuePair]Package a microsecond timestamp; pass decoder configuration only on keyframes.
Other timescales round to the nearest tick, with ties rounded upward.
loc_timestamp_us
Section titled “loc_timestamp_us”def loc_timestamp_us( properties: Iterable[KeyValuePair], *, track_properties: Iterable[KeyValuePair] = (),) -> int | NoneReturn an object’s LOC Timestamp in microseconds, or None when it has no usable one.
The Timestamp is scaled by the first Timescale that is a positive safe integer: the object’s, then the track’s
(track_properties, as SUBSCRIBE_OK carries them). The MSF catalog’s timescale is not consulted: MSF
never ties it to the LOC Timestamp. With none, the Timestamp already is wall-clock microseconds since the Unix
epoch (LOC §2.3.1.1). Ties round upward; a result past 2^53 - 1 is None.
loc_track_properties
Section titled “loc_track_properties”def loc_track_properties(timescale: int = MICROSECONDS_PER_SECOND) -> list[KeyValuePair]Declare timestamp units on a LOC track; returns fresh properties on every call.
media_timeline_entry
Section titled “media_timeline_entry”def media_timeline_entry( timestamp_us: int, group: int, *, object: int = 0, wallclock_us: int | None = None,) -> MediaTimelineEntryReturn the record for an object whose LOC timestamp on the broadcast timeline is timestamp_us.
wallclock_us is when that media was captured, as Unix-epoch microseconds. It defaults to timestamp_us,
since the broadcast timeline is anchored at the epoch; pass 0 for unknown, as msf-01 allows.
nal_unit_type
Section titled “nal_unit_type”def nal_unit_type(nal: bytes) -> intThe nal_unit_type of a NAL unit without its start code: 7 for SPS, 8 for PPS, 5 for IDR.
opus_packet_duration_us
Section titled “opus_packet_duration_us”def opus_packet_duration_us(packet: bytes) -> int | NoneAn Opus packet’s duration in microseconds from its TOC byte (RFC 6716 §3.1), or None if truncated.
Code 0 holds one frame, codes 1 and 2 two, and code 3 the count in the next byte’s low six bits.
parse_interrupt
Section titled “parse_interrupt”def parse_interrupt(payload: bytes) -> InterruptRead an interrupt object’s payload.
Raises ValueError on a payload that is not a UTF-8 JSON object with a non-empty string track and a
nonnegative safe-integer untilUs, or whose fromUs, when present, is not one before untilUs. Other
fields are ignored.
parse_media_timeline
Section titled “parse_media_timeline”def parse_media_timeline(payload: bytes) -> tuple[MediaTimelineEntry, ...]Read a timeline object’s payload into its records, in document order.
Raises ValueError on a payload that is not a UTF-8 JSON array. Records that do not read as a nonnegative
safe-integer [number, [number, number], number] are skipped, a §7.4 template among them; extra items are
ignored. MSF_COMPRESSION payloads are not decompressed.
parse_msf_catalog
Section titled “parse_msf_catalog”def parse_msf_catalog(payload: bytes, *, catalog_namespace: Sequence[str]) -> MsfCatalogRead an independent MSF catalog object’s payload into its track descriptions.
catalog_namespace is the namespace of the catalog track itself, which tracks that declare
none inherit (§5.2.2). Raises ValueError on a payload that is not a UTF-8 JSON object, a
missing or unsupported version, a missing tracks array, or a delta update
(deltaUpdate, not supported). Everything else is lenient, as msf-01 requires: unknown fields
and fields of the wrong type are ignored, track entries without a string name are skipped,
and packaging is returned as declared, not interpreted.
parse_transcript_segment
Section titled “parse_transcript_segment”def parse_transcript_segment(payload: bytes) -> TranscriptSegmentRead a transcript object’s payload.
Raises ValueError on a payload that is not a UTF-8 JSON object with a nonnegative safe-integer seq and
fromUs, a non-empty string track, a string text and a boolean final, or whose untilUs, when
present, is not one after fromUs, or that is final without untilUs. Other fields are ignored.
Classes
Section titled “Classes”AudioTrackInfo
Section titled “AudioTrackInfo”class AudioTrackInfoThe audio track build_msf_catalog describes. Fields left as None are omitted.
Attributes
-
name:str -
codec:str -
samplerate:int— Opus always declares 48000, the rate it decodes at, whatever is given here. -
channel_config:str -
bitrate:int | None -
init_data:bytes | None— Decoder configuration, emitted inline asinitDataList. -
render_group:int— Render group (msf-01 §5.2.11): tracks sharing one play together on one timeline.Give a second source, such as a screen share and its audio, its own.
-
alt_group:int | None— Alternate group (msf-01 §5.2.12): alternate versions of the same content, such as simulcast layers.A viewer subscribes to one member. Members share a role and a render group.
BroadcastClock
Section titled “BroadcastClock”class BroadcastClockA publisher’s single clock: every track in the broadcast is stamped from it, in Unix-epoch microseconds.
now_us is the monotonic time source in microseconds, time.monotonic_ns() // 1000 by default.
origin_us is a moment on that source in integer microseconds; it defaults to the source’s time now, rounded
to whole microseconds. origin_epoch_us is that moment as Unix-epoch microseconds; it defaults to
time.time_ns() at creation less the source time elapsed since the origin, read once. A time t on the
source maps to origin_epoch_us + t - origin_us, so the timeline advances at the source’s rate.
Attributes
origin_us:int(property) — The origin, on the source clock.origin_epoch_us:int(property) — The origin as microseconds since the Unix epoch.
BroadcastClock.now_us
Section titled “BroadcastClock.now_us”def now_us(self) -> intReturn the timeline now: microseconds since the Unix epoch.
BroadcastClock.from_source_us
Section titled “BroadcastClock.from_source_us”def from_source_us(self, source_us: int) -> intMap a timestamp on this clock’s own source to Unix-epoch microseconds, rounded to whole microseconds.
For a capture clock with another origin, use source_timeline.
BroadcastClock.source_timeline
Section titled “BroadcastClock.source_timeline”def source_timeline( self, *, tolerance_us: int = 5000, capture_latency_us: int = 0,) -> SourceTimelineMap a clock with its own origin and rate, kept on the broadcast timeline by calibration.
An AudioContext-style clock, or capture timestamps from another origin. tolerance_us is how far a
calibration may disagree with the current anchor before it replaces it. capture_latency_us is the
capture device’s input delay, subtracted from every timestamp; no helper knows it, so the application
supplies it and sets it again when the device changes.
BroadcastClock.sample_timeline
Section titled “BroadcastClock.sample_timeline”def sample_timeline( self, sample_rate: int, *, first_sample_us: int | None = None,) -> SampleTimelineAnchor a sample counter at first_sample_us on the timeline (default: now).
The device clock that drives the counter drifts against the source, so re-anchor with a fresh timeline when the two disagree by more than a frame.
DataTrackInfo
Section titled “DataTrackInfo”class DataTrackInfoA non-media track build_msf_catalog lists by name and role only.
Attributes
name:strrole:str | None— Must not be a media role (video,audio,captionand the like).
FrameSequencer
Section titled “FrameSequencer”class FrameSequencerOne track’s frame sequencer. Feed it the subscription’s events, then poll() for frames.
clock is the broadcast’s playout clock: it holds the target latency and is where lateness is measured.
grouping is "keyframe" (a keyframe opens each group and later objects depend on it, as video) or "frame"
(every group is independently decodable, as audio). track_properties are SUBSCRIBE_OK’s, which may declare the
Timescale.
Attributes
track_properties:Sequence[KeyValuePair]— The track properties used to read timestamps; set from each SUBSCRIBE_OK.stats
FrameSequencer.object
Section titled “FrameSequencer.object”def object(self, obj: MoqtObject) -> NoneAn object arrived.
FrameSequencer.group_ended
Section titled “FrameSequencer.group_ended”def group_ended(self, group_id: int) -> NoneA group’s stream ended cleanly: no further objects of it will come.
FrameSequencer.group_reset
Section titled “FrameSequencer.group_reset”def group_reset(self, group_id: int) -> NoneA group’s stream was reset: its remaining objects will not come.
FrameSequencer.restart
Section titled “FrameSequencer.restart”def restart(self) -> NoneThe subscription was replaced (a Restart or a Move): forget everything and reset the clock; group IDs start over.
FrameSequencer.poll
Section titled “FrameSequencer.poll”def poll(self, max_frames: int | None = None) -> SequencerPollTake the frames that are ready, at most max_frames (default all), and when to poll again.
A reader that cannot take frames as they come polls one at a time, so each is judged late when it is taken.
FrameSequencerStats
Section titled “FrameSequencerStats”class FrameSequencerStatsCounters, cumulative across restarts.
Attributes
delivered:int— Frames returned bypoll().late:int— Frames discarded: too late, or behind what was already delivered.skipped_groups:int— Groups abandoned or passed over without being delivered whole.resyncs:int— Resyncs: deliveries after nothing arrived within the target latency.untimed:int— Objects without a LOC timestamp, which cannot be sequenced.
H264Config
Section titled “H264Config”class H264ConfigAn avc1 decoder record and the codec string derived from it.
Attributes
avcc:bytescodec:str(property) — Theavc1.PPCCLLcodec string the record declares.
H264Config.from_annexb
Section titled “H264Config.from_annexb”def from_annexb(cls, data: bytes) -> H264ConfigBuild the record from the SPS and PPS in Annex B bytes (an encoder’s extradata or a keyframe).
Raises ValueError as build_avcc does.
H264Config.to_avcc
Section titled “H264Config.to_avcc”def to_avcc(self, data: bytes) -> bytesRe-frame an Annex B access unit for this configuration, dropping in-band SPS/PPS and AUDs.
Interrupt
Section titled “Interrupt”class InterruptOne interrupt: drop the named audio track’s audio in a timeline range, in microseconds.
Attributes
-
track:str— The audio track in the same namespace whose audio to drop. -
until_us:int— Drop before this timestamp: the end of the last audio the talker sent.Frames that arrive later inside the range are dropped too, so the talker’s next audio must start at or after it.
-
from_us:int | None— Drop from this timestamp on;Nonedrops from the oldest audio held.
MediaTimeline
Section titled “MediaTimeline”class MediaTimelineThe retained entries of one broadcast’s media timeline, and the independent document they make.
max_entries bounds the document: past it, the oldest entry is dropped. The default 512 is about 18 KB, 17
minutes of two-second GOPs. A supervised publisher’s next generation is a new broadcast and starts a new timeline.
Attributes
entries:tuple[MediaTimelineEntry, ...](property) — The retained entries, oldest first.document:bytes(property) — The independent document of the retained entries;[]before the first.
MediaTimeline.add
Section titled “MediaTimeline.add”def add( self, timestamp_us: int, group: int, *, object: int = 0, wallclock_us: int | None = None,) -> bytesRecord the object at group and object whose LOC timestamp is timestamp_us; return the document.
Call it once per group of the indexed track, for its first object, when that object was sent. wallclock_us
is as for media_timeline_entry: the timestamp itself by default. A rejected argument raises
ValueError and leaves the timeline unchanged.
MediaTimelineEntry
Section titled “MediaTimelineEntry”class MediaTimelineEntryOne media timeline record, [media_time_ms, [group, object], wallclock_ms] on the wire (msf-01 §7.1.1).
Attributes
media_time_ms:int— Media time of the first sample in the referenced object, floored to whole milliseconds.group:intobject:int— The referenced object’s location in the track the timeline indexes.wallclock_ms:int— Milliseconds since the Unix epoch when that media was captured; 0 when unknown.
MediaTimelineTrackInfo
Section titled “MediaTimelineTrackInfo”class MediaTimelineTrackInfoThe Media Timeline track (msf-01 §7) build_msf_catalog announces.
The entry is listed only while a track it depends on is listed; see spec/media-layer.md.
Attributes
-
name:str -
depends:Sequence[str] | None— The video and audio tracks the timeline applies to (msf-01 §7.2); its locations point into the first.Default: the first video track followed by the first audio track, whichever the catalog has.
MsfCatalog
Section titled “MsfCatalog”class MsfCatalogAn independent MSF catalog (msf-01 §5.1), as parse_msf_catalog reads it.
Attributes
version:strtracks:tuple[MsfCatalogTrack, ...]generated_at_ms:int | Noneis_complete:bool—Trueonly when the catalog declares the broadcast complete (§5.1.3).
MsfCatalogTrack
Section titled “MsfCatalogTrack”class MsfCatalogTrackOne track object of an independent MSF catalog (msf-01 §5.2), as parse_msf_catalog reads it.
Attributes
namespace:tuple[str, ...]— The track’s own slash-joinednamespacesplit into fields, else the catalog track’s.name:strpackaging:str— Payload encapsulation as declared (loc, or e.g. moq-rscmaf/legacy); empty when absent.is_live:bool—Falsewhen absent.role:str | Nonecodec:str | Nonewidth:int | Noneheight:int | Noneframerate:float | Nonesamplerate:int | Nonechannel_config:str | Nonebitrate:int | Nonerender_group:int | None— Render group (§5.2.11): tracks sharing one are meant to play together.alt_group:int | None— Alternate group (§5.2.12): tracks sharing one are alternate versions of one content.mimetype:str | None— MIME type (§5.2.19), e.g.application/jsonon a media timeline track.depends:tuple[str, ...] | None— Names of the tracks this one depends on (§5.2.14), its string items only.init_data:bytes | None— Decoder init data, resolved frominitRefthrough the catalog’s inlineinitDataList.
MsfTrackGroup
Section titled “MsfTrackGroup”class MsfTrackGroupA set of catalog tracks a viewer picks one of.
The members of one alternate group (§5.2.12) within one render group, or a single track that names no alternate group.
Attributes
tracks:tuple[MsfCatalogTrack, ...]— The members, in catalog order.render_group:int | Nonealt_group:int | None—Nonefor a group holding exactly one track that names no alternate group.
PlayoutClock
Section titled “PlayoutClock”class PlayoutClockA player’s clock for one broadcast’s timeline. Audio is the master; video asks it when each frame is due.
late_drop_us (default 40 ms, EBU R37’s audio-early limit) is how late a frame may be shown;
max_video_delay_us (default 1 s) is how far a frame may lead before the clock re-syncs;
audio_timeout_us (default 500 ms) is how long audio may stay silent before wall-clock pacing takes over.
target_latency_us is how far behind the position a frame may still be delivered for decoding, and how late a
frame must be before wall pacing re-anchors on it; by default twice the longest of the last four keyframe
intervals reported, or 2 s before any is.
now_us is the time source, time.monotonic_ns() // 1000 by default.
Attributes
mode:PlayoutMode(property) —idlebefore anything anchors it,audiowhile audio reports arrive,wallotherwise.late_drop_us:int(property) — How late a frame may be shown, in microseconds: thelate_drop_usoption.target_latency_us:float(property) — The target latency now: the option, or twice the longest of the last four keyframe intervals reported.resets:int(property) — How many timesreset()has been called: a frame reader sees from it that the timeline changed.
PlayoutClock.report_audio
Section titled “PlayoutClock.report_audio”def report_audio(self, timestamp_us: int, *, at_us: int | None = None) -> NoneReport that the audio with this timeline timestamp is audible at local time at_us (default: now).
That is what was handed to the output, less the jitter-buffer depth, the output latency, and any extra path.
PlayoutClock.stop_audio
Section titled “PlayoutClock.stop_audio”def stop_audio(self) -> NoneAudio is muted or ended: pace by wall clock from the current position.
PlayoutClock.due
Section titled “PlayoutClock.due”def due(self, timestamp_us: int) -> PlayoutDecisionDecide whether the frame with this timeline timestamp should wait, be shown now, or be dropped.
PlayoutClock.position_us
Section titled “PlayoutClock.position_us”def position_us(self) -> float | NoneReturn the timeline position now, or None while idle.
PlayoutClock.report_keyframe_interval
Section titled “PlayoutClock.report_keyframe_interval”def report_keyframe_interval(self, interval_us: float) -> NoneReport the spacing between two consecutive keyframes on the timeline; a frame reader does this.
PlayoutClock.reset
Section titled “PlayoutClock.reset”def reset(self) -> NoneForget every anchor, at a Restart, a Move, a resync, or a new catalog: the next frame anchors wall pacing.
The keyframe intervals reported so far are kept.
PlayoutClock.local_now_us
Section titled “PlayoutClock.local_now_us”def local_now_us(self) -> floatThe clock’s local time source now, in microseconds: what a frame reader measures its waits in.
PlayoutDecision
Section titled “PlayoutDecision”class PlayoutDecisionWhat to do with one video frame.
offset_us is the frame’s timestamp minus the clock’s position (positive: early). wait_us is how long to
hold the frame before asking again, and is 0 unless action is "wait".
Attributes
action:PlayoutActionoffset_us:intwait_us:int
SampleTimeline
Section titled “SampleTimeline”class SampleTimelineMaps an audio sample counter that starts at zero onto the broadcast timeline.
Attributes
sample_rate:intfirst_sample_us:int
SampleTimeline.timestamp_us
Section titled “SampleTimeline.timestamp_us”def timestamp_us(self, sample_index: int) -> intReturn the broadcast time of the sample at this index: whole microseconds after the first sample.
SequencedFrame
Section titled “SequencedFrame”class SequencedFrameOne frame in decode order.
Attributes
group_id:intobject_id:inttimestamp_us:int— The LOC timestamp in microseconds on the broadcast timeline.key:bool— Whether the frame starts its group: a keyframe under keyframe grouping, every frame under frame grouping.payload:bytesproperties:list[KeyValuePair]discontinuity:FrameDiscontinuity | None— Set on the first frame after a skip, a resync, or a restart.
SequencerPoll
Section titled “SequencerPoll”class SequencerPollWhat FrameSequencer.poll returns.
Attributes
frames:list[SequencedFrame]— The frames ready now, in decode order.wait_us:int | None— Poll again after this many microseconds, when a wait can end; 0 when more is ready now (pastmax_frames, or a resync that takes a poll of its own);Nonewhen only new objects can change it.
SourceTimeline
Section titled “SourceTimeline”class SourceTimelineMaps timestamps from a clock with its own origin and rate onto the broadcast timeline.
Calibrate it with measured pairs of source time and time on the broadcast clock’s own source; without a
calibration, the first timestamp mapped is anchored at the clock’s now_us(). capture_latency_us is
subtracted from every timestamp.
Attributes
-
capture_latency_us:int(property) — The capture device’s input delay in integer microseconds, subtracted from every timestamp.Set it again when the input device changes; it applies from the next timestamp and never to calibration.
-
anchor_source_us:float | None(property) — The source time of the current anchor;Noneuntil the first calibration or timestamp.
SourceTimeline.calibrate
Section titled “SourceTimeline.calibrate”def calibrate(self, source_us: int, *, at_us: int | None = None) -> boolRecord that source time source_us happened at at_us on the clock’s own source (default: now).
The pair becomes the anchor when there is none, or when the current anchor predicts it more than the tolerance off: the source drifted, or jumped. Returns whether the anchor changed.
SourceTimeline.timestamp_us
Section titled “SourceTimeline.timestamp_us”def timestamp_us(self, source_us: int) -> intReturn the broadcast time of this source timestamp, rounded to whole microseconds, less the capture latency.
Without an anchor, the first call anchors this timestamp at the clock’s now_us().
Transcript
Section titled “Transcript”class TranscriptWhat a listener has read of one audio track’s transcript: each segment’s latest version, less what was cut.
apply each segment read for the track and cut on each interrupt for it; lines_at gives what to show as
the audio plays. A segment’s final version is kept against later ones. Cuts are remembered, so a version that
arrives after one is clipped as if it had arrived before. A segment is known by its seq within one run of the
talker’s track, so use a new Transcript when the talker starts over.
Attributes
lines:list[TranscriptLine](property) — Every segment held, in timeline order.
Transcript.apply
Section titled “Transcript.apply”def apply(self, segment: TranscriptSegment) -> boolKeep a segment’s version, clipped by the cuts so far; False if it changed nothing.
Ignored: a version of a segment the talker already sent final, a segment that starts inside a range cut earlier (as audio arriving there late is), and an interim version of a segment a cut ended. A cut segment takes the talker’s final version, clipped: one whose audio ended before the cut is no longer cut.
Transcript.cut
Section titled “Transcript.cut”def cut( self, *, until_us: int, from_us: int | None = None, position_us: float | None = None,) -> NoneApply an interrupt’s range, with the playout position now (PlayoutClock.position_us).
Audio before the position has been heard, so the cut starts at the later of from_us and the position.
It drops the segments that start in the cut, and ends the one playing there at its start, final and cut.
Raises ValueError on a range an interrupt could not have, or a negative or non-finite position.
Transcript.lines_at
Section titled “Transcript.lines_at”def lines_at(self, position_us: float | None) -> list[TranscriptLine]The segments whose audio has started playing at this position, in timeline order; none while idle.
TranscriptLine
Section titled “TranscriptLine”class TranscriptLineA segment as a Transcript holds it: its latest version, and whether an interrupt cut it short.
Attributes
seq:inttrack:strfrom_us:intuntil_us:int | Nonetext:strfinal:boolcut:bool— An interrupt cut the segment’s audio short atuntil_us: the listener heard only the start of the text.
TranscriptSegment
Section titled “TranscriptSegment”class TranscriptSegmentOne version of a transcript segment: the text of the named audio track’s audio from from_us on.
Attributes
seq:int— The segment’s number; a later version of the same segment repeats it.track:str— The audio track in the same namespace whose audio this is the text of.from_us:int— Where the segment’s audio starts, in microseconds on the broadcast timeline.until_us:int | None— Where it ends;Nonewhile the talker does not know yet. A final segment has it.text:str— The text, which a later version replaces whole.final:bool— No later version follows.
VideoTrackInfo
Section titled “VideoTrackInfo”class VideoTrackInfoThe video track build_msf_catalog describes.
An avc1 or avc3 track needs init_data: without it the track is left out of the
catalog, and with it the codec string’s profile and level come from the avcC. Fields left as
None are omitted from the catalog.
Attributes
-
name:str -
codec:str— WebCodecs registry codec string, e.g.avc1.42E01F. -
width:int | None -
height:int | None -
framerate:float | None -
bitrate:int | None -
init_data:bytes | None— Decoder configuration (avcC foravc1), emitted inline asinitDataList. -
render_group:int— Render group (msf-01 §5.2.11): tracks sharing one play together on one timeline.Give a second source, such as a screen share and its audio, its own.
-
alt_group:int | None— Alternate group (msf-01 §5.2.12): alternate versions of the same content, such as simulcast layers.A viewer subscribes to one member. Members share a role and a render group.
Constants
Section titled “Constants”AUDIO_ONLY_TARGET_LATENCY_US
Section titled “AUDIO_ONLY_TARGET_LATENCY_US”AUDIO_ONLY_TARGET_LATENCY_US = 500000The target latency for a broadcast with no video: pass it as target_latency_us, since the 2 s default waits for
keyframe intervals an audio-only track never reports. It covers a player’s 400 ms jitter-buffer ceiling plus a wired
output delay, and a frame sequencer resyncs after half a second of nothing on time instead of two.
AUDIO_TRACK
Section titled “AUDIO_TRACK”AUDIO_TRACK = 'audio'CATALOG_TRACK
Section titled “CATALOG_TRACK”CATALOG_TRACK = 'catalog'INTERRUPT_TRACK
Section titled “INTERRUPT_TRACK”INTERRUPT_TRACK = 'interrupt'The interrupt track’s default name, listed in the catalog as a data track.
LOC_AUDIO_CONFIG
Section titled “LOC_AUDIO_CONFIG”LOC_AUDIO_CONFIG = 15LOC_DOCUMENT
Section titled “LOC_DOCUMENT”LOC_DOCUMENT = 'draft-ietf-moq-loc-04'LOC_TIMESCALE
Section titled “LOC_TIMESCALE”LOC_TIMESCALE = 8LOC_TIMESTAMP
Section titled “LOC_TIMESTAMP”LOC_TIMESTAMP = 16LOC_VIDEO_CONFIG
Section titled “LOC_VIDEO_CONFIG”LOC_VIDEO_CONFIG = 13MEDIA_TIMELINE_TRACK
Section titled “MEDIA_TIMELINE_TRACK”MEDIA_TIMELINE_TRACK = 'timeline'MICROSECONDS_PER_SECOND
Section titled “MICROSECONDS_PER_SECOND”MICROSECONDS_PER_SECOND = 1000000MSF_CATALOG_VERSION
Section titled “MSF_CATALOG_VERSION”MSF_CATALOG_VERSION = 'draft-01'MSF_DOCUMENT
Section titled “MSF_DOCUMENT”MSF_DOCUMENT = 'draft-ietf-moq-msf-01'OPUS_FRAME_US
Section titled “OPUS_FRAME_US”OPUS_FRAME_US = 20000The frame length moq_media.opus.OpusEncoder emits, and the LOC mapping’s one frame per group.
OPUS_SAMPLE_RATE
Section titled “OPUS_SAMPLE_RATE”OPUS_SAMPLE_RATE = 48000The rate every Opus stream decodes at (RFC 7845 §5.1), whatever rate the encoder was fed.
TRACK_TIMESCALE_PROPERTIES
Section titled “TRACK_TIMESCALE_PROPERTIES”TRACK_TIMESCALE_PROPERTIES = tuple(loc_track_properties())Track properties declared on every media track: timestamps are microseconds.
TRANSCRIPT_TRACK
Section titled “TRANSCRIPT_TRACK”TRANSCRIPT_TRACK = 'transcript'The transcript track’s default name, listed in the catalog as a data track.
VIDEO_TRACK
Section titled “VIDEO_TRACK”VIDEO_TRACK = 'video'FrameDiscontinuity
Section titled “FrameDiscontinuity”FrameDiscontinuity = Literal['gap', 'resync', 'restart']Why a frame is not the one decoded after the frame before it: gap (frames before it were skipped: missing,
reset, or too late), resync (nothing arrived within the target latency for one target latency; the sequencer reset
the clock), restart (the subscription was replaced: a want’s Restart or Move). A decoder resets there.
PlayoutAction
Section titled “PlayoutAction”PlayoutAction = Literal['wait', 'show', 'drop']What a video renderer does with one frame.
PlayoutMode
Section titled “PlayoutMode”PlayoutMode = Literal['idle', 'audio', 'wall']What a playout clock is paced by.