Thumbnail Extractor
Generating clip thumbnails using hidden video elements for in-track display
ThumbnailExtractorAdapter generates still-frame thumbnails for video clips by seeking a hidden <video> element to the requested media frame and capturing to a canvas. Thumbnails are displayed inside TimelineClip rows.
This adapter does not use WebCodecs - it works in all browsers that support <video>.
Import
import { createThumbnailExtractor, ThumbnailExtractorAdapter } from '@timelinx/media-web';createThumbnailExtractor(config?)
function createThumbnailExtractor(config?: ThumbnailConfig): ThumbnailExtractorAdapterThumbnailConfig
type ThumbnailConfig = {
width?: number;
height?: number;
quality?: number;
maxConcurrent?: number;
};| Field | Type | Default | Notes |
|---|---|---|---|
width | number | 160 | Output thumbnail width in pixels |
height | number | 90 | Output thumbnail height in pixels |
quality | number | 0.7 | JPEG quality (0–1) |
maxConcurrent | number | 4 | Max concurrent video element seeks to prevent browser throttling |
How It Works
The adapter maintains a pool of hidden <video> elements. For each ThumbnailRequest:
- A video element is loaded with
src = objectURL(file). - The element is seeked to
mediaFrame / fpsseconds. - After
seekedfires, the frame is captured to anOffscreenCanvasand converted to a JPEG data URL. - The data URL is returned as
ThumbnailResult.bitmap.
Object URLs
The adapter manages object URLs internally. Pass the source File object via configureClip() - the adapter handles URL.createObjectURL() and revocation.
configureClip(clipId, file)
Register a source file for a clip before requesting thumbnails:
const extractor = createThumbnailExtractor({ width: 160, height: 90 });
// Called when the user imports a file
await extractor.configureClip('clip-1', videoFile);Thumbnail Requests
The ThumbnailProvider contract is called by PlaybackEngine:
// Called internally - shown here for reference
const result = await extractor({
clipId: 'clip-1',
mediaFrame: toFrame(90),
width: 160,
height: 90,
});
// result.bitmap is a string (data URL) or ImageBitmapPriority Queue
The adapter includes a priority queue for managing large numbers of thumbnail requests efficiently. Visible clips get higher priority than off-screen clips:
import { ThumbnailQueue, ThumbnailCache } from '@timelinx/core';
const queue = new ThumbnailQueue({ maxConcurrent: 4 });
const cache = new ThumbnailCache({ maxSize: 200 });
// Queue a request with priority
queue.enqueue({
request: { clipId, mediaFrame, width, height },
priority: 'high', // 'high' | 'normal' | 'low'
onResult: (result) => cache.set(result),
});Passing to PipelineConfig
import { createThumbnailExtractor } from '@timelinx/media-web';
const thumbnailProvider = createThumbnailExtractor({ width: 160, height: 90 });
const engine = new TimelineEngine({
initialState,
pipeline: {
videoDecoder: createWebCodecsDecoder(),
compositor: createWebGLCompositor({ canvas }),
thumbnailProvider,
},
});When thumbnailProvider is present, TimelineClip automatically renders thumbnail strips inside the clip row.