【问题标题】:Create thumbnail from video file via file input通过文件输入从视频文件创建缩略图
【发布时间】:2014-07-01 16:40:40
【问题描述】:

我正在尝试从 input type='file' 表单中的视频文件 (mp4,3gp) 创建缩略图预览。许多人说这只能在服务器端完成。我发现这很难相信,因为我最近才使用 HTML5 Canvas 和 Javascript 发现了这个 Fiddle。

Thumbnail Fiddle

唯一的问题是这需要视频存在并且用户在点击按钮捕获缩略图之前点击播放。我想知道是否有一种方法可以在没有玩家在场且用户单击按钮的情况下获得相同的结果。例如:用户点击文件上传,选择视频文件,生成缩略图。欢迎任何帮助/想法!

【问题讨论】:

  • 玩家显然必须在场,因为这就是生成要捕获的图像的原因。
  • 您可以使用css隐藏播放器,并调用videoTag.play()开始播放。我建议跳入 18 秒,等待它显示,然后将其发送到画布 drawImage 例程。我以这种方式将一个电影文件夹变成了 chrome 中的缩略图库,所以我可以向你保证它可以工作。
  • 如果视频使用当前浏览器支持的编解码器,您可以处理拖放/选择的视频文件,获取视频文件的对象 URL,并将该值设置为视频的 src 属性元素。
  • 我最近一直在开发一个插件来解决您问题中的项目(从<video>s 生成缩略图,将Blob/File 转换为<video>,等等)。看看,也许它会证明对你有用。 github.com/rnicholus/frame-grab.js
  • frame-grab 的功能是 100% 客户端。例如,如果您在页面上包含文件输入元素或拖放区。当您的用户选择/删除视频文件时,您可以将文件输入/删除事件中的 Blob 传递给 frab-grab 的 blob_to_video 方法,获取 <video> 并将其输入帧抓取实例,您可以在其中通过在 frame-grab 的 API 中公开的各种工作流/方法生成图像。这一切都发生在浏览器中,没有任何东西发送到服务器。我鼓励您在 github 存储库中提出问题或留下功能请求,我们可以在那里讨论更多内容。

标签: javascript html video thumbnails video-thumbnails


【解决方案1】:

Canvas.drawImage 必须基于 html 内容。

source

here is a simplier 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);
}

这种方案的好处是可以根据视频的时间选择自己想要的缩略图。

【讨论】:

  • 他们有什么方法可以做到这一点而不在文档上加载视频
  • 我只能得到视频尺寸0 0和白色图像。
  • 缩略图是从底部裁剪的,我无法获得完整的图像
  • @wajdi_jurry 视频大小是从元数据加载的,因此您可能需要向“loadedmetadata”事件添加事件处理程序并从那里获取视频宽度和高度。完成后,您应该能够调整画布大小并绘制图像。
【解决方案2】:

最近需要这个并进行了一些测试并将其降到最低限度,请参阅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();

【讨论】:

  • 您的 codepen 似乎不错,但是当我尝试上传 > 100mb iPhone 人像拍摄模式视频时,有时屏幕截图是空白的,或者有时它们不会被上传。仅供参考,我希望画布尺寸为 170x100。您是否遇到过 iPhone 纵向模式视频的类似问题?
  • @vbjain 不能说我有,抱歉。
【解决方案3】:

最近需要这个,所以我写了一个函数,接收一个视频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 */
                );
            });
        });
    });
}

【讨论】:

  • 重新使用setTimeout,问题可能是您在搜索之后添加事件侦听器?首先添加侦听器可能有意义,然后导致搜索发生。与加载的元数据相同
  • @xaphod 我记得我什至尝试使用 setTimeout 0 将回调推送到事件队列中,但在 safari 上仍然不够可靠。它需要一个实际的延迟。也许只是 Safari 加载视频太慢了?您是否能够在 Safari 上验证您的建议?
  • 在我的 iMac 上使用了最新的 safari stable,它不需要任何 setTimeout - 或者至少,缩略图生成得很好。这是在请求搜索之前添加侦听器。
  • @xaphod 我还没有在 Safari 14 上,但是没有设置超时,大约十分之八的视频会很好,一些偶尔的视频会无缘无故地失败
  • 感谢您提供的信息。为了安全起见,我会把它留在里面。
【解决方案4】:

有了 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));

【讨论】:

    【解决方案5】:

    显示缩略图的最简单方法是使用&lt;video&gt; 标签本身。

    &lt;video src="http://www.w3schools.com/html/mov_bbb.mp4"&gt;&lt;/video&gt;

    如果您想要 x 秒的缩略图,请在 URL 中使用 #t

    例如:

    &lt;video src="http://www.w3schools.com/html/mov_bbb.mp4#t=5"&gt;&lt;/video&gt;

    确保它不包含任何属性,如autoplaycontrols,并且不应将source 标记作为子元素。

    【讨论】:

    • 这不仅是一种简单的方法,而且也是错误的。使用#t=5 将导致视频从 5 秒开始。
    • 另外,问题是来自文件输入的视频,而不是 url。
    【解决方案6】:

    你可以使用我写的这个函数。您只需要将视频文件作为参数传递给它。它将返回该视频的缩略图(即图像预览)的 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)
    }
    

    【讨论】:

      猜你喜欢
      • 2010-12-18
      • 2012-02-24
      • 2013-12-20
      • 1970-01-01
      • 2012-03-13
      • 2012-02-24
      • 1970-01-01
      • 1970-01-01
      • 2018-05-15
      相关资源
      最近更新 更多