web-dev-qa-db-ja.com

特定の時間にファイル入力によって選択されたビデオファイルのサムネイル/スナップショットを生成します

<input type="file">を介して選択されたビデオファイルのスナップショットを、バックグラウンドでサイレントにビデオ内の特定の時間に取得するにはどうすればよいですか(つまり、目に見える要素、ちらつき、音などはありません)。

11
wilsonzlin

4つの主要なステップがあります。

  1. _<canvas>_要素と_<video>_要素を作成します。
  2. _URL.createObjectURL_によって生成されたビデオファイルのsrcを_<video>_要素にロードし、発生する特定のイベントをリッスンしてロードされるのを待ちます。
  3. ビデオの時間をスナップショットを撮りたいポイントに設定し、追加のイベントをリッスンします
  4. キャンバスを使用して画像を取得します。

ステップ1-要素を作成する

これは非常に簡単です。1つの_<canvas>_要素と1つの_<video>_要素を作成し、それらを_<body>_に追加するだけです(または実際にはどこでも問題ありません)。

_var canvasElem = $( '<canvas class="snapshot-generator"></canvas>' ).appendTo(document.body)[0];
var $video = $( '<video muted class="snapshot-generator"></video>' ).appendTo(document.body);
_

ビデオ要素には属性mutedがあることに注意してください。 autoplaycontrolsのような他の属性を入れないでください。また、どちらもクラス_snapshot-generator_を持っていることに注意してください。これは、両方のスタイルを設定して、邪魔にならないようにするためです。

_.snapshot-generator {
  display: block;
  height: 1px;
  left: 0;
  object-fit: contain;
  position: fixed;
  top: 0;
  width: 1px;
  z-index: -1;
}
_

一部のブラウザは_display: none_に設定して動作しますが、他のブラウザはページに表示されない限り深刻な問題が発生するため、基本的に非表示になるようにごくわずかにします。 (ただし、ビューポートの外に移動しないでください。移動すると、ページに見苦しいスクロールバーが表示される場合があります。)

ステップ2-ビデオをロードする

ここで物事がトリッキーになり始めます。いつ続行するかを知るには、イベントを聞く必要があります。ブラウザーが異なれば、異なるイベント、異なる時間、異なる順序で発生するため、労力を節約できます。ビデオの準備が整う前に常に少なくとも1回は発生する必要がある3つのイベントがあります。彼らです:

  • ロードされたメタデータ
  • ロードされたデータ
  • サスペンド

これらのイベントのイベントハンドラーを設定し、発生したイベントの数を追跡します。 3つすべてが起動したら、次に進む準備ができています。これらのイベントの一部は複数回発生する可能性があるため、発生した各タイプの最初のイベントのみを処理し、後続の発生を破棄する必要があることに注意してください。これを処理するjQueryの_.one_を使用しました。

_var step_2_events_fired = 0;
$video.one('loadedmetadata loadeddata suspend', function() {
    if (++step_2_events_fired == 3) {
        // Ready for next step
    }
}).prop('src', insert_source_here);
_

ソースは、URL.createObjectURL(file)を介して作成されたオブジェクトURLである必要があります。ここで、fileはファイルオブジェクトです。

ステップ3-時間を設定する

この段階は前の段階と似ています。時間を設定してから、イベントをリッスンします。前のコードのifブロック内:

_$video.one('seeked', function() {
    // Ready for next step
}).prop('currentTime', insert_time_here_in_seconds);
_

幸い、今回のイベントは1つだけなので、かなり明確で簡潔です。最後に...

ステップ4-スナップショットを取得する

この部分では、_<canvas>_要素を使用してスクリーンショットを取得しています。 seekedイベントハンドラー内:

_canvas_elem.height = this.videoHeight;
canvas_elem.width = this.videoWidth;
canvas_elem.getContext('2d').drawImage(this, 0, 0);
var snapshot = canvas_elem.toDataURL();

// Remove elements as they are no longer needed
$video.remove();
$(canvas_elem).remove();
_

適切な画像を取得するには、キャンバスがビデオのサイズと一致する必要があります(not_<video>_要素)。また、キャンバスの内部_.height_および_.width_プロパティを設定していますキャンバスの高さ/幅のCSSスタイル値ではありません。

スナップショットの値はデータURIであり、基本的には_data:image/jpeg;base64_で始まり、次にbase64データが続く文字列です。

最終的なJSコードは次のようになります。

_var step_2_events_fired = 0;
$video.one('loadedmetadata loadeddata suspend', function() {
  if (++step_2_events_fired == 3) {
    $video.one('seeked', function() {
      canvas_elem.height = this.videoHeight;
      canvas_elem.width = this.videoWidth;
      canvas_elem.getContext('2d').drawImage(this, 0, 0);
      var snapshot = canvas_elem.toDataURL();

      // Delete the elements as they are no longer needed
      $video.remove();
      $(canvas_elem).remove();
    }).prop('currentTime', insert_time_here_in_seconds);
  }
}).prop('src', insert_source_here);
_

祝う!

あなたはbase64にあなたのイメージを持っています!これをサーバーに送信し、_<img>_要素のsrcなどとして配置します。

たとえば、バイナリにデコードしてファイルに直接書き込むことができます(最初にプレフィックスを削除します)、これがJPEG画像ファイルになります。

これを使用して、アップロード中にビデオのプレビューを提供することもできます。 _<img>_のsrcとして配置する場合は、完全なデータURI(プレフィックスを削除しないでください)

25
wilsonzlin