Extracting a thumbnail from a video in JavaScript
Extracting a single frame (thumbnail) from a video file can be done using Mediabunny.
Here's an extractThumbnail() function you can copy and paste into your project:
extract-thumbnail.tsimport {ALL_FORMATS ,Input ,InputDisposedError ,UrlSource ,VideoSample ,VideoSampleSink } from 'mediabunny'; export typeExtractThumbnailProps = {src : string;timestampInSeconds : number;signal ?:AbortSignal ; }; export async functionextractThumbnail ({src ,timestampInSeconds ,signal }:ExtractThumbnailProps ):Promise <VideoSample > { usinginput = newInput ({formats :ALL_FORMATS ,source : newUrlSource (src ), }); constvideoTrack = awaitinput .getPrimaryVideoTrack (); if (!videoTrack ) { throw newError ('No video track found in the input'); } if (signal ?.aborted ) { throw newError ('Aborted'); } constsink = newVideoSampleSink (videoTrack ); constsample = awaitsink .getSample (timestampInSeconds ); if (!sample ) { throw newError (`No frame found at timestamp ${timestampInSeconds }s`); } returnsample ; }
Example
Here is how you can draw a thumbnail to a canvas:
const sample = await extractThumbnail ({
src : 'https://remotion.media/video.mp4',
timestampInSeconds : 5,
});
const canvas = document .createElement ('canvas');
canvas .width = sample .displayWidth ;
canvas .height = sample .displayHeight ;
const ctx = canvas .getContext ('2d');
sample .draw (ctx !, 0, 0);
sample .close ();Memory management
The function returns a VideoSample object. When it gets cleaned up by garbage collection, it will be automatically closed, but a warning will be printed.
You can call .close() to explicitly close the sample and prevent the warning from being printed.
Explicitly closing a sampleconst sample = await extractThumbnail({ src: 'https://example.com/video.mp4', timestampInSeconds: 5, }); sample.draw(ctx!, 0, 0); sample.close();
Or, you can use the using statement to clean up the sample when it goes out of scope.
using sample = await extractThumbnail({
src: 'https://example.com/video.mp4',
timestampInSeconds: 5,
});
sample.draw(ctx!, 0, 0);Abort frame extraction
Pass an AbortSignal to cancel thumbnail extraction:
const controller = new AbortController ();
setTimeout (() => controller .abort (), 5000);
try {
using sample = await extractThumbnail ({
src : 'https://example.com/video.mp4',
timestampInSeconds : 10,
signal : controller .signal ,
});
console .log ('Got frame!');
} catch (error ) {
console .error ('Thumbnail extraction was aborted or failed:', error );
}Setting a timeout
Here is how you can set a maximum duration for extracting a thumbnail:
Fail if not able to extract within 5 secondsconstcontroller = newAbortController (); consttimeoutPromise = newPromise <never>((_ ,reject ) => { consttimeoutId =setTimeout (() => {controller .abort ();reject (newError ('Thumbnail extraction timed out after 5 seconds')); }, 5000);controller .signal .addEventListener ('abort', () =>clearTimeout (timeoutId ), {once : true}); }); try { usingsample = awaitPromise .race ([extractThumbnail ({src : 'https://example.com/video.mp4',timestampInSeconds : 10,signal :controller .signal , }),timeoutPromise , ]);console .log ('Got frame!'); } catch (error ) {console .error ('Thumbnail extraction was aborted or failed:',error ); }
See also
- Extracting video frames - Extract multiple frames from a video
- Mediabunny Documentation
- Packets & samples in Mediabunny
VideoSampleSinkAPI- Supported formats