Subtitles

PRESTOplay for web has rich support for text tracks. Multiple parsers cover a wide range of subtitle and caption formats, and a choice of displayers lets you match the styling and rendering behavior your application needs.

To render cues, install a text displayer. The HTML text displayer (HtmlTextDisplayer) is recommended: it handles complex layouts and styling, and keeps rendering consistent across platforms.

import "@castlabs/prestoplay/cl.htmlcue";

Without the HTML Cue module, the player falls back to the native text displayer based on the capabilities of the browser, which may have varying levels of support for more complex styling and layouts.

Text parsers

TTML, SMPTE-TT, and IMSC1

The TTML module adds support for Timed Text Markup Language (TTML) and related profiles, including SMPTE Timed Text (SMPTE-TT) and Internet Media Subtitles and Captions 1 (IMSC1) text or image subtitles. Import the module before creating the player:

import "@castlabs/prestoplay/cl.ttml";

Supported features

Supported attributes:

  • tt:begin

  • tt:end

  • tt:region

  • tt:style

  • ttp:cellResolution

  • ttp:frameRate

  • ttp:subFrameRate

  • ttp:frameRateMultiplier

  • ttp:tickRate

  • tts:backgroundColor

  • tts:color

  • tts:direction

  • tts:displayAlign

  • tts:origin

  • tts:extent

  • tts:fontFamily

  • tts:fontSize

  • tts:fontWeight

  • tts:fontStyle

  • tts:lineHeight

  • tts:opacity

  • tts:padding

  • tts:textAlign

  • tts:textOutline

  • tts:textShadow

  • tts:unicodeBidi

  • tts:writingMode

  • tts:zIndex

  • smpte:backgroundImage

SMPTE-TT and IMSC1 support covers bitmap images, such as PNG images encoded in Base64. Position the bitmap images by enclosing them in a region.

Support for legacy manifests

A well-designed TTML manifest defines a root container and vertical cell definitions, which let the layout adjust to different sizes, such as fullscreen mode. Some legacy manifests use pixels instead to define region or font size, for example:

<?xml version="1.0" encoding="utf-8"?>
<tt
  xmlns="http://www.w3.org/ns/ttml"
  xmlns:tts="http://www.w3.org/ns/ttml#styling" xml:lang="en">
  <head>
    <styling>
      <style xml:id="s1"
        tts:fontSize="16px"
        tts:textAlign="center"/>
    </styling>
    <layout>
      <region xml:id="r1"
        style="s1"
        tts:displayAlign="center"
        tts:extent="512px 48px"
        tts:origin="48px 512px"/>
    </layout>
  </head>
  <body>
    <div xml:lang="en">
      <p begin="00:00:00.000000" end="00:00:10.000000" region="r1">
        Lorem ipsum
      </p>
    </div>
  </body>
</tt>

For manifests like this, provide hints to the parser:

const playerConfig = {
  // ...
  ttml: {
    containerWidth: 640,
    containerHeight: 480,
    fontSizeRadix: 16,
  },
};

containerWidth and containerHeight convert a region size into percentages. In this example, the r1 region’s width becomes 80% and its height becomes 10%; its x-position becomes 80% and its y-position becomes 10%.

fontSizeRadix converts a font size into em. In this example, the cue font size becomes 1em. The HTML displayer then adjusts the parent font size based on the video size. See TextStyleConfiguration for details. Cues sized this way are responsive.

SMPTE-TT over HLS

HTTP Live Streaming (HLS) doesn’t support SMPTE-TT natively. Harmonic proposed an extension to the HLS specification to work around this, described in SMPTE-TT Embedded in ID3 for HTTP Live Streaming. The HlsSmpte plugin implements this extension. It extends the TTML parser and works exclusively on Safari with native HLS playback — it has no effect on the MSE-based player.

