More types of messages are now supported than before.
SubscribeError handling has been improved.
A new error has been added when no catalog.
Existing messages have been reviewed to eliminate inconsistencies in the
migration from draft-11 to draft-14 support.
Fix log levels
Allow evict the buffer more aggressive in MSF streams
Do not create a prefetch because for MSF it is not necessary because the
data is always in the segments.
Set MSF streams as low latency.
## Summary
- In MSF live streams, audio and video segments arrive at different
wall-clock times over WebTransport (audio frames are ~8KB vs ~125KB
video keyframes, so audio arrives first).
- The first stream type to deliver a segment calls `lockStartTime()` and
`setSegmentAvailabilityDuration(0.5)`, creating a tight availability
window. When the other type's segments arrive even slightly later,
`mergeAndEvict` immediately evicts them because they fall before
`getSegmentAvailabilityStart()`.
- This causes video to never render — StreamingEngine repeatedly logs
`(video:N) cannot find segment` because every video segment is evicted
on arrival.
### Fix
1. Added `firstSegmentStartTimes_` map to track the first segment start
time per content type.
2. Before the timeline is locked, pass `0` as the eviction start to
`mergeAndEvict` to prevent premature segment eviction.
3. Only call `lockStartTime()` once ALL expected stream types (audio AND
video) have received at least one segment.
4. Set `segmentAvailabilityDuration` wide enough to cover the gap
between the earliest and latest stream start times (`gap + duration`),
so the stream whose segments arrived first isn't continuously evicted.
Co-authored-by: Erik Herz <erik@vivoh.com>
## Summary
- When MSF catalog track objects don't include an explicit `namespace`
field, `subscribeToTrack` falls through to an empty string. This causes
SUBSCRIBE messages with an empty namespace that the relay cannot match
to any published broadcast.
- The MSF catalog spec does not require per-track namespace fields — the
namespace is established at the transport level via PUBLISH_NAMESPACE.
Shaka should fall back to the session namespace.
- Fix: fall back to `config.msf.namespaces`, then to
`publishNamespaces_` (from PUBLISH_NAMESPACE), before defaulting to
empty string.
---------
Co-authored-by: Erik Herz <erik@vivoh.com>
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
## Summary
MSF works in uncompiled mode but fails with error 4058
(`MSF_VOD_CONTENT_NOT_SUPPORTED`) in the compiled build.
## Cause
The `MSFTrack` and `MSFCatalog` typedefs are defined in regular source
code (`msf_utils.js`). Closure Compiler mangles their property names in
the compiled build (e.g., `track.isLive` → `track.sa`).
Since the catalog is received as external JSON via `JSON.parse()`, the
actual keys remain unchanged and all property accesses return
`undefined`.
Moving the typedefs to an externs file tells Closure Compiler to
preserve the property names.
## Changes
- Move `MSFTrack` and `MSFCatalog` typedefs to a new externs file
(`externs/msf_catalog.js`) with the `@externs` annotation
- Update type references in `msf_parser.js` to use the new extern types
- Remove the original typedefs from `msf_utils.js`
## Testing
Tested manually with a draft-14 publisher
([moqtail](https://github.com/moqtail/moqtail)) and relay
([moq-wasm](https://github.com/nttcom/moq-wasm)).
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
So basically, when a license request fails (eg. network Error, server
down whatever), apps can now retry from scratch by calling
`player.retryLicensing()`. This was tricky to implement because of EME
spec limitations: `generateRequest()` can only be called once per
session. So if it fails, it would be stuck.
So I close the old session and create a brand new one with the same
`initData`
> Will Video element throw an error during this process?
we were worried that closing the session would leave the video without
keys for a brief moment, potentially triggering errors. But in practice,
the transition is fast enough( I added a 0.1s delay for CDM clean up)
and the video element handles it gracefully
> Will new encrypted event fire? If not, will it limit this feature?
The encrypted event only fires when the browser first encounters
encrypted content. When we close and recreate a session, the content is
already loaded, so no new event
Solutions: In `CreateSession()` metadata store `initData` and
`initDataType` in the session metadata when the session is first
created. So when `retryLicensing()`is called, we just grab the stored
data and pass it to `generateRequest()` on the new session. No need to
wait for an `encrypted` event at all.
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
Co-authored-by: Wojciech Tyczyński <tykus160@gmail.com>