2025-06-26 15:12:34 +08:00

139 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 从视频文件中提取第一帧并返回为File对象
* @param videoFile 视频文件
* @returns 包含视频第一帧的File对象
*/
export const extractVideoFirstFrame = (videoFile: File): Promise<File> => {
return new Promise((resolve, reject) => {
const videoUrl = URL.createObjectURL(videoFile);
const video = document.createElement('video');
video.src = videoUrl;
video.crossOrigin = 'anonymous';
video.muted = true;
video.preload = 'metadata';
video.onloadeddata = () => {
try {
// 设置视频时间到第一帧
video.currentTime = 0.1;
} catch (e) {
URL.revokeObjectURL(videoUrl);
reject(e);
}
};
video.onseeked = () => {
try {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('无法获取canvas上下文');
}
// 绘制视频帧到canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// 将canvas转换为DataURL
const dataUrl = canvas.toDataURL('image/jpeg');
// 将DataURL转换为Blob
const byteString = atob(dataUrl.split(',')[1]);
const mimeString = dataUrl.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ab], { type: mimeString });
// 创建File对象
const frameFile = new File(
[blob],
`${videoFile.name.replace(/\.[^/.]+$/, '')}_frame.jpg`,
{ type: 'image/jpeg' }
);
// 清理URL对象
URL.revokeObjectURL(videoUrl);
resolve(frameFile);
} catch (e) {
URL.revokeObjectURL(videoUrl);
reject(e);
}
};
video.onerror = () => {
URL.revokeObjectURL(videoUrl);
reject(new Error('视频加载失败'));
};
});
};
// 获取视频时长
export const getVideoDuration = (file: File): Promise<number> => {
return new Promise((resolve) => {
const video = document.createElement('video');
video.preload = 'metadata';
video.onloadedmetadata = () => {
URL.revokeObjectURL(video.src);
resolve(video.duration);
};
video.onerror = () => {
URL.revokeObjectURL(video.src);
resolve(0); // Return 0 if we can't get the duration
};
video.src = URL.createObjectURL(file);
});
};
// 根据 mp4 的url来获取视频时长
/**
* 根据视频URL获取视频时长
* @param videoUrl 视频的URL
* @returns 返回一个Promise解析为视频时长
*/
export const getVideoDurationFromUrl = async (videoUrl: string): Promise<number> => {
return await new Promise((resolve, reject) => {
// 创建临时的video元素
const video = document.createElement('video');
// 设置为只加载元数据,不加载整个视频
video.preload = 'metadata';
// 处理加载成功
video.onloadedmetadata = () => {
// 释放URL对象
URL.revokeObjectURL(video.src);
// 返回视频时长(秒)
resolve(video.duration);
};
// 处理加载错误
video.onerror = () => {
URL.revokeObjectURL(video.src);
reject(new Error('无法加载视频'));
};
// 处理网络错误
video.onabort = () => {
URL.revokeObjectURL(video.src);
reject(new Error('视频加载被中止'));
};
// 设置视频源
video.src = videoUrl;
// 添加跨域属性(如果需要)
video.setAttribute('crossOrigin', 'anonymous');
// 开始加载元数据
video.load();
});
};