To use it:

  1. Import the TTML module:

    import "@castlabs/prestoplay/cl.ttml";
    
  2. Import the HLS-SMPTE module:

    import "@castlabs/prestoplay/cl.hlssmpte";
    
  3. Enable the config.hlssmpte.patchMetadataTrack flag if recent Safari versions fail to create the metadata track on load. As a workaround, the plugin briefly switches to another audio track and back to force Safari to create it. This flag is opt-in rather than default because the workaround requires at least two audio tracks and causes an audible track switch.

  4. Adjust config.hlssmpte.timestampOffset if cues are time-shifted.

  5. Provide placeholder text tracks using config.remoteTextTracks. Each placeholder must define kind, language, mimeType, and an optional label. Omit url — the HlsSmpte plugin injects cues from the metadata track. The language code must match the metadata track’s language codes, and the MIME type must be text/vtt.

In the example below, url is omitted from each placeholder because cues are delivered through the metadata track; the placeholders are still needed to expose the language options.

<link rel="stylesheet" href="./clpp.styles.css">
<video id="video" width="640" height="360" controls crossorigin="anonymous"></video>
<script type="module">
  import { clpp } from "@castlabs/prestoplay";
  import "@castlabs/prestoplay/cl.ttml";
  import "@castlabs/prestoplay/cl.htmlcue";
  import "@castlabs/prestoplay/cl.hlssmpte";

  const player = new clpp.Player('video', { });

  player.open({
    source: '...',
    hlssmpte: {
      patchMetadataTrack: true
    },
    htmlcue: {
      stretchSmpteImage: true
    },
    remoteTextTracks: [
      {
        kind: 'subtitles',
        language: 'ara',
        mimeType: 'text/vtt',
        label: 'Arabic'
      },
      {
        kind: 'subtitles',
        language: 'eng',
        mimeType: 'text/vtt',
        label: 'English'
      }
    ]
  });
</script>

WebVTT and SubRip

The VTT module adds support for WebVTT subtitles and SubRip (SRT) files. Files with the text/srt, application/x-subrip, or text/plain MIME type are converted to WebVTT internally. Import the module before creating the player:

import "@castlabs/prestoplay/cl.vtt";

Supported features

  • Inline text formatting

  • Voice spans

  • Ruby characters

  • Text direction

  • Text alignment

  • Positioning

  • Size

  • Regions

  • Embedded styles

SubRip (SRT) subtitles

SRT files are usually side-loaded next to the stream. Import the VTT module and add the file as a remote text track with the text/srt MIME type:

import { clpp } from "@castlabs/prestoplay";
import "@castlabs/prestoplay/cl.mse";
import "@castlabs/prestoplay/cl.dash";
import "@castlabs/prestoplay/cl.vtt";

const player = new clpp.Player("video", {});

await player.open({
  source: "https://testcontent.castlabs.com/QA/tos_1080_clear/manifest.mpd",
  remoteTextTracks: [
    {
      url: "https://demo.cf.castlabs.com/media/TOS/TOS-ml.srt",
      mimeType: "text/srt",
      kind: "subtitles",
      language: "mal",
      label: "Malayalam",
    },
  ],
  preferredTextLanguage: "mal",
});

preferredTextLanguage selects the track when playback starts. To switch tracks later, use the TrackManager (see Tracks and languages).

The SRT conversion has these requirements and limitations:

  • The file must be UTF-8 encoded.

  • Each cue must start with a numeric identifier, followed by a timestamp line in the 00:00:23,000 --> 00:00:24,500 format and the cue text. The player drops cues without an identifier.

  • The player removes HTML formatting tags such as <i> and <b> from the cue text, so cues render as plain text.

The SRT subtitles sample plays a stream with side-loaded SRT subtitles.

Side-loaded remote text tracks

Use config.remoteTextTracks to add side-loaded (“sidecar”) subtitle files that aren’t present in the manifest. Each entry follows RemoteTextTrack, and mimeType must match a format whose module is imported, for example text/vtt or text/srt with the VTT module.

Note

With MSE playback, side-loaded text tracks are only supported for video on demand (VOD). Adding them to a live stream fails with the CANNOT_ADD_EXTERNAL_TEXT_TO_LIVE_STREAM error.

