Skip to content

Live HLS ingest drops the master's SUBTITLES renditions, so live subtitles are unreachable #359

Description

@superuser404notfound

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:

  1. 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.
  2. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions