Constructor
new LOCParser(codecnon-null, frameDuration, normalizedCodecopt)
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
codec |
shaka.extern.MsfCodec | The negotiated draft's primitive codec, used to read the variable-length integers inside the Object Properties block. Draft-17 changed that encoding, so the same property bytes mean different numbers depending on which draft delivered them. | |
frameDuration |
number | ||
normalizedCodec |
string |
<optional> |
The track's codec as returned by shaka.util.MimeUtils.getNormalizedCodec, used to decode the Video Config property. Omit for tracks that cannot carry one. |
Members
SNAP_TOLERANCE_FRAMES_ :number
How far, in frame durations, a LOC timestamp may sit from the previous
reference's end and still be treated as publish-clock noise rather than a
discontinuity.
A real discontinuity is a track switch or a source change, which is orders
of magnitude larger than this.
Type:
- number
- Source:
SNAP_TOLERANCE_MIN_SEC_ :number
Minimum snap tolerance in seconds, regardless of frame duration.
Jitter is absolute, not proportional to frame rate, so a frame-counted
tolerance under-serves short frames: two frames of 48 kHz AAC is 42.7 ms,
which is inside the range ordinary publish-clock wander reaches.
Type:
- number
- Source:
TIMESCALE_ID_ :number
ID of the LOC Timescale property (draft-ietf-moq-loc-04 §2.3.1.2).
Unchanged across draft-02 and draft-04. Even, so the value is a bare vi64.
Type:
- number
- Source:
TIMESTAMP_ID_ :number
ID of the LOC Timestamp property (draft-ietf-moq-loc-04 §2.3.1.1).
Even, so the value is a bare vi64.
Type:
- number
- Source:
TIMESTAMP_ID_LEGACY_ :number
ID the LOC Timestamp property used up to draft-ietf-moq-loc-02.
Accepted alongside the current ID because publishers of both vintages are
deployed. It is read only when the current ID is absent, so a publisher
that sends 0x10 is never affected by it.
Type:
- number
- Source:
VIDEO_CONFIG_ID_ :number
ID of the LOC Video Config property (draft-ietf-moq-loc-04 §2.3.2.1).
Odd, so the value is a length-prefixed byte string: the codec's extradata,
which is an AVCDecoderConfigurationRecord for H.264 and an
HEVCDecoderConfigurationRecord for H.265. Carried only on key frames, and
only by publishers that strip parameter sets from the bitstream.
Type:
- number
- Source:
lastEndTime_ :number
End time of the last reference this parser emitted, or null before the
first one. See snapStartTime_.
Type:
- number
- Source:
Methods
avcParameterSets_(confignon-null) → {Array<!Uint8Array>}
Extracts the SPS and PPS NAL units from an AVCDecoderConfigurationRecord
(ISO/IEC 14496-15 §5.3.3.1), or null if the record is malformed.
Parameters:
| Name | Type | Description |
|---|---|---|
config |
Uint8Array |
- Source:
Returns:
- Type
- Array<!Uint8Array>
frameDurationFromTrack(track) → {number}
Computes the fixed frame duration (seconds) for a LOC track from MSF
catalog fields.
Rules:
- Video: `1 / track.framerate`
- Audio AAC (`mp4a.40.2`, AAC-LC): `1024 / track.samplerate`
(AAC-LC always uses 1024 samples per frame per ISO 14496-3)
- Audio Opus: `960 / track.samplerate`
(standard 20 ms Opus frame at any sample rate)
Parameters:
| Name | Type | Description |
|---|---|---|
track |
msfCatalog.Track |
- Source:
Returns:
- Type
- number
hevcParameterSets_(confignon-null) → {Array<!Uint8Array>}
Extracts the VPS, SPS and PPS NAL units from an
HEVCDecoderConfigurationRecord (ISO/IEC 14496-15 §8.3.3.1), or null if
the record is malformed.
The record groups NAL units into arrays keyed by type rather than listing
them in a fixed order, and H265.parseInfo() needs all three, so every
array is emitted in the order the record lists them.
Parameters:
| Name | Type | Description |
|---|---|---|
config |
Uint8Array |
- Source:
Returns:
- Type
- Array<!Uint8Array>
prependAv1ConfigObus_(payloadnon-null, confignon-null) → {Uint8Array}
Prepends the configOBUs of an AV1CodecConfigurationRecord to `payload`.
The record (AV1 ISOBMFF binding §2.3.3) is four fixed bytes describing the
decoder requirements, followed by configOBUs — the sequence header OBU,
byte for byte as it would appear in the bitstream. Only those trailing
bytes belong in the temporal unit; the fixed four are a container-level
summary and would decode as a stray OBU.
Returns `payload` unchanged when the record is malformed or carries no
OBUs, so a bad config degrades to the previous behaviour.
Parameters:
| Name | Type | Description |
|---|---|---|
payload |
Uint8Array | |
config |
Uint8Array |
- Source:
Returns:
- Type
- Uint8Array
readSizedNalu_(confignon-null, offset) → {?{data: !Uint8Array, offset: number}}
Reads one 2-byte big-endian length-prefixed NAL unit from a decoder
configuration record, or null if it does not fit.
Parameters:
| Name | Type | Description |
|---|---|---|
config |
Uint8Array | |
offset |
number |
- Source:
Returns:
- Type
- ?{data: !Uint8Array, offset: number}
parse(objnon-null) → {!{startTime: number, duration: number, payload: !Uint8Array}}
Parses a single LOC MoQ object.
Resolves `startTime` in priority order:
1. Timestamp property in public properties (`obj.extensions`)
2. Fallback: `groupId × frameDuration`
Parameters:
| Name | Type | Description |
|---|---|---|
obj |
shaka.msf.Utils.MOQObject |
- Source:
Returns:
- Type
- !{startTime: number, duration: number, payload: !Uint8Array}
parseExtensions_(datanon-null) → {Map<bigint, (bigint|!Uint8Array)>}
Parses the raw MOQ Object Properties buffer into a property map.
Wire format — a flat sequence of MOQT Key-Value-Pairs
(draft-ietf-moq-transport-18 §1.4.3) running to the end of the buffer;
the total-length prefix was already consumed by the transport layer
before storing the bytes in `obj.extensions`:
delta type (vi64)
value: vi64 when the resolved type is even
length (vi64) + bytes when the resolved type is odd
Types are DELTA encoded against the previous type in the block, starting
from 0. Reading them as absolute is worse than dropping the trailing
properties: a delta can collide with a real type and bind an unrelated
property's value to it. With Timestamp and Timescale present the deltas
are 6 and 2, so an absolute read loses Timescale entirely and
timestampToSeconds_ silently falls back to its microsecond default.
If parsing throws at any point the partial map built so far is returned,
so callers always receive a valid (possibly empty) map.
Parameters:
| Name | Type | Description |
|---|---|---|
data |
Uint8Array | Raw bytes from `obj.extensions`. |
- Source:
Returns:
- Type
- Map<bigint, (bigint|!Uint8Array)>
prependParameterSets_(payloadnon-null, confignon-null) → {Uint8Array}
Prepends the parameter sets carried in the Video Config property to
`payload`, as 4-byte length-prefixed NAL units.
LOC-04 §2.3.2.1 defines Video Config as "the extradata bytes defined by
the corresponding codec specification", so its layout depends on the
codec: an AVCDecoderConfigurationRecord for H.264, an
HEVCDecoderConfigurationRecord for H.265, and an
AV1CodecConfigurationRecord for AV1. A publisher that uses it has
stripped the parameter sets from the bitstream (§2.1.2), so re-inlining
them here restores exactly what was removed, at the earliest point, and
every downstream consumer stays unchanged — in-band parameter sets ahead
of a key frame are what decoders expect anyway.
The record's own `lengthSizeMinusOne` describes how the RECORD frames its
entries, not how the bitstream frames its NAL units, so the restored sets
are always emitted with the 4-byte prefix LOC-04 §2.1.3 specifies.
AV1 is the exception to that framing: OBUs are self-delimiting, carrying
their own type and size, and a temporal unit is a bare concatenation of
them. A length prefix would be read as an OBU header and desynchronise
the whole unit, so the sequence header OBUs are concatenated raw.
Returns `payload` unchanged when the codec has no known record layout or
the record is malformed: a bad config must degrade to the previous
behaviour rather than corrupt the bitstream.
Parameters:
| Name | Type | Description |
|---|---|---|
payload |
Uint8Array | |
config |
Uint8Array | The Video Config property value. |
- Source:
Returns:
- Type
- Uint8Array
readVi64At_(buffernon-null, offset) → {{value: bigint, bytesRead: number}}
Reads one variable-length integer from `buffer` at byte `offset`, in
whichever encoding the negotiated draft uses. Draft-17 replaced the QUIC
two-bit size tag with a leading-ones count, so the same bytes mean
different numbers under the two, and Object Properties are the one place
a packaging has to read them itself.
Synchronous equivalent of `Reader.u62WithSize()` in msf_classes.js.
Parameters:
| Name | Type | Description |
|---|---|---|
buffer |
Uint8Array | |
offset |
number |
- Source:
Returns:
- Type
- {value: bigint, bytesRead: number}
snapStartTime_(startTime) → {number}
Snaps `startTime` onto the previous reference's end time when it is close
enough to be publish-clock noise rather than a real discontinuity.
A LOC reference is assembled from two different clocks: `startTime` comes
from the publisher's Timestamp, `duration` is a constant derived from the
catalog. The two cannot agree exactly, so consecutive references overlap
or leave a hole — even a publisher whose timestamps are exact leaves
sub-microsecond gaps, because a frame duration such as 1024/48000 s is
not representable in whole timestamp units.
That is not cosmetic, and the size of the gap barely matters.
StreamingEngine asks for the segment covering the previous reference's
endTime; when that instant falls in a hole of ANY width,
SegmentIndex.find() returns null and update_ takes its "segment could not
be found ... just try again" path, backing off half a segment and
retrying instead of appending.
Beyond the tolerance the publisher's clock stays authoritative, so a real
discontinuity still resyncs the timeline.
The trade-off is explicit: within the tolerance a genuinely lost frame is
closed up rather than preserved as a gap, which advances this track
against the others by that frame's duration. For live playback that is
the right trade — a lost frame is inaudible or invisible, a hole stalls
outright, and the drift is bounded because any real discontinuity
re-anchors on the publisher's clock.
Parameters:
| Name | Type | Description |
|---|---|---|
startTime |
number |
- Source:
Returns:
- Type
- number
timestampToSeconds_(timestamp, timescale) → {number}
Converts a raw LOC Timestamp value to presentation seconds.
Per LOC §2.3.1.1–§2.3.1.2:
- When `timescale` is present: `seconds = timestamp / timescale`
- When `timescale` is absent: the timestamp is wall-clock µs since the
Unix epoch, so the implicit timescale is 1 000 000.
Parameters:
| Name | Type | Description |
|---|---|---|
timestamp |
bigint | Raw vi64 timestamp value. |
timescale |
bigint | undefined | Raw vi64 timescale, or `undefined` if the Timescale property was absent. |
- Source:
Returns:
- Type
- number