<link rel="stylesheet" href="./clpp.styles.css">
<video id="video" width="640" height="360" controls crossorigin="anonymous"></video>
<script type="module">
  import { clpp } from "@castlabs/prestoplay";
  import "@castlabs/prestoplay/cl.mse";
  import "@castlabs/prestoplay/cl.vtt";
  import "@castlabs/prestoplay/cl.htmlcue";

  const player = new clpp.Player('video', { });

  player.open({
    source: '...',
    remoteTextTracks: [
      {
        url: 'https://example.com/subtitles/en.vtt',
        kind: 'subtitles',
        language: 'eng',
        mimeType: 'text/vtt',
        label: 'English'
      },
      {
        url: 'https://example.com/subtitles/es.vtt',
        kind: 'subtitles',
        language: 'spa',
        mimeType: 'text/vtt',
        label: 'Spanish'
      }
    ]
  });
</script>

Text displayers

HTML displayer

The HTML displayer renders text cues using the HTML surface, with rich styling capabilities and consistent results across browsers. It has strong support for rendering parsed TTML, SMPTE-TT, IMSC1, WebVTT, and SRT cues.

To use it:

  1. Add the PRESTOplay default styles to your page:

    <link rel="stylesheet" href="clpp.styles.css">
    
  2. Import the HTML Cue module:

    import "@castlabs/prestoplay/cl.htmlcue";
    

The HTML text displayer is used by default once @castlabs/prestoplay/cl.htmlcue is imported. Set enableHtmlCue to false in the player configuration to change this.

<link rel="stylesheet" href="clpp.styles.css">
<video id="video" width="640" height="360" crossorigin="anonymous" controls></video>
<script type="module">
  import { clpp } from "@castlabs/prestoplay";
  import "@castlabs/prestoplay/cl.mse";
  import "@castlabs/prestoplay/cl.dash";
  import "@castlabs/prestoplay/cl.ttml";
  import "@castlabs/prestoplay/cl.vtt";
  import "@castlabs/prestoplay/cl.htmlcue";

  const player = new clpp.Player('video', {});

  player.open({
    source: '//demo.cf.castlabs.com/media/prestodash/Manifest.mpd'
  });
</script>

Styling

Styles apply in this order, from lowest to highest priority:

  1. Player styles

  2. Website styles

  3. Embedded styles

  4. Text style options

Player styles come from clpp.styles.css and are required — they provide default styles for text cues and regions.

Override the default styles with your own stylesheet by extending the .clpp-text-container, .clpp-text-cue, or .clpp-text-cue-region classes. Website styles take priority over default styles.

A text track can contain embedded styles, which take priority over website and default styles.

Text style options, set in the player configuration or through player.getTextDisplayer(), take priority over embedded, website, and default styles. Use these to let your users customize the look and feel of subtitles. The subtitle text styling sample sets the font, color, background, and edge style through text style options.

Font size

The HTML displayer adjusts a base font size to the container size for responsive design. By default, the font size is 7% of the container height. Adjust it in the player configuration:

const playerConfig = {
  // ...
  textStyle: {
    fontSizePercent: 0.03
  }
};

Native text displayer

Styling

Style text cues using text style options in the player configuration:

const playerConfig = {
  // ...
  textStyle: {
    backgroundColor: 'rgba(207, 0, 15, 0.75)',
    fontFamily: 'monospace',
    fontColor: 'blue',
    fontSize: '1.2em',
    edgeColor: 'green',
    edgeType: clpp.text.EdgeType.DROP_SHADOW
  }
}

Or style cues on the fly through a text displayer instance:

player.getTextDisplayer().setFontColor('blue');

Native text-cue styling has some constraints:

  • It requires Edge 19 or later.

  • Background color and opacity are configurable on browsers other than Safari (macOS/iPadOS/iOS) and Chrome (macOS).

Use the HTML displayer to style text cues consistently across all supported browsers.

CEA-608 captions

PRESTOplay for web supports CEA-608 captions, the legacy closed-captioning standard embedded directly in the video signal. The caption parser requires mux.js (version 5.7.0 or later) available in the global scope:

import * as muxjs from "mux.js";
window.muxjs = muxjs;

See Components for the full mux.js installation steps.

Next topic: Thumbnails
Previous topic: Tracks and languages