Skip to content

5.6.0

Choose a tag to compare

@superuser404notfound superuser404notfound released this 17 Jul 16:56

Feature release: A53/SEI-embedded CEA-608 closed captions (#131).

Added

  • A53/SEI-embedded CEA-608 captions are now extracted and rendered (#131). US broadcast and cable-sourced MPEG-TS feeds carry closed captions as ATSC A/53 cc_data inside the video picture (user_data_registered_itu_t_t35 SEI for H.264/HEVC, picture user data for MPEG-2), not as a demuxable caption stream. FFmpeg's mpegts demuxer never synthesizes a caption stream for them, so the #77 closed-caption tap never armed and a whole class of captioned live channels played with no subtitle option.

    Two extraction paths, both feeding the existing #77 line-21 decoder and cue machinery:

    • Native remux path (H.264/HEVC): the segment producer scans video packets for GA94 T.35 SEI at the same per-packet spot as the HDR10+ scan (a cheap GA94 prefilter keeps uncaptioned packets nearly free), in both Annex B and length-prefixed NAL framing, so TS live channels and MKV/MP4 recordings are covered. SEI arrives in decode order and the 608 decoder is order-sensitive, so caption groups are reordered to presentation order via a packet-DTS watermark before decode (B-frame content would otherwise garble roll-up text).
    • Software-decode path (MPEG-2 and friends): captions come from AV_FRAME_DATA_A53_CC decoded-frame side data, already in presentation order.

    Since no caption AVStream exists, a synthetic eia_608 track (id 99608) surfaces in subtitleTracks lazily on the first real (non-padding) caption pair; encoders that continuously send padding-only cc_data never produce a dead menu entry. Hosts need no changes: the track rides the existing subtitleTracks + subtitleCues contract, and selection, mirroring, and seek resets work through the same paths as a demuxable CC track. Extraction survives seeks, producer restarts, live reopens, and no-reprobe reloads (audio switches).

    Scope notes: host-overlay rendering only for now (no native WebVTT rendition for the synthetic track, so no PiP/AirPlay captions on this path yet), and CEA-708 stays out of scope; A53 cc_data carries both 608 and 708, so the extraction layer is shared and 708 decode can layer on later, matching #77's field-1/CC1 first cut.

Thanks to dlev02 for the precise engine audit: the exact arming gap, the two extraction points in preference order, and the packet-PTS keying caveat were all spot on and scoped this implementation.