Media stream handling, text tracks, and audio/video element support for multimedia applications. Provides comprehensive media functionality for web applications.
Represents a stream of media content.
/**
* Represents a stream of media content
*/
class MediaStream extends EventTarget {
constructor(stream?: MediaStream | MediaStreamTrack[]);
/** Stream ID */
readonly id: string;
/** Stream active flag */
readonly active: boolean;
/** Get all tracks */
getTracks(): MediaStreamTrack[];
/** Get audio tracks */
getAudioTracks(): MediaStreamTrack[];
/** Get video tracks */
getVideoTracks(): MediaStreamTrack[];
/** Get track by ID */
getTrackById(id: string): MediaStreamTrack | null;
/** Add track */
addTrack(track: MediaStreamTrack): void;
/** Remove track */
removeTrack(track: MediaStreamTrack): void;
/** Clone stream */
clone(): MediaStream;
// Event handlers
onaddtrack: ((event: MediaStreamTrackEvent) => void) | null;
onremovetrack: ((event: MediaStreamTrackEvent) => void) | null;
}Represents a single media track.
/**
* Represents a single media track
*/
class MediaStreamTrack extends EventTarget {
/** Track ID */
readonly id: string;
/** Track kind */
readonly kind: MediaStreamTrackKind;
/** Track label */
readonly label: string;
/** Enabled state */
enabled: boolean;
/** Muted state */
readonly muted: boolean;
/** Ready state */
readonly readyState: MediaStreamTrackState;
/** Get capabilities */
getCapabilities(): MediaTrackCapabilities;
/** Get constraints */
getConstraints(): MediaTrackConstraints;
/** Get settings */
getSettings(): MediaTrackSettings;
/** Apply constraints */
applyConstraints(constraints?: MediaTrackConstraints): Promise<void>;
/** Clone track */
clone(): MediaStreamTrack;
/** Stop track */
stop(): void;
// Event handlers
onmute: ((event: Event) => void) | null;
onunmute: ((event: Event) => void) | null;
onended: ((event: Event) => void) | null;
}
type MediaStreamTrackKind = 'audio' | 'video';
type MediaStreamTrackState = 'live' | 'ended';Represents a text track for media elements.
/**
* Represents a text track for media elements
*/
class TextTrack extends EventTarget {
/** Track kind */
readonly kind: TextTrackKind;
/** Track label */
readonly label: string;
/** Track language */
readonly language: string;
/** Track ID */
readonly id: string;
/** Track mode */
mode: TextTrackMode;
/** Track cues */
readonly cues: TextTrackCueList | null;
/** Active cues */
readonly activeCues: TextTrackCueList | null;
/** Add cue */
addCue(cue: TextTrackCue): void;
/** Remove cue */
removeCue(cue: TextTrackCue): void;
// Event handlers
oncuechange: ((event: Event) => void) | null;
}
type TextTrackKind = 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata';
type TextTrackMode = 'disabled' | 'hidden' | 'showing';Base class for text track cues.
/**
* Base class for text track cues
*/
class TextTrackCue extends EventTarget {
constructor(startTime: number, endTime: number, text: string);
/** Cue start time */
startTime: number;
/** Cue end time */
endTime: number;
/** Pause on exit flag */
pauseOnExit: boolean;
/** Cue ID */
id: string;
/** Owner track */
readonly track: TextTrack | null;
// Event handlers
onenter: ((event: Event) => void) | null;
onexit: ((event: Event) => void) | null;
}WebVTT cue implementation.
/**
* WebVTT cue implementation
*/
class VTTCue extends TextTrackCue {
constructor(startTime: number, endTime: number, text: string);
/** Cue text */
text: string;
/** Cue region */
region: VTTRegion | null;
/** Vertical writing direction */
vertical: DirectionSetting;
/** Snap to lines flag */
snapToLines: boolean;
/** Line position */
line: LineAndPositionSetting;
/** Line alignment */
lineAlign: LineAlignSetting;
/** Position */
position: LineAndPositionSetting;
/** Position alignment */
positionAlign: PositionAlignSetting;
/** Size */
size: number;
/** Text alignment */
align: AlignSetting;
/** Get cue as HTML */
getCueAsHTML(): DocumentFragment;
}
type DirectionSetting = '' | 'rl' | 'lr';
type LineAndPositionSetting = number | 'auto';
type LineAlignSetting = 'start' | 'center' | 'end';
type PositionAlignSetting = 'line-left' | 'center' | 'line-right' | 'auto';
type AlignSetting = 'start' | 'center' | 'end' | 'left' | 'right';Collection of text tracks.
/**
* Collection of text tracks
*/
class TextTrackList extends EventTarget {
/** Number of tracks */
readonly length: number;
/** Get track by index */
item(index: number): TextTrack | null;
/** Get track by ID */
getTrackById(id: string): TextTrack | null;
/** Array-like access */
[index: number]: TextTrack;
// Event handlers
onchange: ((event: Event) => void) | null;
onaddtrack: ((event: TrackEvent) => void) | null;
onremovetrack: ((event: TrackEvent) => void) | null;
}Collection of text track cues.
/**
* Collection of text track cues
*/
class TextTrackCueList {
/** Number of cues */
readonly length: number;
/** Get cue by index */
item(index: number): TextTrackCue | null;
/** Get cue by ID */
getCueById(id: string): TextTrackCue | null;
/** Array-like access */
[index: number]: TextTrackCue;
}Remote playback control for media elements.
/**
* Remote playback control for media elements
*/
class RemotePlayback extends EventTarget {
/** Remote playback state */
readonly state: RemotePlaybackState;
/** Watch availability */
watchAvailability(callback: RemotePlaybackAvailabilityCallback): Promise<number>;
/** Cancel watch availability */
cancelWatchAvailability(id?: number): Promise<void>;
/** Prompt for remote playback */
prompt(): Promise<void>;
// Event handlers
onconnecting: ((event: Event) => void) | null;
onconnect: ((event: Event) => void) | null;
ondisconnect: ((event: Event) => void) | null;
}
type RemotePlaybackState = 'connecting' | 'connected' | 'disconnected';
type RemotePlaybackAvailabilityCallback = (available: boolean) => void;import { MediaStream, MediaStreamTrack } from "happy-dom";
// Create media stream from tracks
const audioTrack = new MediaStreamTrack();
const videoTrack = new MediaStreamTrack();
const stream = new MediaStream([audioTrack, videoTrack]);
console.log('Stream ID:', stream.id);
console.log('Stream active:', stream.active);
console.log('Audio tracks:', stream.getAudioTracks().length);
console.log('Video tracks:', stream.getVideoTracks().length);
// Handle track events
stream.addEventListener('addtrack', (event) => {
console.log('Track added:', event.track);
});
stream.addEventListener('removetrack', (event) => {
console.log('Track removed:', event.track);
});
// Clone stream
const clonedStream = stream.clone();
console.log('Cloned stream ID:', clonedStream.id);
// Stop tracks when done
stream.getTracks().forEach(track => {
track.stop();
});import { Window } from "happy-dom";
const window = new Window();
const document = window.document;
// Create video element
const video = document.createElement('video');
video.src = 'movie.mp4';
// Add text track
const track = video.addTextTrack('subtitles', 'English', 'en');
track.mode = 'showing';
// Add cues
track.addCue(new VTTCue(0, 5, 'Hello, welcome to our video!'));
track.addCue(new VTTCue(5, 10, 'This is the second subtitle.'));
track.addCue(new VTTCue(10, 15, 'And this is the third one.'));
// Handle cue changes
track.addEventListener('cuechange', () => {
const activeCues = track.activeCues;
if (activeCues && activeCues.length > 0) {
console.log('Active cue:', activeCues[0].text);
}
});
// Access all tracks
video.textTracks.addEventListener('change', () => {
console.log('Text tracks changed');
});
// Get track by ID
const foundTrack = video.textTracks.getTrackById('track-1');
if (foundTrack) {
foundTrack.mode = 'showing';
}import { Window, VTTCue } from "happy-dom";
const window = new Window();
const document = window.document;
// Create video element with comprehensive setup
const video = document.createElement('video');
video.src = 'presentation.mp4';
video.controls = true;
// Add multiple text tracks
const subtitlesEn = video.addTextTrack('subtitles', 'English Subtitles', 'en');
const subtitlesEs = video.addTextTrack('subtitles', 'Spanish Subtitles', 'es');
const chapters = video.addTextTrack('chapters', 'Chapters', 'en');
// Add subtitle cues
subtitlesEn.addCue(new VTTCue(0, 10, 'Introduction to the topic'));
subtitlesEn.addCue(new VTTCue(10, 20, 'Main content begins here'));
subtitlesEs.addCue(new VTTCue(0, 10, 'Introducción al tema'));
subtitlesEs.addCue(new VTTCue(10, 20, 'El contenido principal comienza aquí'));
// Add chapter cues
chapters.addCue(new VTTCue(0, 10, 'Introduction'));
chapters.addCue(new VTTCue(10, 60, 'Chapter 1: Basics'));
chapters.addCue(new VTTCue(60, 120, 'Chapter 2: Advanced Topics'));
// Set default track
subtitlesEn.mode = 'showing';
chapters.mode = 'hidden';
// Handle media events
video.addEventListener('loadedmetadata', () => {
console.log('Video duration:', video.duration);
console.log('Text tracks count:', video.textTracks.length);
});
video.addEventListener('timeupdate', () => {
// Check active cues
for (let i = 0; i < video.textTracks.length; i++) {
const track = video.textTracks[i];
if (track.activeCues && track.activeCues.length > 0) {
const cue = track.activeCues[0];
if (track.kind === 'chapters') {
console.log('Current chapter:', cue.text);
}
}
}
});
document.body.appendChild(video);