通常の video 要素は動画の見る分には十分な機能が備わっていますが。細かいことをするには不十分です。特にフレーム回りは標準機能が全くといいほどないです。この状況に対して Firefox と MicrosoftEdge は独自実装のメソッドを生やすことで対応しています。
HTMLVideoElement – Web API | MDN#gecko_固有のプロパティ
HTMLVideoElement – Web API | MDN#microsoft_拡張
もう非推奨になってしまっていますが Firefox 限定のコマ送りメソッドもあります。
HTMLMediaElement.seekToNextFrame() – Web APIs | MDN
microsoft拡張ですが、いくらか試したり調べたところ InternetExplorer からの遺産であり Microsoft Edge が Chromium ベースになったあたりで相当数が消えていそうです。
Google Chrome について特に MDN に記述はなく Can I use 等の他でも見つけられませんでした。
そんな感じで未だ機能が不足しつつ、何が使えるのかも微妙なブラウザの動画機能ですが昨今らしい対応策が二つあります。一つは WebAssembly です。
WebAssembly はブラウザ上で使えるバイナリ言語です。このバイナリ言語は C/C++、Rust、Go などの JavaScript 以外の言語からもコンパイルでき、その言語界隈で積み上げられたライブラリ等の成果物をブラウザ上で使用できるようになります。例えば、FFmpeg のコードを WebAssembly 形式に出力でき、ブラウザ上でこれまで読むのが困難だったローカル動画ファイルのフレームやメタ情報といった部分(File オブジェクトから得られるバイナリを直にどうこうする必要があった)を FFmpeg 形式で簡易に読むことができます。
ffmpegwasm/ffmpeg.wasm: FFmpeg for browser and node, powered by WebAssembly
いい参考例がありませんが、本来ブラウザ上で再生不可能な形式の動画ファイルをそれ用の動画プレイヤーのコードを使った WebAssembly で再生することもできそうです。
もう一つは WebRTC です。WebRTC は Web Real-Time Communications の略です。これはビデオ会議の様に各ユーザーがリアルタイムにそれぞれのユーザーと音声と動画を送受信するための仕組みです。Zoom もこの仕組みを部分的に使っています。
WebRTC はその目的上、動画と音声のストリーミングを操作する機能に長けています。この動画を操作する機能を video 要素につなげることによって本来の video 要素には備わっていない処理ができるようになります。
JavaScript 上で WebRTC と video 要素をつなげるには次の様に HTMLMediaElement から継承した caputureStream メソッドを使います。例えば次で WebRTC のストリーミングから動画の現在のフレーム情報を取得する準備ができます。
/** video 要素 */
const videoEl = document.querySelector('video');
/** video 要素を WebRTC のストリーミングに変換 */
// @see https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement/captureStream
// @see https://developer.mozilla.org/ja/docs/Web/API/MediaStream
const videoStream = videoEl.captureStream();
// WebRTC のストリーミングから動画要素だけを抜き取り
// @see https://developer.mozilla.org/ja/docs/Web/API/MediaStream/getVideoTracks
// @see https://developer.mozilla.org/ja/docs/Web/API/MediaStreamTrack
const videoTrack = videoStream.getVideoTracks()[0];
// WebRTC のストリーミングの動画要素から読み取り用のストリームオブジェクトを得る
// @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrackProcessor
// @see https://developer.mozilla.org/ja/docs/Web/API/ReadableStream
const videoReadableStream = new MediaStreamTrackProcessor(videoTrack).readable;
// 読み取り用ストリームオブジェクトから流れているデータを読み取るためのオブジェクトを取得
// @see https://developer.mozilla.org/ja/docs/Web/API/ReadableStream/getReader
// @see https://developer.mozilla.org/ja/docs/Web/API/ReadableStreamDefaultReader
const videoReader = videoReadableStream.getReader();
チェーンが長いですが、これで準備が整います。この状態で次の様にすることで動画が現在参照しているフレームについての情報を得ることができる様になります。
// 先述の videoReader 変数の使いまわし
videoReader.read().then(e=>console.log(e))
/*
done: false,
value: VideoFrame
codedHeight: 368
codedRect: DOMRectReadOnly
bottom: 368
height: 368
left: 0
right: 640
top: 0
width: 640
X: 0
y: 0
[[Prototype]]: DOMRectReadOnly
codedWidth: 640
colorSpace: VideoColorSpace
fullRange: null
matrix: null
primaries: null
transfer: null
[[Prototype]]: VideoColorSpace
displayHeight: 360
displayWidth: 640
duration: 33367
format: "1420"
timestamp: 753030358
visibleRect: DOMRectReadOnly
bottom: 360
height: 360
left: 0
right: 640
top: 0
width: 640
X: 0
y: 0
[[Prototype]]: DOMRectReadOnly
[[Prototype]]: VideoFrame
[[Prototype]]: Object
*/
フレームの大きさやフレームの表示時刻が得られます。例ではフレームのために ReadableStreamDefaultReader まで進みましたが、途中で経由したオブジェクトからまた別の方向に分岐して別のメソッドやプロパティを使うことによってもっと別の様々なこともできそうです。
注意点として WebRTC 関連技術の実装段階はブラウザによってまちまちな点があります。サービスの対象とするブラウザとの兼ね合いは必須です。