Classes
Classes exported by @mcut/media.
Classes
AudioNotDecodableError
class AudioNotDecodableError extends Error {
constructor(codec: string | undefined)
}Error thrown when a file has an audio track but the browser has no decoder for its codec.
MediaProbeError
class MediaProbeError extends Error {
readonly name = 'MediaProbeError'
readonly code: MediaProbeErrorCode
}Error thrown by probeMedia and createAsset when a file cannot be read as media or has no playable track. code says which.
PreviewMediaPool
class PreviewMediaPool implements FrameSource {
constructor(private resolveAsset: (assetId: AssetId) => AssetRef | undefined)
readonly audio: PreviewAudio
get frameVersion(): number
getFrame(assetId: AssetId, sourceTimeMs: number): CanvasImageSource | null
sync(items: ActiveMediaItem[], options: PreviewSyncOptions): void
pauseAll(): void
dispose(): void
}The approximate FrameSource for interactive preview. It keeps one muted pooled video element per asset in sync with the playback clock and decodes bitmaps for images. Sound comes from audio, not from the elements. frameVersion goes up whenever getFrame may return a different picture for the same arguments, such as when a seek starts or lands, a video loads, a decoded frame arrives, or an image loads. A paused preview repaints only when it moves, and a paused element more than 5 ms from the playhead seeks to it.
PreviewAudio
class PreviewAudio {
setAudioSources(sources: ReadonlyMap<ElementId, string> | undefined): void
getAudioSources(): ReadonlyMap<ElementId, string> | undefined
clockTimeMs(displayTimeMs: number): number | null
sync(project: Project, playback: PlaybackState): void
dispose(): void
}Preview sound on one AudioContext, exported as a type and reached through PreviewMediaPool.audio. It plays the same audible segments and timed decode as exportProject, so every container is heard at the source time export writes. sync schedules sound a second ahead of the playhead with each clip's volume, fades, keyframes, time stretch, and reversal, applies track mute, master volume, and mute, and stays silent while paused or when the transport rate is above 4. A clip at a constant speed plays from one continuous decode and one time stretcher, so a stretched or reversed clip has no seams. A clip on a speed curve plays in crossfaded half-second windows. A seek starts a new anchor. A rate change hands off to a new anchor 200 ms ahead with a 20 ms equal-power crossfade, so sound carries across it. setAudioSources swaps an element's audio for a URL, as export does with audioSources. clockTimeMs returns the timeline time being heard at displayTimeMs, the time the next frame reaches the screen. While the context is suspended, and before the output first renders, it holds at the play position while a clip is due within a second, and returns null when there is nothing to hear, so a silent stretch plays on the wall clock while a sound server starts. A pause fades the sound out over 5 ms at a stop 50 ms ahead of the context clock, because sound already sent to the output still plays. While paused, clockTimeMs follows the sound still being heard up to the middle of that fade and holds there, so the paused playhead comes to rest where the sound stopped and a play from it starts there, with nothing heard twice or skipped. Two seconds after the fade has been heard the context suspends, and a play after that holds at the paused position while the context resumes. A play anchors its sound on the frame that schedules its voices, and a voice starts at its anchor while the anchor lies ahead of the context clock, so a slow frame does not trim its first samples. PlayerCanvas passes it to the playback loop so the picture follows the sound.
ScrubFrameCache
class ScrubFrameCache {
constructor(
private maxFrames = 150,
private minGapMs = 90,
)
capture(source: HTMLVideoElement, timeMs: number): void
nearest(timeMs: number): OffscreenCanvas | null
get size(): number
clear(): void
}A bounded cache of downscaled video frames sorted by time, capturing opportunistically and serving the nearest frame while a seek is pending.