JavaScript API VideoEncoder/Decoderの使い方
最近(?)追加された動画用のエンコード・デコードを行うAPIを使ってみました。キャンバスの画像(2フレーム)をエンコード(録画)、デコード(再生)するプログラムです。
ソースコード
export async function main(){
let canvas = document.getElementById("canvas");
if(!(canvas instanceof HTMLCanvasElement))throw canvas;
let ctx = canvas.getContext("2d");
if(!(ctx instanceof CanvasRenderingContext2D))throw ctx;
let width = canvas.width;
let height = canvas.height;
ctx.clearRect(0,0, width, height);
ctx.fillStyle = "white";
let encoder = new EncodeVideo();
let decoder = new DecodeVideo();
let vconfig = {
codec: "vp09.00.10.08",//prof0 lv1.0 8bit
// codec: "vp8",
width: width,
height: height,
bitrate: 5_000_000, //bps
framerate: 30,
};
encoder.configure(vconfig);
decoder.configure(vconfig);
// エンコード 録画
ctx.fillRect(width/4, height/4, width/2, height/2);
let keyChunk = await encoder.encode(0, canvas, true);//keyframe
ctx.clearRect(0,0, width, height);
let deltaChunk = await encoder.encode(16666, canvas, false);
console.log(keyChunk, deltaChunk)
// デコード 再生
let keyFrame = await decoder.decode(keyChunk);
let deltaFrame = await decoder.decode(deltaChunk);
console.log(keyFrame, deltaFrame)
for(let i=0;i<10;++i){
await new Promise<void>(rs=>setTimeout(rs,1000));
ctx.drawImage(keyFrame, 0, 0);
await new Promise<void>(rs=>setTimeout(rs,1000));
ctx.drawImage(deltaFrame, 0, 0);
}
deltaFrame.close();
keyFrame.close();
}
//古いTypeScriptは、VideoEncoderでエラー(定義未対応)
// 5.2.2を使用
export class EncodeVideo{
encoder : VideoEncoder;
signal : SignalWait;
constructor(){
let init : VideoEncoderInit = {
output: (chunk, metadata) => {
this.signal.signal(chunk);
},
error: (e) => {
console.log(e.message);
},
};
this.encoder = new VideoEncoder(init);
this.signal = new SignalWait();
}
// config
configure(config : VideoEncoderConfig){
this.encoder.configure(config);
}
// エンコード簡易版 終了まで待つ
async encode(timeUS : number, image : HTMLCanvasElement, keyframe : boolean){
let frame = new VideoFrame(image,{ timestamp: timeUS});
this.encoder.encode(frame, {keyFrame : keyframe});
let chunk = await this.signal.wait();
frame.close();
return chunk;
}
}
export class DecodeVideo{
decoder : VideoDecoder;
signal : SignalWait;
constructor(){
let init : VideoDecoderInit = {
output: (frame) => {
this.signal.signal(frame);
},
error: (e) => {
console.log(e.message);
},
};
this.decoder = new VideoDecoder(init);
this.signal = new SignalWait();
}
// config
configure(config : VideoEncoderConfig){
this.decoder.configure(config);
}
// デコード 簡易版 終了まで待つ
// 使い終わったVideoFrameはclose()を忘れずに
async decode(chunk : EncodedVideoChunk){
this.decoder.decode(chunk);
let frame = await this.signal.wait();
return frame;
}
}
export class SignalWait<T=any>{
resolve : any[];
constructor(){
this.resolve = [];
}
async wait() : Promise<T>{
return new Promise((rs)=>{this.resolve.push(rs);});
}
signal(val : T){
if(this.resolve !== undefined){
for(let rs of this.resolve){
rs(val);
}
this.resolve = [];
}
}
}
いいなと思ったら応援しよう!
![zerogram](https://assets.st-note.com/production/uploads/images/79406964/profile_52d439edf0ed5fbf453ca56d5f22a6d9.png?width=600&crop=1:1,smart)