What happens
On the loopback live path (effective video route = loopback), a channel whose upstream master advertises a SUBTITLES group plays without any subtitle track. subtitleTracks stays empty, so nothing is selectable and nothing renders.
Measurement
MDR Sachsen (public HLS, https://mdrtvsnhls.akamaized.net/hls/live/2016928/mdrtvsn/index.m3u8) advertises a WebVTT rendition on every variant:
#EXT-X-STREAM-INF:...,AUDIO="program_audio",SUBTITLES="subs"
#EXT-X-MEDIA:TYPE=SUBTITLES,NAME="Untertitel",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="de",GROUP-ID="subs",URI=".../master-subs-1200.m3u8"
What the engine does with it:
[HLSIngest] master playlist: picked variant bandwidth=8500800
[HLSIngest] demuxed audio rendition (group "program_audio", default=true): starting companion reader on ...
[Demuxer] Opened: 1 streams
[Demuxer] stream[0] type=video codec=h264 1920x1080
[AetherEngine] native subtitles: prepare=false eager=false textTracks=0 bitmapOCR=0 enable=false
The variant's SUBTITLES group is never looked at. HLSLiveIngestReader.swift (551 lines) contains no occurrence of "subtitle", and HLSMasterPlaylist models variants, demuxedAudioGroupIDs and audioRenditions only. The served local master consequently declares CLOSED-CAPTIONS=NONE and carries no rendition.
The audio side of exactly this shape is already handled: a demuxed audio rendition gets a companion reader. Subtitles have no equivalent.
What the segments look like
Plain .vtt, 2 s each:
WEBVTT
X-TIMESTAMP-MAP=LOCAL:159:04:22.306,MPEGTS:183000
164:04:24.000 --> 164:04:25.440
Alles klar?
Two properties that shape any implementation:
- Cue times are not stream times. They sit on a long-running LOCAL clock (164 h here) and only
X-TIMESTAMP-MAP anchors them to the program's 90 kHz MPEGTS clock, which is the same clock the segment producer already derives playlistShiftSeconds from. The mapping is therefore cue time -> MPEGTS time -> player clock, wrap-safe on 33 bits.
- Cues repeat across segment boundaries. A cue spanning several segments is repeated in each, clipped to the boundary (segments 133/134/135 above each carry the same line with adjacent ranges). A naive collector publishes the same line three times.
Proposed shape
HLSPlaylistParser learns EXT-X-MEDIA:TYPE=SUBTITLES and the variant's SUBTITLES group id.
- The ingest hands the picked variant's renditions up as metadata, without fetching.
- They surface as
TrackInfo entries in subtitleTracks under their own synthetic id range, like the A53 closed-caption track and the external tracks already do.
selectSubtitleTrack on such an id starts a fetch loop over the rendition playlist: first what the playlist currently lists (so a jump back inside the DVR window is covered), then following the live edge. Deselect stops it.
- Segments are parsed into
SubtitleCues, mapped onto the player clock as above, with repeats merged rather than appended.
Out of scope for a first cut: WebVTT in fMP4, the bypass path (it sees renditions natively), and Teletext (a TS concern).
What happens
On the loopback live path (
effective video route = loopback), a channel whose upstream master advertises aSUBTITLESgroup plays without any subtitle track.subtitleTracksstays empty, so nothing is selectable and nothing renders.Measurement
MDR Sachsen (public HLS,
https://mdrtvsnhls.akamaized.net/hls/live/2016928/mdrtvsn/index.m3u8) advertises a WebVTT rendition on every variant:What the engine does with it:
The variant's
SUBTITLESgroup is never looked at.HLSLiveIngestReader.swift(551 lines) contains no occurrence of "subtitle", andHLSMasterPlaylistmodelsvariants,demuxedAudioGroupIDsandaudioRenditionsonly. The served local master consequently declaresCLOSED-CAPTIONS=NONEand carries no rendition.The audio side of exactly this shape is already handled: a demuxed audio rendition gets a companion reader. Subtitles have no equivalent.
What the segments look like
Plain
.vtt, 2 s each:Two properties that shape any implementation:
X-TIMESTAMP-MAPanchors them to the program's 90 kHz MPEGTS clock, which is the same clock the segment producer already derivesplaylistShiftSecondsfrom. The mapping is therefore cue time -> MPEGTS time -> player clock, wrap-safe on 33 bits.Proposed shape
HLSPlaylistParserlearnsEXT-X-MEDIA:TYPE=SUBTITLESand the variant'sSUBTITLESgroup id.TrackInfoentries insubtitleTracksunder their own synthetic id range, like the A53 closed-caption track and the external tracks already do.selectSubtitleTrackon such an id starts a fetch loop over the rendition playlist: first what the playlist currently lists (so a jump back inside the DVR window is covered), then following the live edge. Deselect stops it.SubtitleCues, mapped onto the player clock as above, with repeats merged rather than appended.Out of scope for a first cut: WebVTT in fMP4, the bypass path (it sees renditions natively), and Teletext (a TS concern).