react-video-capture-lib
v0.0.2
Published
A React library for video capture and preview
Downloads
170
Readme
react-video-capture
用于在线视频关键帧提取与预览进度条展示的 React 组件库。
当前项目包含两部分能力:
utils:从远程.mp4URL 渐进式提取预览帧,或按指定time/percent获取单帧。VideoCapture:一个可控/非可控的预览进度条组件,内置底部多帧预览、悬浮预览、拖拽选中与原图回传。
安装
pnpm add react-video-capture要求:
react >= 18react-dom >= 18
快速开始
import { useState } from "react";
import { VideoCapture } from "react-video-capture";
import "react-video-capture/lib/styles.css";
export function Demo() {
const [percent, setPercent] = useState(0);
const [frame, setFrame] = useState<Blob | null>(null);
return (
<VideoCapture
src="https://example.com/demo.mp4"
mode="percent"
value={percent}
onChange={(nextPercent, nextFrame) => {
setPercent(nextPercent);
setFrame(nextFrame);
}}
/>
);
}导出
export {
VideoCapture,
createVideoCapture,
captureVideoFrames,
// 以及对应的类型导出
}当前包根已同时导出组件和 utils,完整导出见 src/index.ts。
工具能力
createVideoCapture
创建一个可复用的视频预览提取器。
适用场景:
- 首次加载时根据已缓冲进度,逐步产出多张底部预览图
- 鼠标悬浮到任意位置时请求单张预览图
- 拖拽/点击选中后获取对应帧
import { createVideoCapture } from "react-video-capture";
const captureSession = await createVideoCapture(videoUrl, {
maxWidth: 352,
maxHeight: 264,
quality: 0.72,
cacheTimeStep: 100,
});参数
CreateVideoCaptureOptions
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| imageType | "image/jpeg" \| "image/png" \| "image/webp" | 输出图片格式 |
| quality | number | 图片质量,范围 0 ~ 1 |
| crossOrigin | "anonymous" \| "use-credentials" | 视频跨域模式 |
| maxWidth | number | 预览尺寸最大宽度,等比缩小,不会放大 |
| maxHeight | number | 预览尺寸最大高度,等比缩小,不会放大 |
| cacheTimeStep | number | 预览近似缓存吸附步长,单位毫秒 |
元数据
const metadata = captureSession.getMetadata();返回:
| 字段 | 说明 |
| --- | --- |
| durationTime | 视频总时长,单位毫秒 |
| width | 预览输出宽度 |
| height | 预览输出高度 |
| sourceWidth | 视频原始宽度 |
| sourceHeight | 视频原始高度 |
captureFrames
根据当前已加载到本地的缓冲区,渐进式产出多张预览图。
支持两种策略。
当前统一使用 VideoCaptureStrategy 语义:
- 固定时间间隔:
interval - 固定分段数量:
segments
const result = await captureSession.captureFrames({
strategy: {
type: "segments",
count: 10,
},
onFrame(frame) {
console.log("new frame", frame);
},
onProgress(progress) {
console.log(progress.bufferedRanges);
},
});行为说明
- 只会消费“已经缓冲到本地”的片段。
- 如果某些目标位置还没加载到,本轮不会强制继续拉取视频。
- 当浏览器判断缓冲基本停止增长后,会结束本轮提取,并通过
missingSlots返回未命中的位置。
CaptureFramesOptions
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| strategy | VideoCaptureStrategy | 预览图生成策略 |
| startTime | number | 起始时间,毫秒 |
| endTime | number | 结束时间,毫秒 |
| onFrame | (frame) => void | 每抓到一帧时回调 |
| onProgress | (progress) => void | 进度变化时回调 |
| signal | AbortSignal | 中止任务 |
CapturedFrame
| 字段 | 说明 |
| --- | --- |
| slotIndex | 当前帧对应的槽位索引 |
| requestedTime | 请求时间,毫秒 |
| actualTime | 实际截取时间,毫秒 |
| percent | 对应视频进度百分比 |
| blob | 图片二进制数据 |
captureFrameAt
获取指定时间点的一帧。
支持两种输入:
{ time }{ percent }
const frameByTime = await captureSession.captureFrameAt(
{ time: 5_000 },
{ cacheMode: "exact", outputSize: "preview" },
);
const frameByPercent = await captureSession.captureFrameAt(
{ percent: 50 },
{ cacheMode: "exact", outputSize: "source" },
);行为说明
- 如果指定位置尚未缓冲到本地,直接返回
null outputSize: "preview"返回缩略图尺寸outputSize: "source"返回视频原始尺寸cacheMode: "approximate"会按cacheTimeStep吸附时间,用于减少高频 seekcacheMode: "exact"一定按目标时间请求
CaptureFrameAtOptions
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| cacheMode | "approximate" \| "exact" | 近似缓存或精确时间 |
| outputSize | "preview" \| "source" | 返回预览尺寸或原始尺寸 |
| signal | AbortSignal | 中止本次获取 |
captureVideoFrames
一次性抽取多张分段帧的便捷方法。
import { captureVideoFrames } from "react-video-capture";
const result = await captureVideoFrames(videoUrl, {
count: 8,
});说明:
- 内部使用
segments - 要求目标帧已经全部缓冲完成
- 如果仍有未加载位置,会直接抛错
- 更适合“视频本地可快速加载完成”的简单场景
VideoCapture 组件
用于展示视频底部预览进度条。
能力:
- 默认外层 block
- 类似 iOS 视频预览的底部轨道样式
- 支持拖动、点击、悬浮预览
- 支持受控 / 非受控
mode="time"时,value与onChange使用毫秒mode="percent"时,value与onChange使用百分比onChange返回的frame为原始尺寸Blob
import { useState } from "react";
import { VideoCapture } from "react-video-capture";
export function TimeModeDemo() {
const [time, setTime] = useState(0);
const [frame, setFrame] = useState<Blob | null>(null);
return (
<VideoCapture
src="https://example.com/demo.mp4"
mode="time"
value={time}
onChange={(nextTime, nextFrame) => {
setTime(nextTime);
setFrame(nextFrame);
}}
captureStrategy={{ type: "segments", count: 10 }}
/>
);
}Props
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| src | string | 视频 URL |
| mode | "time" \| "percent" | 值模式 |
| value | number | 受控值 |
| defaultValue | number | 非受控初始值 |
| onChange | (value, frame) => void | 值变化回调,frame 为原始尺寸 Blob \| null |
| captureStrategy | VideoCaptureStrategy | 底部预览图生成策略 |
| captureOptions | CreateVideoCaptureOptions | 提取器配置 |
| disabled | boolean | 是否禁用 |
继承原生 div 属性。
交互说明
- 初次加载时,组件会根据
captureStrategy渐进式填充底部预览图 - hover 时会先显示最近邻预加载图,再异步补精确预览
- hover 精确图请求带短延迟,并且鼠标移走后会被中止
- drag 时会更新位置值,并在短延迟后请求选中帧
- 选中帧请求与 hover 预览分开缓存
onChange会先回调最新value与null,等原始帧完成后再回调一次真实Blob
受控与非受控
受控:
<VideoCapture
src={videoUrl}
mode="percent"
value={percent}
onChange={(nextPercent) => setPercent(nextPercent)}
/>非受控:
<VideoCapture
src={videoUrl}
mode="percent"
defaultValue={0}
onChange={(nextPercent, frame) => {
console.log(nextPercent, frame);
}}
/>性能设计
当前实现做了这些优化:
- 预加载缩略图与选中原图分开缓存
hover与selected使用独立任务通道- 所有
seek / drawImage / toBlob串行执行,避免同一video/canvas被并发污染 approximate模式通过cacheTimeStep吸附时间,减少相近点频繁截图hover优先显示最近邻预加载图,降低首次等待感
限制说明
- 依赖浏览器
HTMLVideoElement、canvas与buffered能力,不支持 SSR 直接执行 - 远程视频必须满足可播放与正确的 CORS 配置,否则无法截图
- 如果目标时间点尚未缓冲,
captureFrameAt会返回null - 目前工具层只处理截图提取,不包含你业务侧的视频播放器联动逻辑
本地开发
pnpm install
pnpm dev库构建:
pnpm buildPlayground 构建:
pnpm build:webLint:
pnpm lint目录结构
.
├── src/
│ ├── components/ # VideoCapture 组件
│ ├── utils/ # 视频帧提取工具
│ ├── index.ts # 对外导出
│ └── styles.less
├── example/ # 本地示例
├── public/ # 示例静态资源
└── README.mdLicense
MIT
