Blog スタッフブログ

JavaScript WEB制作 システム開発

[JavaScript]選択した動画ファイルからサムネイルを取得

html5

こんにちは、株式会社MIXシステム開発担当のBloomです。

早速本題の選択した動画ファイルからサムネイルを取得する手順について、

お仕事の中で得た知見を共有させていただきたいと思います。

サムネイルの生成方法

ブラウザ上でユーザがアップロードした動画ファイルを用いてサムネイルを生成する場合、次のフローに則って行います。

  1. <input type=”file”>要素からファイルを選択
  2. 選択された動画を新規生成した<video>要素で再生
  3. 再生後、さらに新規生成した<canvas>要素で描画
  4. 描画後にtoDataURL()関数を利用して画像blobを取得、適宜<img>要素へ適応

それでは実際に構築してみましょう。

<head>
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<style>
img.thumbnail
{
  max-width: 640px;
  max-height: 480px;
}
</style>
</head>
<body>
  <form>
    <label for="select-file">Select Video file</label>
    <input type="file" id="select-file" name="file" onchange="makeThumbnail(this);">
  </form>
  <img class="thumbnail">
</body>
<script>
  function makeThumbnail(_this) {
    // 1.ファイルを選択
    var file = $(_this).prop('files')[0];

    var fileReader = new FileReader();
    fileReader.onload = function() {
        var blob = new Blob([fileReader.result], {type: file.type});
        var url = URL.createObjectURL(blob);
        var video = document.createElement('video');

        var thumbnailFrame = 0; // サムネイル取得秒指定
        var retryCount = 10; // サムネイル生成失敗許容回数
        var currentCount = 0;

        // timeupdateイベントで動画の再生を検知しています
        var timeupdate = function() {
            if (snapImage()) {
                video.removeEventListener('timeupdate', timeupdate);
                video.pause();
            }
            else if (currentCount == retryCount) {
            // サムネイル生成失敗
                console.log("make thumbnail failed");
                video.removeEventListener('timeupdate', timeupdate);
                video.pause();
            }
        }
      
        video.addEventListener('canplay', function() {
            if (video.duration > thumbnailFrame) {
                video.currentTime = thumbnailFrame; 
            }
            else {
                video.currentTime = video.duration / 2;
            }
        });
        var snapImage = function() {
            // 3.再生後、さらに新規生成した<canvas>要素で描画
            var canvas = document.createElement('canvas');
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
            // 4.描画後にtoDataURL()関数を利用して画像blobを取得、適宜<img>要素へ適応
            var image = canvas.toDataURL("image/png");
            var success = image.length > 100000;
            if (success) {
                var img = $(".thumbnail");
                img.attr('src', image);
                setTimeout(() => { // Safari対策
                    URL.revokeObjectURL(url);
                }, 2000);
            }
            else {
                currentCount+=1;
            }
            return success;
        }
        // 2.選択された動画を新規生成した<video>要素で再生
        video.addEventListener('timeupdate', timeupdate);
        video.preload = 'meta';
        video.src = url;
        // Safari / IE11
        video.muted = true;
        video.playsInline = true;
        video.currentTime = thumbnailFrame;
        video.play();
    }
    fileReader.readAsArrayBuffer(file);
  }
</script>

実行結果

これで簡単にサムネイルが生成できました。

画像blobをそのまま<input type=”hidden”>要素のvalueへ格納することでフォームでそのまま送信することもできます。良かったですね。

注意点

2.の行程において必ず<video>要素による再生処理を必要とするため、ブラウザが対応していないコーデックの動画が選択された場合はサムネイルの生成に失敗します。

一般的なH.264形式はほとんどのブラウザで対応していますが、現在iOS端末カメラアプリのデフォルト設定となるHEVC(H.265)形式は現在Safariを含む一部のブラウザでしか対応していないためご注意ください。

参考文献

Video and image thumbnail for HTML file upload – Codepen.io