An HTML5 video player that supports HLS and DASH with a common API and skin.
—
Video.js provides comprehensive track management for video, audio, and text tracks with full accessibility support and standardized APIs following HTML5 specifications.
Manage subtitles, captions, descriptions, chapters, and metadata tracks.
/**
* Text track for subtitles, captions, descriptions, chapters, and metadata
*/
class TextTrack {
/**
* Text track kind
*/
readonly kind: TextTrackKind;
/**
* Text track label
*/
readonly label: string;
/**
* Text track language code
*/
readonly language: string;
/**
* Text track display mode
*/
mode: TextTrackMode;
/**
* List of track cues
*/
readonly cues: TextTrackCueList;
/**
* Active cues at current playback time
*/
readonly activeCues: TextTrackCueList;
/**
* Add cue to track
* @param cue - Cue to add
*/
addCue(cue: VTTCue): void;
/**
* Remove cue from track
* @param cue - Cue to remove
*/
removeCue(cue: VTTCue): void;
}
/**
* List of text tracks
*/
class TextTrackList {
/**
* Number of tracks
*/
readonly length: number;
/**
* Get track by index
* @param index - Track index
* @returns Text track
*/
[index: number]: TextTrack;
/**
* Get track by ID
* @param id - Track ID
* @returns Text track or null
*/
getTrackById(id: string): TextTrack | null;
}Usage Examples:
// Access text tracks
const textTracks = player.textTracks();
console.log('Number of text tracks:', textTracks.length);
// Find subtitle track
for (let i = 0; i < textTracks.length; i++) {
const track = textTracks[i];
if (track.kind === 'subtitles' && track.language === 'en') {
track.mode = 'showing'; // Show English subtitles
break;
}
}
// Add programmatic text track
const track = player.addTextTrack('subtitles', 'English', 'en');
track.addCue(new VTTCue(0, 5, 'Hello World'));
track.addCue(new VTTCue(5, 10, 'This is a subtitle'));
track.mode = 'showing';
// Listen for cue changes
track.addEventListener('cuechange', () => {
const activeCues = track.activeCues;
if (activeCues.length > 0) {
console.log('Active cue:', activeCues[0].text);
}
});Manage multiple audio tracks for language selection and audio descriptions.
/**
* Audio track representation
*/
class AudioTrack {
/**
* Unique track identifier
*/
readonly id: string;
/**
* Track kind (main, alternative, commentary, etc.)
*/
readonly kind: string;
/**
* Human-readable track label
*/
readonly label: string;
/**
* Track language code
*/
readonly language: string;
/**
* Whether track is enabled (only one can be enabled at a time)
*/
enabled: boolean;
}
/**
* List of audio tracks
*/
class AudioTrackList {
/**
* Number of tracks
*/
readonly length: number;
/**
* Get track by index
* @param index - Track index
* @returns Audio track
*/
[index: number]: AudioTrack;
/**
* Get track by ID
* @param id - Track ID
* @returns Audio track or null
*/
getTrackById(id: string): AudioTrack | null;
}Usage Examples:
// Access audio tracks
const audioTracks = player.audioTracks();
console.log('Available audio tracks:', audioTracks.length);
// Switch to Spanish audio
for (let i = 0; i < audioTracks.length; i++) {
const track = audioTracks[i];
if (track.language === 'es') {
track.enabled = true; // Enable Spanish audio
} else {
track.enabled = false; // Disable other audio tracks
}
}
// Listen for audio track changes
audioTracks.addEventListener('change', () => {
const enabledTrack = Array.from(audioTracks).find(track => track.enabled);
console.log('Audio track changed to:', enabledTrack?.label);
});Manage multiple video tracks for different camera angles or video qualities.
/**
* Video track representation
*/
class VideoTrack {
/**
* Unique track identifier
*/
readonly id: string;
/**
* Track kind (main, alternative, sign, etc.)
*/
readonly kind: string;
/**
* Human-readable track label
*/
readonly label: string;
/**
* Track language code
*/
readonly language: string;
/**
* Whether track is selected (only one can be selected at a time)
*/
selected: boolean;
}
/**
* List of video tracks
*/
class VideoTrackList {
/**
* Number of tracks
*/
readonly length: number;
/**
* Get track by index
* @param index - Track index
* @returns Video track
*/
[index: number]: VideoTrack;
/**
* Get track by ID
* @param id - Track ID
* @returns Video track or null
*/
getTrackById(id: string): VideoTrack | null;
/**
* Get currently selected track
* @returns Selected video track or null
*/
getSelectedTrack(): VideoTrack | null;
}Usage Examples:
// Access video tracks
const videoTracks = player.videoTracks();
// Switch to alternate camera angle
for (let i = 0; i < videoTracks.length; i++) {
const track = videoTracks[i];
if (track.kind === 'alternative' && track.label.includes('Angle 2')) {
track.selected = true; // Select alternate angle
} else {
track.selected = false; // Deselect other tracks
}
}
// Get currently selected track
const selectedTrack = videoTracks.getSelectedTrack();
console.log('Current video track:', selectedTrack?.label);Player-level methods for managing tracks.
/**
* Get text tracks list
* @returns Text tracks list
*/
textTracks(): TextTrackList;
/**
* Get audio tracks list
* @returns Audio tracks list
*/
audioTracks(): AudioTrackList;
/**
* Get video tracks list
* @returns Video tracks list
*/
videoTracks(): VideoTrackList;
/**
* Add text track programmatically
* @param kind - Track kind
* @param label - Track label
* @param language - Language code
* @returns Created text track
*/
addTextTrack(kind: TextTrackKind, label?: string, language?: string): TextTrack;
/**
* Add remote text track from URL
* @param options - Track options including src URL
* @param manualCleanup - Whether to handle cleanup manually
* @returns HTML track element
*/
addRemoteTextTrack(options: RemoteTextTrackOptions, manualCleanup?: boolean): HTMLTrackElement;
/**
* Remove remote text track
* @param track - Track or track element to remove
*/
removeRemoteTextTrack(track: TextTrack | HTMLTrackElement): void;Manage individual cues within text tracks.
/**
* VTT cue representing timed text
*/
class VTTCue {
/**
* Create VTT cue
* @param startTime - Start time in seconds
* @param endTime - End time in seconds
* @param text - Cue text content
*/
constructor(startTime: number, endTime: number, text: string);
/**
* Cue start time in seconds
*/
startTime: number;
/**
* Cue end time in seconds
*/
endTime: number;
/**
* Cue text content
*/
text: string;
/**
* Cue identifier
*/
id: string;
/**
* Cue positioning and styling
*/
position: number;
line: number;
size: number;
align: string;
vertical: string;
}
/**
* List of cues
*/
class TextTrackCueList {
/**
* Number of cues
*/
readonly length: number;
/**
* Get cue by index
* @param index - Cue index
* @returns VTT cue
*/
[index: number]: VTTCue;
/**
* Get cue by ID
* @param id - Cue ID
* @returns VTT cue or null
*/
getCueById(id: string): VTTCue | null;
}Usage Examples:
// Create cues with styling
const cue1 = new VTTCue(0, 3, 'Welcome to our video');
cue1.line = 1;
cue1.position = 50;
cue1.align = 'center';
const cue2 = new VTTCue(3, 6, '<c.highlight>Important information</c>');
cue2.line = -1; // Bottom line
// Add to track
const track = player.addTextTrack('captions', 'English Captions', 'en');
track.addCue(cue1);
track.addCue(cue2);
track.mode = 'showing';Load text tracks from external files.
// Add remote text track
const trackElement = player.addRemoteTextTrack({
kind: 'subtitles',
src: 'path/to/subtitles.vtt',
srclang: 'en',
label: 'English Subtitles',
default: true
});
// Access the track
const track = trackElement.track;
track.addEventListener('load', () => {
console.log('Track loaded with', track.cues.length, 'cues');
});
// Remove when done
player.removeRemoteTextTrack(trackElement);Text tracks support various events for monitoring state changes.
// Track list events
textTracks.addEventListener('addtrack', (event) => {
console.log('Track added:', event.track);
});
textTracks.addEventListener('removetrack', (event) => {
console.log('Track removed:', event.track);
});
textTracks.addEventListener('change', () => {
console.log('Track modes changed');
});
// Individual track events
track.addEventListener('cuechange', () => {
const activeCues = track.activeCues;
for (let i = 0; i < activeCues.length; i++) {
console.log('Active cue:', activeCues[i].text);
}
});
track.addEventListener('load', () => {
console.log('Track cues loaded');
});
track.addEventListener('error', (event) => {
console.error('Track error:', event);
});Video.js provides built-in accessibility support for text tracks.
// Configure accessibility options
const player = videojs('my-video', {
tracks: [
{
kind: 'captions',
src: 'captions.vtt',
srclang: 'en',
label: 'English Captions',
default: true
},
{
kind: 'descriptions',
src: 'descriptions.vtt',
srclang: 'en',
label: 'Audio Descriptions'
}
]
});
// Enable keyboard navigation for tracks
player.ready(() => {
// Tracks menu is automatically accessible via keyboard
const trackMenuButton = player.getChild('ControlBar').getChild('SubtitlesButton');
if (trackMenuButton) {
trackMenuButton.focus(); // Can be focused with keyboard
}
});type TextTrackKind = 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
type TextTrackMode = 'disabled' | 'hidden' | 'showing';
interface RemoteTextTrackOptions {
kind: TextTrackKind;
src: string;
srclang?: string;
label?: string;
default?: boolean;
}
interface TextTrackOptions {
kind: TextTrackKind;
src?: string;
srclang?: string;
label?: string;
default?: boolean;
[key: string]: any;
}
interface TextTrack {
kind: TextTrackKind;
label: string;
language: string;
mode: TextTrackMode;
cues: TextTrackCueList;
activeCues: TextTrackCueList;
addCue(cue: VTTCue): void;
removeCue(cue: VTTCue): void;
}
interface AudioTrack {
id: string;
kind: string;
label: string;
language: string;
enabled: boolean;
}
interface VideoTrack {
id: string;
kind: string;
label: string;
language: string;
selected: boolean;
}
interface VTTCue {
startTime: number;
endTime: number;
text: string;
id: string;
position: number;
line: number;
size: number;
align: string;
vertical: string;
}Install with Tessl CLI
npx tessl i tessl/npm-video-js