JavaScript VideoDecoderを使った高機能動画再生プログラム
標準の<video>動画再生ではできない機能を実装した動画再生プログラムです。プレーヤーアプリではないので簡易的なUIのみの実装です。
高速&安定したシーク、複数動画の同期再生、オーディオ遅延補正、コマ送り戻し(ステップ再生)に対応、ストリーミング再生には対応していません(動画ファイルの再生のみ)。
ソースコードはすべて自作(オリジナル)、オープンソース、生成AIは使っていません。
対応ファイルとコーデック
動作環境
Windows Edge/Chrome
まだ一般的ではないAPIを使用しているため、これだけです。(MacOSのEdge/Chromeは未確認)
webmファイル
映像 : VP8 VP9
音声 : OPUS
mp4 movファイル
映像 : avc1
音声 : mp4a
※出力アプリやエンコーダーによって再生できないことがあります。
確認済みファイル
mp4 AVUTL 拡張x264出力
mp4 Win+G画面録画
mov iMovie ※2年前ぐらいに作成したもの
mov iPad画面収録 ※
webm JavaScript MediaRecorder
機能説明
高速&安定したシーク
<video>の動画再生はストリーミング用のためか、シークを連続して行うと停止したりシークに時間が掛かることがあります(edge chrome safari)。
シークを連続発行しても、処理が渋滞しないように古いシーク処理を素早く中断させることで、高速&安定したシーク処理を実現しています。
同期再生
<video>では時間(時刻)を指定した再生はできません。同時にplayしてもずれます(ランダム)。
再生時に時刻を指定することで複数の動画/音声を同期再生可能です。
同期するための時計はWeb Audio APIの時刻を利用しています。
ブラウザ(JavaScript)の仕様により、精度はms単位
オーディオ遅延補正
映像と音のずれを補正できます。
音声だけ早く再生を開始することで実現しています。
コマ送り戻し
意外と実装されない機能、音ゲーの練習のときに欲しかったので作成しました。進む方向は高速、戻りは低速(毎回シーク処理)。
使用例(ソースコード)
動画再生クラスの提供です。簡易的なUIのみ。DIY。
処理の流れ
ファイルの部分アクセスを行うインターフェイスを用意
動画クラスの作成(new)
load処理 引数に読み込みインターフェイス
seek / play / pause
画像は指定したキャンバスに出力
音声はWeb Audio APIで再生
export async function main(){
let canvas = <HTMLCanvasElement>document.getElementById("canvas");
let ctx = <CanvasRenderingContext2D>canvas.getContext("2d");
let canvas2 = <HTMLCanvasElement>document.getElementById("canvas2");
let ctx2 = <CanvasRenderingContext2D>canvas2.getContext("2d");
let seek = <HTMLInputElement>document.getElementById("seek");
let inc = <HTMLButtonElement>document.getElementById("inc");
let dec = <HTMLButtonElement>document.getElementById("dec");
let btnPlay = <HTMLButtonElement>document.getElementById("btn-play");
let video = <HTMLVideoElement>document.getElementById("video");
let video2 = <HTMLVideoElement>document.getElementById("video2");
// ローカルファイル
let sigFile = new SignalWait<File>();
let inputFile = <HTMLInputElement>document.getElementById("file");
inputFile.oninput = ()=>{
if(inputFile.files && inputFile.files[0]){
sigFile.signal(inputFile.files[0])
}
inputFile.oninput = null;
inputFile.remove();
}
let blob = await sigFile.wait();
let file = new BlobBinaryReadIF(blob);
// // fetchで部分アクセス
// let file = new FetchFile();
// await file.open("./video.webm");
// // fetch>blob
// let blob = await (await fetch("./video.webm")).blob();
// let readeIF = new BlobBinaryReadIF(blob);
let player = new VideoPlayer();
// オーディオ遅延補正
player.latencyAudio = player.latencyAudioOutput;
btnPlay.onclick = ()=>{
player.resume();//やらなくてもいいような UI操作したらオーディオ出力有効?
if(player.playing){
player.pause();
player.waitTask().then(()=>{
//console.log("pause")
})
}else{
player.play();
player.waitTask().then(()=>{
//console.log("play")
})
}
}
player.setTarget(ctx);
await player.load(file);
player.volume = 0.7;//音量
inc.onclick = ()=>{
player.nextFrame();
}
dec.onclick = ()=>{
player.prevFrame();
}
seek.min = "0";
seek.max = "" + player.duration;
seek.step = "0.1";
player.onTimeUpdate = (timeSec)=>{
seek.value = "" + timeSec;
}
seek.oninput = ()=>{
let time = parseFloat(seek.value);
player.seek(time);
}
}
2つの動画を同期再生
// 同じAudioContextを使用する(同じ時計を参照)
let ctxAudio = new WebAudioContext;
let file1 = new FetchFile();
await file1.open("./video.webm");
let player1 = new VideoPlayer(ctxAudio);
player1.setTarget(ctx);
await player1.load(file1);
let file2 = new FetchFile();
await file2.open("./video.webm");
let player2 = new VideoPlayer(ctxAudio);
player2.setTarget(ctx2);
await player2.load(file2);
await player1.waitSeek();
await player2.waitSeek();
// 0.5秒後に再生開始
let time = 0.5 + player1.getClockTimeSec();
player1.play(time)
player2.play(time)
ソースコード
すぐに利用できるjsファイルとTypeScriptのプロジェクトをまとめたzipファイルです。VIdeoDecoderを使用するためには、HTTPSのセキュア通信かlocalhost接続が必要になります。
ダウンロード警告が
ブラウザのセキュリティー設定によってダウンロードできない場合があるようです。他のブラウザを使用するか、一時的にセキュリティー設定を変更してダウンロードしてください。
localhost接続(オフライン)を前提にしたプログラムなので問題はないと思いますが、念のため実行前にソースコードの内容を確認をしてください。
ここから先は
¥ 1,500
この記事が役に立ったという方は、サポートお願いします。今後の製作の励みになります。