Создать миниатюру из видео файла через файл ввода

Я пытаюсь создать миниатюру предварительного просмотра из видеофайла (mp4,3gp) из формы input type='file', Многие говорят, что это может быть сделано только на стороне сервера. Мне трудно в это поверить, так как я недавно натолкнулся на эту скрипку, используя HTML5 Canvas и Javascript.

Миниатюра скрипка

Единственная проблема состоит в том, что это требует присутствия видео, и пользователь нажимает кнопку воспроизведения, прежде чем нажать кнопку, чтобы сделать пиктограмму. Мне интересно, есть ли способ получить те же результаты без присутствия игрока и нажатия кнопки пользователем. Например: пользователь нажимает на загрузку файла и выбирает видео файл, а затем генерируется миниатюра. Любая помощь / мысли приветствуются!

8 ответов

Canvas.drawImage должен быть основан на содержании HTML.

источник

вот простой jsfiddle

//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();
})

Другие вопросы по тегам