video 要素はフレームをはじめとした動画のメタ情報を取得するには貧弱な要素です。
<video>: 動画埋め込み要素 – HTML: HyperText Markup Language | MDN
基本的にブラウザは動画を如何に再生するかに関心があるため、普段は困らないのですが稀にフレーム単位で色々やりたいという要望があります。そういった時、フレーム単位で処理できないのは困ります。こういった時、よくやるのはあらかじめサーバ側でそういったメタ情報を用意しておき、API 等で動画と一緒にブラウザで使う手法です。これで大体は解決しますが、ユーザが動画をブラウザ上において再生し、かつコマ送りをしたいなどといった時に困ります。FireFox ではこれを比較的簡潔に解決できます。
Firefox には seekToNextFrame というメソッドがあり、これはそのままコマ送りを行うメソッドです。これを用いることで連鎖的に FPS、動画全体のフレーム数を知れます。
HTMLMediaElement.seekToNextFrame() – Web APIs | MDN
“seekToNextFrame” | Can I use… Support tables for HTML5, CSS3, etc
これを用いたコード例が次です。
/**
* video 要素の動画のフレームについての情報を得る
* @params {HTMLVideoElement} videoEl
*/
const getVideoFrameMeta = (videoEl) => {
const tmpSec = videoEl.currentTime; // 元々の動画の位置を記憶
// 次のフレームの開始地点に移動
// p 秒から q 秒まで n フレーム目が表示される、の p 秒ぴったりに移動するイメージ
videoEl.seekToNextFrame();
const sSec = videoEl.currentTime; // n フレーム目の秒数を記憶
videoEl.seekToNextFrame(); // n + 1 フレーム目の開始地点に移動
const eSec = videoEl.currentTime; // n + 1 フレーム目の秒数を記憶
// 動画の再生位置を元に戻す
videoEl.currentTime = tmpSec;
// 固定フレームレートの動画ならこれでOK
// 可変ならこれでざっくり。いくつかのフレームを見て回って平均等の信頼できるサンプルを集めるのが良さそう
const fps = eSec - sSec; // 1フレーム分の秒数
const frameCount = videoEl.duration / fps; // 全体の動画長(秒) / 1フレームの長さ(秒) = フレーム数
return { fps, frameCount };
};
// 目的の video 要素を得るセレクタを使う
const videoEl = document.querySelector('video');
const frameMeta = getVideoFrameMeta(videoEl);
これを使ったデモが次です。
FireFox 限定ということで受託案件ではなかなか使えませんが、クライアント側のみでさっくり完結するという点は便利です。