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.tstsimport {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:
tsxconstsample = awaitextractThumbnail ({src : 'https://remotion.media/video.mp4',timestampInSeconds : 5,});constcanvas =document .createElement ('canvas');canvas .width =sample .displayWidth ;canvas .height =sample .displayHeight ;constctx =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 sampletsconst 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.
tsusing 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:
tsconstcontroller = newAbortController ();setTimeout (() =>controller .abort (), 5000);try {usingsample = awaitextractThumbnail ({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 secondstsconstcontroller = 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