SubtitlesOctopus Documentation

repository·master·Indexed 19 days ago

https://github.com/libass/javascriptsubtitlesoctopus

A high-performance subtitle renderer using WebAssembly (via libass) to display .ass (SSA/ASS) subtitles in HTML5 video players or on a canvas. It provides professional-grade rendering in the browser with support for multiple rendering modes (wasm-blend, js-blend, and lossy), dynamic track management, and custom font loading. Version 4.1.0.

Tokens
2.8K
Snippets
7
Records
12
Agent score
17%

What's inside SubtitlesOctopus

  1. Understand SubtitlesOctopus rendering modes

    master

    SubtitlesOctopus supports three rendering modes via the renderMode option:

    1. WASM Blending (wasm-blend): The default mode. It blends bitmaps of different events within WebAssembly. This is faster when many or complex subtitles are displayed simultaneously. Falls back to asm.js if WebAssembly is unavailable.
    2. JS Blending (js-blend): Performs bitmap processing outside of WebAssembly using JavaScript.
    3. Lossy Render Mode (lossy): EXPERIMENTAL. Uses createImageBitmap to render bitmaps in the Worker using Promises. This prevents the main thread from hanging/freezing during heavy subtitle rendering, though it may cause bitmap loss or skipped frames on low-end devices. Warning: Not stable and not supported in all browsers.
  2. Build SubtitlesOctopus

    master

    Prerequisites

    You must install the following dependencies:

    • git, make, python3, cmake, pkgconfig, patch, libtool, autotools (autoconf, automake, autopoint), gettext, licensecheck.
    • emscripten (configured in your environment).
    • ragel (required by Harfbuzz).
    • itstool (required by Fontconfig).
    • python3-ply (required by WebIDL).
    • gperf (required by Fontconfig).
    1. Install Docker.
    2. Run ./run-docker-build.sh.
    3. Artifacts will be located in /dist/js.

    Build via Buildah

    1. Install Buildah and a backend like crun or runc.
    2. Run ./run-buildah-build.sh.
    3. Artifacts will be located in /dist/js.

    Build Manually (No Containers)

    1. Install all dependency packages listed in Prerequisites.
    2. Run make.
      • Note for macOS users: If using libtool from Homebrew, use: LIBTOOLIZE=glibtoolize make.
    3. Artifacts will be located in /dist/js.
  3. Use SubtitlesOctopus with a Canvas only

    master

    You can use SubtitlesOctopus without a video element by providing a canvas element in the options. When using this mode, you must manually manage the subtitle timing using instance.setCurrentTime(time).

    var options = {
        canvas: document.getElementById('canvas'), // canvas element
        subUrl: '/test/test.ass',
        fonts: ['/test/font-1.ttf', '/test/font-2.ttf'],
        workerUrl: '/libassjs-worker.js'
    };
    var instance = new SubtitlesOctopus(options);
    // Render subtitles at 00:15 on your canvas
    instance.setCurrentTime(15);
  4. Basic Usage: Integrate Subtitles with HTML5 Video

    master

    To display .ass subtitles on an HTML5 video element, instantiate SubtitlesOctopus with an options object containing the video element and the subUrl (or subContent). SubtitlesOctopus will automatically connect to the video and begin rendering.

    var options = {
        video: document.getElementById('video'), // HTML5 video element
        subUrl: '/test/test.ass', // Link to subtitles
        fonts: ['/test/font-1.ttf', '/test/font-2.ttf'], // Links to fonts
        workerUrl: '/libassjs-worker.js', // Link to WebAssembly-based file
        legacyWorkerUrl: '/libassjs-worker-legacy.js' // Link to non-WebAssembly worker
    };
    var instance = new SubtitlesOctopus(options);
  5. Configure SubtitlesOctopus options

    master

    The SubtitlesOctopus constructor accepts an options object. Key configuration parameters include:

    OptionTypeDescription
    videoHTMLVideoElementThe video element to attach listeners to. (Optional)
    canvasHTMLCanvasElementThe canvas to render to. If omitted, a new canvas is created as a sibling to the video. (Optional)
    subUrlstringURL of the subtitle file. (Required if subContent is not provided)
    subContentstringRaw content of the subtitle file. (Required if subUrl is not provided)
    workerUrlstringURL of the worker file. Default: libassjs-worker.js
    fontsstring[]Array of links to font files. (Optional)
    availableFontsObjectMap of font names (lowercase) to URLs: {"arial": "/font1.ttf"}. (Optional)
    fallbackFontstringURL to override the default fallback font (Liberation Sans). (Optional)
    lazyFileLoadingbooleanIf true, loads files via FS.createLazyFile(). Requires Access-Control-Expose-Headers for Accept-Ranges, Content-Length, and Content-Encoding.
    timeOffsetnumberTime offset from the video. Default: 0
    onReadyFunctionCallback when SubtitlesOctopus is ready.
    onErrorFunctionCallback on critical errors.
    debugbooleanPrint performance info to console. Default: false
    renderModestringjs-blend, wasm-blend (default), or lossy (experimental).
    targetFpsnumberTarget FPS. Default: 24
    libassMemoryLimitnumberlibass bitmap cache limit in MiB. Default: 0 (no limit)
    libassGlyphLimitnumberlibass glyph cache limit in MiB. Default: 0 (no limit)
    prescaleFactornumberScale factor for canvas (e.g., < 1.0 for performance, > 1.0 for quality). Default: 1.0
    prescaleHeightLimitnumberHeight threshold for prescaling. Default: 1080
    maxRenderHeightnumberMax height before browser upscaling. Default: 0 (no limit)
    dropAllAnimationsbooleanIf true, attempts to discard all animated tags. Use only as a last resort for low-end hardware. Default: false
  6. Dynamically change or remove subtitles

    master

    Use the following methods to update the subtitle track during playback without re-instantiating the object:

    • setTrackByUrl(url): Sets the subtitle track using a URL (equivalent to the subUrl option).
    • setTrack(content): Sets the subtitle track using raw string content (equivalent to the subContent option).
    • freeTrack(): Removes the current subtitles.
    var instance = new SubtitlesOctopus(options);
    
    // Change the subtitles to a new URL
    instance.setTrackByUrl('/test/railgun_op.ass');
  7. Clean up SubtitlesOctopus instance

    master

    To prevent memory leaks and correctly dispose of the Web Worker and other resources, call instance.dispose() when you are finished with the subtitle rendering.

    var instance = new SubtitlesOctopus(options);
    
    // After you've finished using it...
    instance.dispose();
  8. Manage subtitle events and styles

    master

    The library allows direct manipulation of the subtitle track's contents:

    Events

    • getEventCount(): Returns the total number of events in the current track.
    • allocEvent(): Allocates a new event in the track.
    • removeEvent(eid): Removes an event by its ID.
    • removeAllEvents(): Flushes all events from the track.

    Styles

    • getStyleCount(): Returns the number of styles defined in the track.
    • getStyleByName(name): Returns the index of a style by its name.
    • allocStyle(): Allocates a new style.
    • removeStyle(sid): Removes a style by its ID.
  9. Configure subtitle rendering options

    master

    The SubtitleOctopus class provides several methods to fine-tune the rendering behavior:

    • Set Margins: Use setMargin(top, bottom, left, right) to adjust the subtitle positioning relative to the canvas edges.
    • Memory Limits: Use setMemoryLimits(glyph_limit, bitmap_cache_limit) to constrain the memory used by libass for glyphs and bitmap caching (values in MiB).
    • Drop Animations: Use setDropAnimations(value) (where value is truthy) to strip out animated ASS tags (like karaoke, fades, or transforms). This is useful for performance or when animations are not supported by your rendering pipeline. Once dropped, animations cannot be restored without reloading the track.
  10. Create and manage subtitle tracks

    master

    You can load subtitle data into the renderer using either a file path or a memory buffer.

    • From a file: Use createTrack(subfile) where subfile is the path to an .ass or .ssa file.
    • From memory: Use createTrackMem(buf, bufsize) to load subtitles from a raw buffer.

    To prevent memory leaks, always call removeTrack() when you are finished with a track or before loading a new one. If you need to completely reset the library state, use reloadLibrary() or quitLibrary().

    // Loading from a file
    octopus.createTrack("subtitles.ass");
    
    // Loading from a memory buffer
    octopus.createTrackMem(buffer, bufferSize);
    
    // Cleanup
    octopus.removeTrack();
  11. Initialize the SubtitleOctopus library

    master

    To start using the library, call initLibrary with the video frame dimensions and an optional default font name. This initializes the underlying libass library and renderer. You should also call resizeCanvas to set the rendering area dimensions.

    Note that initLibrary also triggers reloadFonts which uses a specific configuration path (/assets/fonts.conf) to provide font support.

    // Example initialization sequence
    octopus.initLibrary(frame_w, frame_h, "MyFontName");
    octopus.resizeCanvas(frame_w, frame_h);
  12. Render subtitle frames

    master

    Rendering can be performed in two ways depending on your integration needs:

    1. Standard Rendering (Canvas-ready)

    Use renderImage(time, changed) to get an ASS_Image pointer. The time parameter is in seconds. The changed output parameter (passed as a pointer to an integer) will be updated to indicate if the frame has changed since the last render.

    2. Blended Rendering (Direct Image Access)

    If you need a single composited image (e.g., for direct pixel manipulation or custom canvas drawing), use renderBlend(tm, force). This returns a RenderBlendResult object containing:

    • image: A pointer to the raw unsigned char pixel data.
    • dest_x, dest_y, dest_width, dest_height: The bounding box of the rendered subtitles.
    • changed: Whether the frame changed.
    • blend_time: The time taken to perform the blend operation.

    Use force = 1 if you want to force a re-render even if nothing has changed.

    // Standard render
    let changed = 0;
    let img = octopus.renderImage(currentTime, changed);
    
    // Blended render for direct pixel access
    let result = octopus.renderBlend(currentTime, 0);
    if (result.image) {
      // Use result.image, result.dest_x, etc.
    }