Skip to main content

在 JavaScript 中从视频中提取缩略图

可以使用 Mediabunny 从视频文件中提取单个帧(缩略图)。

🌐 Extracting a single frame (thumbnail) from a video file can be done using Mediabunny.

这是一个 extractThumbnail() 函数,你可以将其复制并粘贴到你的项目中:

🌐 Here's an extractThumbnail() function you can copy and paste into your project:

extract-thumbnail.ts
import {ALL_FORMATS, Input, InputDisposedError, UrlSource, VideoSample, VideoSampleSink} from 'mediabunny'; export type ExtractThumbnailProps = { src: string; timestampInSeconds: number; signal?: AbortSignal; }; export async function extractThumbnail({src, timestampInSeconds, signal}: ExtractThumbnailProps): Promise<VideoSample> { using input = new Input({ formats: ALL_FORMATS, source: new UrlSource(src), }); const videoTrack = await input.getPrimaryVideoTrack(); if (!videoTrack) { throw new Error('No video track found in the input'); } if (signal?.aborted) { throw new Error('Aborted'); } const sink = new VideoSampleSink(videoTrack); const sample = await sink.getSample(timestampInSeconds); if (!sample) { throw new Error(`No frame found at timestamp ${timestampInSeconds}s`); } return sample; }

示例

🌐 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

该函数返回一个 VideoSample 对象。当它被垃圾回收清理时,它将被自动关闭,但会打印一个警告。

🌐 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.

你可以调用 .close() 来显式关闭样本并防止打印警告。

🌐 You can call .close() to explicitly close the sample and prevent the warning from being printed.

Explicitly closing a sample
const sample = await extractThumbnail({ src: 'https://example.com/video.mp4', timestampInSeconds: 5, }); sample.draw(ctx!, 0, 0); sample.close();

或者,你可以使用 using 语句在样本超出作用域时进行清理。

🌐 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

传递一个 AbortSignal 来取消缩略图提取:

🌐 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 seconds
const controller = new AbortController(); const timeoutPromise = new Promise<never>((_, reject) => { const timeoutId = setTimeout(() => { controller.abort(); reject(new Error('Thumbnail extraction timed out after 5 seconds')); }, 5000); controller.signal.addEventListener('abort', () => clearTimeout(timeoutId), {once: true}); }); try { using sample = await Promise.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