Создать миниатюру из видео файла через файл ввода
Я пытаюсь создать миниатюру предварительного просмотра из видеофайла (mp4,3gp) из формы input type='file'
, Многие говорят, что это может быть сделано только на стороне сервера. Мне трудно в это поверить, так как я недавно натолкнулся на эту скрипку, используя HTML5 Canvas и Javascript.
Единственная проблема состоит в том, что это требует присутствия видео, и пользователь нажимает кнопку воспроизведения, прежде чем нажать кнопку, чтобы сделать пиктограмму. Мне интересно, есть ли способ получить те же результаты без присутствия игрока и нажатия кнопки пользователем. Например: пользователь нажимает на загрузку файла и выбирает видео файл, а затем генерируется миниатюра. Любая помощь / мысли приветствуются!
8 ответов
Canvas.drawImage должен быть основан на содержании HTML.
//and code
function capture(){
var canvas = document.getElementById('canvas');
var video = document.getElementById('video');
canvas.getContext('2d').drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
}
Преимущество этого решения заключается в том, что вы можете выбрать нужный эскиз в зависимости от времени видео.
Недавно это было нужно, поэтому я написал функцию, чтобы снимать видео file
и желаемый timestamp
и вернуть image blob
на тот момент видео.
Пример использования:
try {
// get the frame at 1.5 seconds of the video file
const cover = await getVideoCover(file, 1.5);
// print out the result image blob
console.log(cover);
} catch (ex) {
console.log("ERROR: ", ex);
}
Функция:
function getVideoCover(file, seekTo = 0.0) {
console.log("getting video cover for file: ", file);
return new Promise((resolve, reject) => {
// load the file to a video player
const videoPlayer = document.createElement('video');
videoPlayer.setAttribute('src', URL.createObjectURL(file));
videoPlayer.load();
videoPlayer.addEventListener('error', (ex) => {
reject("error when loading video file", ex);
});
// load metadata of the video to get video duration and dimensions
videoPlayer.addEventListener('loadedmetadata', () => {
// seek to user defined timestamp (in seconds) if possible
if (videoPlayer.duration < seekTo) {
reject("video is too short.");
return;
}
// delay seeking or else 'seeked' event won't fire on Safari
setTimeout(() => {
videoPlayer.currentTime = seekTo;
}, 200);
// extract video thumbnail once seeking is complete
videoPlayer.addEventListener('seeked', () => {
console.log('video is now paused at %ss.', seekTo);
// define a canvas to have the same dimension as the video
const canvas = document.createElement("canvas");
canvas.width = videoPlayer.videoWidth;
canvas.height = videoPlayer.videoHeight;
// draw the video frame to canvas
const ctx = canvas.getContext("2d");
ctx.drawImage(videoPlayer, 0, 0, canvas.width, canvas.height);
// return the canvas image as a blob
ctx.canvas.toBlob(
blob => {
resolve(blob);
},
"image/jpeg",
0.75 /* quality */
);
});
});
});
}
Недавно это понадобилось, и я провёл немало тестирований и довел его до минимума, см. https://codepen.io/aertmann/pen/mAVaPx
Есть некоторые ограничения, где он работает, но в настоящее время довольно хорошая поддержка браузера: Chrome, Firefox, Safari, Opera, IE10, IE11, Android (Chrome), iOS Safari (10+).
video.preload = 'metadata';
video.src = url;
// Load video in Safari / IE11
video.muted = true;
video.playsInline = true;
video.play();
Самый простой способ отобразить эскиз - использовать
<video>
сам тег.
Убедитесь, что в нем нет таких атрибутов, как
autoplay
или же
controls
и у него не должно быть
source
тег как дочерний элемент.
Вы можете использовать эту функцию, которую я написал. Вам просто нужно передать ему видеофайл в качестве аргумента. Он вернет dataURL эскиза (то есть предварительного просмотра изображения) этого видео. Вы можете изменить тип возвращаемого значения в соответствии с вашими потребностями.
const generateVideoThumbnail = (file: File) => {
return new Promise((resolve) => {
const canvas = document.createElement("canvas");
const video = document.createElement("video");
// this is important
video.autoplay = true;
video.muted = true;
video.src = URL.createObjectURL(file);
video.onloadeddata = () => {
let ctx = canvas.getContext("2d");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
video.pause();
return resolve(canvas.toDataURL("image/png"));
};
});
};
Имейте в виду, что это асинхронная функция. Поэтому убедитесь, что используете его соответствующим образом.
Например:
const handleFileUpload = async (e) => {
const thumbnail = await generateVideoThumbnail(e.target.files[0]);
console.log(thumbnail)
}
С jQuery Lib вы можете использовать здесь мой код. $video - это элемент Video. Эта функция возвращает строку
function createPoster($video) {
//here you can set anytime you want
$video.currentTime = 5;
var canvas = document.createElement("canvas");
canvas.width = 350;
canvas.height = 200;
canvas.getContext("2d").drawImage($video, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL("image/jpeg");;
}
Пример использования:
$video.setAttribute("poster", createPoster($video));
Я столкнулся с этой проблемой на сайте nextJS, где я использовал Mozilla MediaRecorder для записи клиентской части видео в браузере. На мобильных устройствах первый кадр всегда был пустым, и хаки с использованием нескольких миллисекунд не помогли, поскольку мое видео шло из объекта MediaRecorder (а не передавалось в потоковом режиме из файла).
Если это поможет кому-то, кто хочет сделать это в реакции, и использование «react-media-recorder» ниже отлично сработало для меня, я сделал снимок видео после начала записи, а затем использовал его в качестве миниатюры в качестве атрибута «плакат».
import React, { useEffect, useRef, useState } from "react";
import { useReactMediaRecorder } from "react-media-recorder";
export default function VideoRecorder() {
const { status, startRecording, stopRecording, mediaBlobUrl, error } =
useReactMediaRecorder({ video: true });
// State to hold the thumbnail URL
const [thumbnailUrl, setThumbnailUrl] = useState(null);
// Reference to the video element
const videoRef = useRef(null);
// Reference to the canvas element
const canvasRef = useRef(null);
useEffect(() => {
if (videoRef.current) {
videoRef.current.addEventListener("playing", captureSnapshot);
}
return () => {
if (videoRef.current) {
videoRef.current.removeEventListener("playing", captureSnapshot);
}
};
}, []);
const captureSnapshot = () => {
/* Get canvas context */
const ctx = canvasRef.current.getContext("2d");
/* Set canvas dimensions to match video */
canvasRef.current.width = videoRef.current.videoWidth;
canvasRef.current.height = videoRef.current.videoHeight;
/* Draw current video frame onto the canvas */
ctx.drawImage(videoRef.current, 0, 0);
/* Export canvas content to data URL and set as thumbnail */
setThumbnailUrl(canvasRef.current.toDataURL("image/jpeg", 0.95));
};
if (error) {
return <p>Error: {error}</p>;
}
return (
<div>
<p>{status}</p>
<button onClick={startRecording} disabled={status === "recording"}>
Start Recording
</button>
<button onClick={stopRecording} disabled={status !== "recording"}>
Stop Recording
</button>
{mediaBlobUrl && (
<video
ref={videoRef}
poster={thumbnailUrl}
src={mediaBlobUrl}
controls
autoPlay
loop
style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }}
/>
)}
<canvas ref={canvasRef} style={{ display: "none" }}></canvas>
</div>
);
}
Недавно я наткнулся на ту же проблему, и вот как я ее обошёл.
во-первых, будет проще, если у вас есть видео в виде HTML-элемента, поэтому вы либо имеете его в HTML, как это
<video src="http://www.w3schools.com/html/mov_bbb.mp4"></video>
или вы берете из ввода и создаете с ним HTML-элемент.
Хитрость заключается в том, чтобы установить время начала в теге видео для части, которую вы хотите найти и иметь в качестве миниатюры, вы можете сделать это, добавив#t=1.5
до конца источника видео.
<video src="http://www.w3schools.com/html/mov_bbb.mp4#t=1.5"></video>
где1.5
это время, когда вы хотите найти и получить миниатюру.
Это, однако, заставляет видео начинать воспроизведение с этого раздела видео, чтобы избежать добавления прослушивателя событий на кнопки воспроизведения видео и запускать видео с самого начала, установивvideo.currentTime = 0
const video = document.querySelector('video');
video.addEventListener('click', (e)=> {
video.currentTime = 0 ;
video.play();
})