ãã©ãŠã¶ã§é«å質ãªãããªã¹ããªãŒãã³ã°ãå®çŸãWebCodecs APIãšVideoFrameæäœã䜿çšããŠããã€ãºãªãã¯ã·ã§ã³ã®ããã®é«åºŠãªæéçãã£ã«ã¿ãªã³ã°ãå®è£ ããæ¹æ³ãåŠã³ãŸãã
WebCodecsããã¹ã¿ãŒããïŒæéçãã€ãºãªãã¯ã·ã§ã³ã«ãããããªå質ã®åäž
ãŠã§ãããŒã¹ã®ãããªã³ãã¥ãã±ãŒã·ã§ã³ãã¹ããªãŒãã³ã°ããªã¢ã«ã¿ã€ã ã¢ããªã±ãŒã·ã§ã³ã®äžçã§ã¯ãå質ãæãéèŠã§ããäžçäžã®ãŠãŒã¶ãŒã¯ãããžãã¹äŒè°ãã©ã€ãã€ãã³ãã®èŠèŽããªã¢ãŒããµãŒãã¹ãšã®å¯Ÿè©±ãªã©ãã©ã®ãããªç¶æ³ã§ãé®®æã§ã¯ãªã¢ãªãããªãæåŸ ããŠããŸãããããããããªã¹ããªãŒã ã¯ãã°ãã°ãæç¶çã§éªéãªã¢ãŒãã£ãã¡ã¯ãã§ãããã€ãºã«æ©ãŸãããŸãããã®ããžã¿ã«ãã€ãºã¯ããã°ãã°ããããããããŸãã¯éçãªãã¯ã¹ãã£ãšããŠèŠããèŠèŽäœéšãäœäžãããé©ãã¹ãããšã«åž¯åå¹ ã®æ¶è²»ãå¢å ãããå¯èœæ§ããããŸãã幞ããªããšã«ã匷åãªãã©ãŠã¶APIã§ããWebCodecsã¯ãéçºè ã«ãã®åé¡ã«æ£é¢ããåãçµãããã®åäŸã®ãªãäœã¬ãã«ã®å¶åŸ¡ãæäŸããŸãã
ãã®å
æ¬çãªã¬ã€ãã§ã¯ãç¹å®ã®ã圱é¿ã®å€§ãããããªåŠçæè¡ã§ããæéçãã€ãºãªãã¯ã·ã§ã³ã«WebCodecsã䜿çšããæ¹æ³ãæ·±ãæãäžããŠãããŸãããããªãã€ãºãšã¯äœãããªããããæå®³ãªã®ãããããŠVideoFrame
ãªããžã§ã¯ããæŽ»çšããŠãã©ãŠã¶ã§çŽæ¥ãã£ã«ã¿ãªã³ã°ãã€ãã©ã€ã³ãæ§ç¯ããæ¹æ³ãæ¢ããŸããåºæ¬çãªçè«ããå®è·µçãªJavaScriptå®è£
ãWebAssemblyã«ããããã©ãŒãã³ã¹ã®èæ
®äºé
ããããŠãããã§ãã·ã§ãã«ã°ã¬ãŒãã®çµæãéæããããã®é«åºŠãªæŠå¿µãŸã§ããã¹ãŠãã«ããŒããŸãã
ãããªãã€ãºãšã¯äœãããããŠãªããããéèŠãªã®ãïŒ
åé¡ã解決ããåã«ããŸããããçè§£ããªããã°ãªããŸãããããžã¿ã«ãããªã«ãããŠããã€ãºãšã¯ãããªä¿¡å·ã®èŒåºŠãè²æ å ±ã®ã©ã³ãã ãªå€åãæããŸããããã¯ãç»åãã£ããã£ããã³äŒéããã»ã¹ã®æãŸãããªãå¯ç£ç©ã§ãã
ãã€ãºã®åå ãšçš®é¡
- ã»ã³ãµãŒãã€ãºïŒ äž»ãªåå ã§ããäœç §åºŠæ¡ä»¶äžã§ã¯ãã«ã¡ã©ã»ã³ãµãŒã¯å ¥åä¿¡å·ãå¢å¹ ããŠååã«æããç»åãçæããŸãããã®å¢å¹ ããã»ã¹ã¯ãã©ã³ãã ãªé»åçå€åãå¢å¹ ããç®ã«èŠããç²åæïŒã°ã¬ã€ã³ïŒãçããããŸãã
- ç±ãã€ãºïŒ ã«ã¡ã©ã®é»åæ©åšã«ãã£ãŠçºçããç±ãé»åã®ã©ã³ãã ãªåããåŒãèµ·ãããå éãšã¯ç¡é¢ä¿ãªãã€ãºãçæããããšããããŸãã
- éååãã€ãºïŒ ã¢ããã°ããããžã¿ã«ãžã®å€æããã³å§çž®ããã»ã¹äžã«å°å ¥ãããé£ç¶çãªå€ãéããã颿£ã¬ãã«ã®ã»ããã«ãããã³ã°ãããéã«çºçããŸãã
ãã®ãã€ãºã¯éåžžãã¬ãŠã¹ãã€ãºãšããŠçŸããåãã¯ã»ã«ã®åŒ·åºŠããã®çã®å€ã®åšãã§ã©ã³ãã ã«å€åãããã¬ãŒã å šäœã«åŸ®çްã§ã¡ãã€ããããªç²åæãçã¿åºããŸãã
ãã€ãºã®äºéã®åœ±é¿
ãããªãã€ãºã¯åãªãèŠãç®ã®åé¡ã§ã¯ãããŸãããããã¯éå€§ãªæè¡çããã³ç¥èŠçãªçµæããããããŸãïŒ
- ãŠãŒã¶ãŒäœéšã®äœäžïŒ æãæçœãªåœ±é¿ã¯èŠèŠçãªå質ã§ãããã€ãºã®å€ããããªã¯ãããã§ãã·ã§ãã«ã«èŠãããæ°ãæ£ãããéèŠãªè©³çްãèå¥ããã®ãå°é£ã«ããå¯èœæ§ããããŸãããã¬ãäŒè°ã®ãããªã¢ããªã±ãŒã·ã§ã³ã§ã¯ãåå è ãããããããŠäžé®®æã«èŠããååšæãæãªãããšããããŸãã
- å§çž®å¹çã®äœäžïŒ ããã¯çŽæçã§ã¯ãããŸããããåæ§ã«éèŠãªåé¡ã§ããçŸä»£ã®ãããªã³ãŒããã¯ïŒH.264ãVP9ãAV1ãªã©ïŒã¯ãåé·æ§ãå©çšããŠé«ãå§çž®çãéæããŸãããããã¯ãã¬ãŒã éïŒæéçåé·æ§ïŒããã³åäžãã¬ãŒã å ïŒç©ºéçåé·æ§ïŒã®é¡äŒŒæ§ãæ¢ããŸãããã€ãºã¯ãã®æ§è³ªäžãã©ã³ãã ã§äºæž¬äžå¯èœã§ããããã¯ãããã®åé·æ§ã®ãã¿ãŒã³ãç Žå£ããŸãããšã³ã³ãŒããŒã¯ã©ã³ãã ãªãã€ãºãä¿åããªããã°ãªããªãé«åšæ³¢ã®è©³çްãšèŠãªããå®éã®ã³ã³ãã³ãã®ä»£ããã«ãã€ãºããšã³ã³ãŒãããããã«ããå€ãã®ããããå²ãåœãŠãããšã匷å¶ãããŸããããã«ãããåãç¥èŠå質ã§ãã¡ã€ã«ãµã€ãºã倧ãããªãããåããããã¬ãŒãã§å質ãäœäžãããã®ã©ã¡ããã«ãªããŸãã
ãšã³ã³ãŒãåã«ãã€ãºãé€å»ããããšã§ããããªä¿¡å·ãããäºæž¬å¯èœã«ãããšã³ã³ãŒããŒãããå¹ççã«åäœã§ããããã«ããŸããããã«ãããããè¯ãèŠèŠå質ãããäœã垯åå¹ ã®äœ¿çšããããŠäžçäžã®ãŠãŒã¶ãŒã«ãšã£ãŠããã¹ã ãŒãºãªã¹ããªãŒãã³ã°äœéšãããããããŸãã
WebCodecsã®ç»å ŽïŒäœã¬ãã«ãããªå¶åŸ¡ã®å
é·å¹Žããã©ãŠã¶ã§ã®çŽæ¥çãªãããªæäœã¯éãããŠããŸãããéçºè
ã¯äž»ã«<video>
èŠçŽ ãšCanvas APIã®æ©èœã«å¶çŽãããŠããããããã¯ãã°ãã°GPUããã®ããã©ãŒãã³ã¹ãèããäœäžããããªãŒãããã¯ã䌎ããŸãããWebCodecsã¯ããã®ç¶æ³ãå®å
šã«å€ããŸãã
WebCodecsã¯ããã©ãŠã¶ã«çµã¿èŸŒãŸããã¡ãã£ã¢ãšã³ã³ãŒããŒãšãã³ãŒããŒã«çŽæ¥ã¢ã¯ã»ã¹ã§ããäœã¬ãã«APIã§ããããã¯ããããªãšãã£ã¿ãã¯ã©ãŠãã²ãŒãã³ã°ãã©ãããã©ãŒã ãé«åºŠãªãªã¢ã«ã¿ã€ã ã³ãã¥ãã±ãŒã·ã§ã³ã¯ã©ã€ã¢ã³ããªã©ãã¡ãã£ã¢åŠçã«å¯Ÿãã粟å¯ãªå¶åŸ¡ãå¿ èŠãšããã¢ããªã±ãŒã·ã§ã³åãã«èšèšãããŠããŸãã
ç§ãã¡ãçŠç¹ãåœãŠãã³ã¢ã³ã³ããŒãã³ãã¯VideoFrame
ãªããžã§ã¯ãã§ããVideoFrame
ã¯ãããªã®åäžãã¬ãŒã ãç»åãšããŠè¡šçŸããŸãããåãªããããããã以äžã®ãã®ã§ããããã¯ãæ§ã
ãªãã¯ã»ã«ãã©ãŒãããïŒRGBAãI420ãNV12ãªã©ïŒã§ãããªããŒã¿ãä¿æã§ãã以äžã®ãããªéèŠãªã¡ã¿ããŒã¿ãéã¶ãéåžžã«å¹ççã§è»¢éå¯èœãªãªããžã§ã¯ãã§ãïŒ
timestamp
: ãã¬ãŒã ã®è¡šç€ºæéïŒãã€ã¯ãç§åäœïŒãduration
: ãã¬ãŒã ã®æç¶æéïŒãã€ã¯ãç§åäœïŒãcodedWidth
ãšcodedHeight
: ãã¬ãŒã ã®å¯žæ³ïŒãã¯ã»ã«åäœïŒãformat
: ããŒã¿ã®ãã¯ã»ã«ãã©ãŒãããïŒäŸïŒ'I420'ã'RGBA'ïŒã
éèŠãªããšã«ãVideoFrame
ã¯copyTo()
ãšããã¡ãœãããæäŸããŠãããããã«ããçã®éå§çž®ãã¯ã»ã«ããŒã¿ãArrayBuffer
ã«ã³ããŒããããšãã§ããŸããããããåæãšæäœã®ããã®ç§ãã¡ã®ãšã³ããªãŒãã€ã³ãã§ããçã®ãã€ãããŒã¿ãååŸãããããã€ãºãªãã¯ã·ã§ã³ã¢ã«ãŽãªãºã ãé©çšãã倿ŽãããããŒã¿ããæ°ããVideoFrame
ãæ§ç¯ããŠãåŠçãã€ãã©ã€ã³ã®ããã«äžæµïŒäŸãã°ããããªãšã³ã³ãŒããŒããã£ã³ãã¹ïŒã«æž¡ãããšãã§ããŸãã
æéçãã£ã«ã¿ãªã³ã°ã®çè§£
ãã€ãºãªãã¯ã·ã§ã³æè¡ã¯ã倧ããåããŠç©ºéçãšæéçã®2çš®é¡ã«åé¡ã§ããŸãã
- 空éçãã£ã«ã¿ãªã³ã°ïŒ ãã®æè¡ã¯åäžã®ãã¬ãŒã ãåç¬ã§æäœããŸãã飿¥ãããã¯ã»ã«éã®é¢ä¿ãåæããŠãã€ãºãç¹å®ããæ»ããã«ããŸããç°¡åãªäŸã¯ãŒãããã£ã«ã¿ãŒã§ãããã€ãºã®äœæžã«ã¯å¹æçã§ããã空éãã£ã«ã¿ãŒã¯éèŠãªãã£ããŒã«ããšããžããŒãããŠããŸããç»åãã·ã£ãŒãã§ãªããªãå¯èœæ§ããããŸãã
- æéçãã£ã«ã¿ãªã³ã°ïŒ ãããç§ãã¡ãçŠç¹ãåœãŠãŠãããããæŽç·Žãããã¢ãããŒãã§ããããã¯æéãéããŠè€æ°ã®ãã¬ãŒã ã«ããã£ãŠæäœããŸããåºæ¬çãªååã¯ãå®éã®ã·ãŒã³ã®ã³ã³ãã³ãã¯ãã¬ãŒã ããæ¬¡ã®ãã¬ãŒã ãžãšçžé¢ããŠããå¯èœæ§ãé«ãã®ã«å¯Ÿãããã€ãºã¯ã©ã³ãã ã§çžé¢ããªããšããããšã§ããç¹å®ã®å Žæã«ãããã¯ã»ã«ã®å€ãè€æ°ã®ãã¬ãŒã ã«ããã£ãŠæ¯èŒããããšã§ãäžè²«ããä¿¡å·ïŒå®éã®ç»åïŒãšã©ã³ãã ãªå€åïŒãã€ãºïŒãåºå¥ã§ããŸãã
æéçãã£ã«ã¿ãªã³ã°ã®æãåçŽãªåœ¢åŒã¯æéçå¹³ååã§ããçŸåšã®ãã¬ãŒã ãšåã®ãã¬ãŒã ããããšæ³åããŠãã ãããã©ã®ãã¯ã»ã«ã«ã€ããŠãããã®ãçã®ãå€ã¯çŸåšã®ãã¬ãŒã ã®å€ãšåã®ãã¬ãŒã ã®å€ã®éã®ã©ããã«ããå¯èœæ§ãé«ãã§ãããããããã¬ã³ãããããšã§ãã©ã³ãã ãªãã€ãºãå¹³ååããããšãã§ããŸããæ°ãããã¯ã»ã«å€ã¯ãåçŽãªå éå¹³åã§èšç®ã§ããŸãïŒ
new_pixel = (alpha * current_pixel) + ((1 - alpha) * previous_pixel)
ããã§ãalpha
ã¯0ãã1ã®éã®ãã¬ã³ãä¿æ°ã§ããalpha
ãé«ãã»ã©çŸåšã®ãã¬ãŒã ãããä¿¡é Œããããšãæå³ãããã€ãºãªãã¯ã·ã§ã³ã¯å°ãªããªããŸãããã¢ãŒã·ã§ã³ã¢ãŒãã£ãã¡ã¯ããå°ãªããªããŸããalpha
ãäœãã»ã©åŒ·åãªãã€ãºãªãã¯ã·ã§ã³ãæäŸããŸãããåãã®ããé åã§ããŽãŒã¹ãã£ã³ã°ããæ®åãåŒãèµ·ããå¯èœæ§ããããŸããé©åãªãã©ã³ã¹ãèŠã€ããããšãéµã§ãã
åçŽãªæéçå¹³ååãã£ã«ã¿ãŒã®å®è£
ãã®æŠå¿µãWebCodecsã䜿ã£ãŠå®è·µçã«å®è£ ããŠã¿ãŸããããç§ãã¡ã®ãã€ãã©ã€ã³ã¯ãäž»ã«3ã€ã®ã¹ãããã§æ§æãããŸãïŒ
VideoFrame
ãªããžã§ã¯ãã®ã¹ããªãŒã ãååŸããŸãïŒäŸïŒãŠã§ãã«ã¡ã©ããïŒã- åãã¬ãŒã ã«å¯ŸããŠãåã®ãã¬ãŒã ã®ããŒã¿ã䜿çšããŠæéçãã£ã«ã¿ãŒãé©çšããŸãã
- æ°ããã¯ãªãŒã³ã¢ããããã
VideoFrame
ãäœæããŸãã
ã¹ããã1ïŒãã¬ãŒã ã¹ããªãŒã ã®ã»ããã¢ãã
VideoFrame
ãªããžã§ã¯ãã®ã©ã€ãã¹ããªãŒã ãååŸããæãç°¡åãªæ¹æ³ã¯ãMediaStreamTrackProcessor
ã䜿çšããããšã§ããããã¯MediaStreamTrack
ïŒgetUserMedia
ããã®ãã®ãªã©ïŒãæ¶è²»ãããã®ãã¬ãŒã ãèªã¿åãå¯èœãªã¹ããªãŒã ãšããŠå
¬éããŸãã
æŠå¿µçãªJavaScriptã®ã»ããã¢ããïŒ
async function setupVideoStream() {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const track = stream.getVideoTracks()[0];
const trackProcessor = new MediaStreamTrackProcessor({ track });
const reader = trackProcessor.readable.getReader();
let previousFrameBuffer = null;
let previousFrameTimestamp = -1;
while (true) {
const { value: frame, done } = await reader.read();
if (done) break;
// ããã§å 'frame' ãåŠçããŸã
const processedFrame = await applyTemporalFilter(frame, previousFrameBuffer);
// 次ã®ã€ãã¬ãŒã·ã§ã³ã®ããã«ã*ãªãªãžãã«*ã®çŸåšã®ãã¬ãŒã ã®ããŒã¿ãä¿åããå¿
èŠããããŸã
// ããã§ãéããåã«ãªãªãžãã«ã®ãã¬ãŒã ããŒã¿ã 'previousFrameBuffer' ã«ã³ããŒããŸãã
// ã¡ã¢ãªãè§£æŸããããã«ãã¬ãŒã ãéããã®ãå¿ããªãã§ãã ããïŒ
frame.close();
// processedFrame ã§äœããè¡ããŸãïŒäŸïŒãã£ã³ãã¹ãžã®æç»ããšã³ã³ãŒãïŒ
// ...ãããŠããããéããŠãã ããïŒ
processedFrame.close();
}
}
ã¹ããã2ïŒãã£ã«ã¿ãªã³ã°ã¢ã«ãŽãªãºã - ãã¯ã»ã«ããŒã¿ã®æäœ
ãããç§ãã¡ã®äœæ¥ã®äžå¿ã§ããapplyTemporalFilter
颿°å
ã§ãå
¥åãã¬ãŒã ã®ãã¯ã»ã«ããŒã¿ã«ã¢ã¯ã»ã¹ããå¿
èŠããããŸããç°¡åã«ããããã«ããã¬ãŒã ã'RGBA'ãã©ãŒãããã§ãããšä»®å®ããŸããããåãã¯ã»ã«ã¯4ãã€ãã§è¡šçŸãããŸãïŒèµ€ãç·ãéããããŠã¢ã«ãã¡ïŒéæåºŠïŒã
async function applyTemporalFilter(currentFrame, previousFrameBuffer) {
// ãã¬ã³ãä¿æ°ãå®çŸ©ããŸãã0.8ã¯æ°ãããã¬ãŒã ã®80%ãšå€ããã¬ãŒã ã®20%ãæå³ããŸãã
const alpha = 0.8;
// 寞æ³ãååŸããŸã
const width = currentFrame.codedWidth;
const height = currentFrame.codedHeight;
// çŸåšã®ãã¬ãŒã ã®ãã¯ã»ã«ããŒã¿ãä¿æããããã®ArrayBufferãå²ãåœãŠãŸãã
const currentFrameSize = width * height * 4; // RGBAã®å Žåããã¯ã»ã«ããã4ãã€ã
const currentFrameBuffer = new Uint8Array(currentFrameSize);
await currentFrame.copyTo(currentFrameBuffer);
// ãããæåã®ãã¬ãŒã ã§ããå Žåããã¬ã³ãããåã®ãã¬ãŒã ã¯ãããŸããã
// ãã®ãŸãŸè¿ããŸãããæ¬¡ã®ã€ãã¬ãŒã·ã§ã³ã®ããã«ãã®ãããã¡ãä¿åããŸãã
if (!previousFrameBuffer) {
const newFrameBuffer = new Uint8Array(currentFrameBuffer);
// ãã®é¢æ°ã®å€ã§ãã°ããŒãã«ãª 'previousFrameBuffer' ãããã§æŽæ°ããŸãã
return { buffer: newFrameBuffer, frame: currentFrame };
}
// åºåãã¬ãŒã çšã«æ°ãããããã¡ãäœæããŸãã
const outputFrameBuffer = new Uint8Array(currentFrameSize);
// ã¡ã€ã³ã®åŠçã«ãŒãã
for (let i = 0; i < currentFrameSize; i++) {
const currentPixelValue = currentFrameBuffer[i];
const previousPixelValue = previousFrameBuffer[i];
// åã«ã©ãŒãã£ã³ãã«ã«æéçå¹³ååã®åŒãé©çšããŸãã
// ã¢ã«ãã¡ãã£ã³ãã«ïŒ4ãã€ãããšïŒã¯ã¹ãããããŸãã
if ((i + 1) % 4 !== 0) {
outputFrameBuffer[i] = Math.round(alpha * currentPixelValue + (1 - alpha) * previousPixelValue);
} else {
// ã¢ã«ãã¡ãã£ã³ãã«ã¯ãã®ãŸãŸã«ããŸãã
outputFrameBuffer[i] = currentPixelValue;
}
}
return { buffer: outputFrameBuffer, frame: currentFrame };
}
YUVãã©ãŒãããïŒI420, NV12ïŒã«ã€ããŠã®æ³šæïŒ RGBAã¯çè§£ããããã§ãããã»ãšãã©ã®ãããªã¯å¹çã®ããã«ãã€ãã£ãã«YUVè²ç©ºéã§åŠçãããŸããYUVã®æ±ãã¯ããè€éã§ãè²ïŒU, VïŒãšèŒåºŠïŒYïŒã®æ å ±ãå¥ã ã«ïŒããã¬ãŒã³ãã«ïŒä¿åãããŸãããã£ã«ã¿ãªã³ã°ã®ããžãã¯ã¯åãã§ãããåãã¬ãŒã³ïŒY, U, VïŒãå¥ã ã«å埩åŠçããããããã®å¯žæ³ïŒã«ã©ãŒãã¬ãŒã³ã¯ãã°ãã°è§£å床ãäœããã¯ãããµããµã³ããªã³ã°ãšåŒã°ããæè¡ïŒã«æ³šæããå¿ èŠããããŸãã
ã¹ããã3ïŒæ°ãããã£ã«ã¿ãªã³ã°æžã¿`VideoFrame`ã®äœæ
ã«ãŒããçµäºããåŸãoutputFrameBuffer
ã«ã¯æ°ããã¯ãªãŒã³ã«ãªã£ããã¬ãŒã ã®ãã¯ã»ã«ããŒã¿ãå«ãŸããŠããŸãããããæ°ããVideoFrame
ãªããžã§ã¯ãã§ã©ããããå
ã®ãã¬ãŒã ããã¡ã¿ããŒã¿ãã³ããŒããããšã確èªããå¿
èŠããããŸãã
// applyTemporalFilterãåŒã³åºããåŸã®ã¡ã€ã³ã«ãŒãå
ã§...
const { buffer: processedBuffer, frame: originalFrame } = await applyTemporalFilter(frame, previousFrameBuffer);
// åŠçããããããã¡ããæ°ããVideoFrameãäœæããŸãã
const newFrame = new VideoFrame(processedBuffer, {
format: 'RGBA',
codedWidth: originalFrame.codedWidth,
codedHeight: originalFrame.codedHeight,
timestamp: originalFrame.timestamp,
duration: originalFrame.duration
});
// éèŠïŒæ¬¡ã®ã€ãã¬ãŒã·ã§ã³ã®ããã«åã®ãã¬ãŒã ãããã¡ãæŽæ°ããŸãã
// ãã£ã«ã¿ãªã³ã°ãããããŒã¿ã§ã¯ãªãã*ãªãªãžãã«*ã®ãã¬ãŒã ããŒã¿ãã³ããŒããå¿
èŠããããŸãã
// ãã£ã«ã¿ãªã³ã°ã®åã«å¥ã®ã³ããŒãäœæããå¿
èŠããããŸãã
previousFrameBuffer = new Uint8Array(originalFrameData);
// ããã§ 'newFrame' ã䜿çšã§ããŸããæç»ãããããšã³ã³ãŒããããããŸãã
// renderer.draw(newFrame);
// ãããŠéèŠãªããšãšããŠãã¡ã¢ãªãªãŒã¯ãé²ãããã«ã䜿ãçµãã£ããéããŠãã ããã
newFrame.close();
ã¡ã¢ãªç®¡çã¯éåžžã«éèŠã§ãïŒ VideoFrame
ãªããžã§ã¯ãã¯å€§éã®éå§çž®ãããªããŒã¿ãä¿æã§ããJavaScriptããŒãå€ã®ã¡ã¢ãªã«è£æã¡ãããŠããå ŽåããããŸãã䜿ãçµãã£ããã¹ãŠã®ãã¬ãŒã ã§frame.close()
ãåŒã³åºãå¿
èŠããããŸãããããæ ããšãããã«ã¡ã¢ãªãæ¯æžããã¿ããã¯ã©ãã·ã¥ããŸãã
ããã©ãŒãã³ã¹ã«é¢ããèæ ®äºé ïŒJavaScript vs. WebAssembly
äžèšã®çŽç²ãªJavaScriptå®è£ ã¯åŠç¿ããã¢ã³ã¹ãã¬ãŒã·ã§ã³ã«ã¯æé©ã§ãããããã30 FPSã1080pïŒ1920x1080ïŒã®ãããªã§ã¯ãç§ãã¡ã®ã«ãŒãã¯æ¯ç§2å4800äžå以äžã®èšç®ãå®è¡ããå¿ èŠããããŸãïŒïŒ1920 * 1080 * 4ãã€ã * 30 fpsïŒãçŸä»£ã®JavaScriptãšã³ãžã³ã¯éåžžã«é«éã§ããããã®ãã¯ã»ã«ããšã®åŠçã¯ãããããã©ãŒãã³ã¹æåã®æè¡ã§ããWebAssembly (Wasm)ã®å®ç§ãªãŠãŒã¹ã±ãŒã¹ã§ãã
WebAssemblyã¢ãããŒã
WebAssemblyã䜿çšãããšãC++ãRustãGoãªã©ã®èšèªã§æžãããã³ãŒãããã©ãŠã¶ã§ã»ãŒãã€ãã£ãã®é床ã§å®è¡ã§ããŸããç§ãã¡ã®æéçãã£ã«ã¿ãŒã®ããžãã¯ã¯ããããã®èšèªã§ç°¡åã«å®è£ ã§ããŸããå ¥åãããã¡ãšåºåãããã¡ãžã®ãã€ã³ã¿ãåãåããåãå埩çãªãã¬ã³ãæäœãå®è¡ãã颿°ãäœæããŸãã
Wasmçšã®æŠå¿µçãªC++颿°ïŒ
extern "C" {
void apply_temporal_filter(unsigned char* current_frame, unsigned char* previous_frame, unsigned char* output_frame, int buffer_size, float alpha) {
for (int i = 0; i < buffer_size; ++i) {
if ((i + 1) % 4 != 0) { // ã¢ã«ãã¡ãã£ã³ãã«ãã¹ããã
output_frame[i] = (unsigned char)(alpha * current_frame[i] + (1.0 - alpha) * previous_frame[i]);
} else {
output_frame[i] = current_frame[i];
}
}
}
}
JavaScriptåŽããã¯ããã®ã³ã³ãã€ã«ãããWasmã¢ãžã¥ãŒã«ãããŒãããŸããäž»èŠãªããã©ãŒãã³ã¹äžã®å©ç¹ã¯ãã¡ã¢ãªãå
±æããããšããåŸãããŸããJavaScriptã§Wasmã¢ãžã¥ãŒã«ã®ãªãã¢ã¡ã¢ãªã«è£æã¡ãããArrayBuffer
ãäœæã§ããŸããããã«ãããé«äŸ¡ãªã³ããŒãªãã§ãã¬ãŒã ããŒã¿ãWasmã«æž¡ãããšãã§ããŸãããã®åŸããã¯ã»ã«åŠçã«ãŒãå
šäœãåäžã®é«åºŠã«æé©åãããWasm颿°åŒã³åºããšããŠå®è¡ãããJavaScriptã®`for`ã«ãŒãããã倧å¹
ã«é«éã«ãªããŸãã
é«åºŠãªæéçãã£ã«ã¿ãªã³ã°æè¡
åçŽãªæéçå¹³ååã¯çŽ æŽãããåºçºç¹ã§ãããã¢ãŒã·ã§ã³ãã©ãŒãããŽãŒã¹ãã£ã³ã°ããå°å ¥ãããšããéå€§ãªæ¬ ç¹ããããŸãããªããžã§ã¯ããåããšãçŸåšã®ãã¬ãŒã ã®ãã¯ã»ã«ãåã®ãã¬ãŒã ã®èæ¯ãã¯ã»ã«ãšãã¬ã³ããããæ®åãäœæãããŸããçã«ãããã§ãã·ã§ãã«ã°ã¬ãŒãã®ãã£ã«ã¿ãŒãæ§ç¯ããã«ã¯ãåããèæ ®ã«å ¥ããå¿ èŠããããŸãã
åãè£åæéçãã£ã«ã¿ãªã³ã° (MCTF)
æéçãã€ãºãªãã¯ã·ã§ã³ã®ãŽãŒã«ãã¹ã¿ã³ããŒãã¯ãåãè£åæéçãã£ã«ã¿ãªã³ã°ã§ããMCTFã¯ããã¯ã»ã«ãåã®ãã¬ãŒã ã®åã(x, y)座æšã®ãã¯ã»ã«ãšç²ç®çã«ãã¬ã³ããã代ããã«ããŸããã®ãã¯ã»ã«ãã©ãããæ¥ãããçªãæ¢ããããšããŸãã
ããã»ã¹ã«ã¯ä»¥äžãå«ãŸããŸãïŒ
- åãæšå®ïŒ ã¢ã«ãŽãªãºã ã¯çŸåšã®ãã¬ãŒã ããããã¯ïŒäŸïŒ16x16ãã¯ã»ã«ïŒã«åå²ããŸããåãããã¯ã«ã€ããŠãåã®ãã¬ãŒã ãæ€çŽ¢ããŠæãé¡äŒŒãããããã¯ïŒäŸïŒçµ¶å¯Ÿå·®ã®åèšãæãäœãïŒãèŠã€ããŸããããã2ã€ã®ãããã¯éã®å€äœã¯ãåããã¯ãã«ããšåŒã°ããŸãã
- åãè£åïŒ æ¬¡ã«ããããã¯ããã®åããã¯ãã«ã«åŸã£ãŠã·ãããããããšã«ãããåã®ãã¬ãŒã ã®ãåãè£åãããŒãžã§ã³ãæ§ç¯ããŸãã
- ãã£ã«ã¿ãªã³ã°ïŒ æåŸã«ãçŸåšã®ãã¬ãŒã ãšãã®æ°ããåãè£åãããåã®ãã¬ãŒã ãšã®éã§æéçå¹³ååãå®è¡ããŸãã
ããã«ãããåããŠãããªããžã§ã¯ãã¯ããããã¡ããã©èŠãé ããèæ¯ã§ã¯ãªããåã®ãã¬ãŒã ã®èªåèªèº«ãšãã¬ã³ããããŸããããã«ããããŽãŒã¹ãã£ã³ã°ã¢ãŒãã£ãã¡ã¯ããå€§å¹ ã«æžå°ããŸããåãæšå®ã®å®è£ ã¯èšç®éçŽçã§è€éã§ããããã°ãã°é«åºŠãªã¢ã«ãŽãªãºã ãå¿ èŠãšããã»ãšãã©ã®å ŽåãWebAssemblyãŸãã¯WebGPUã³ã³ãã¥ãŒãã·ã§ãŒããŒã®ä»äºã§ãã
é©å¿ãã£ã«ã¿ãªã³ã°
ããäžã€ã®åŒ·åç¹ã¯ããã£ã«ã¿ãŒãé©å¿çã«ããããšã§ãããã¬ãŒã å
šäœã«åºå®ã®alpha
å€ã䜿çšãã代ããã«ã屿çãªæ¡ä»¶ã«åºã¥ããŠãããå€åãããããšãã§ããŸãã
- åãé©å¿æ§ïŒ åãã倧ããæ€åºãããé åã§ã¯ã
alpha
ãïŒäŸãã°0.95ã1.0ã«ïŒå¢ãããŠãã»ãŒå®å šã«çŸåšã®ãã¬ãŒã ã«äŸåãããã¢ãŒã·ã§ã³ãã©ãŒãé²ããŸããéçãªé åïŒèæ¯ã®å£ãªã©ïŒã§ã¯ãalpha
ãïŒäŸãã°0.5ã«ïŒæžãããŠããã匷åãªãã€ãºãªãã¯ã·ã§ã³ãè¡ããŸãã - èŒåºŠé©å¿æ§ïŒ ãã€ãºã¯ç»åã®æãé åã§ããç®ç«ã€ããšãå€ãã§ãããã£ã«ã¿ãŒã¯ã圱ã®éšåã§ã¯ããç©æ¥µçã«ãæããéšåã§ã¯ãã£ããŒã«ãä¿æããããã«ããæ§ããã«ããããšãã§ããŸãã
å®çšçãªãŠãŒã¹ã±ãŒã¹ãšã¢ããªã±ãŒã·ã§ã³
ãã©ãŠã¶ã§é«å質ãªãã€ãºãªãã¯ã·ã§ã³ãå®è¡ã§ããèœåã¯ãæ°å€ãã®å¯èœæ§ãåãéããŸãïŒ
- ãªã¢ã«ã¿ã€ã ã³ãã¥ãã±ãŒã·ã§ã³ (WebRTC)ïŒ ãŠãŒã¶ãŒã®ãŠã§ãã«ã¡ã©ãã£ãŒãããããªãšã³ã³ãŒããŒã«éä¿¡ããåã«ååŠçããŸããããã¯äœç §åºŠç°å¢ã§ã®ãããªé話ã«ãšã£ãŠå€§ããªåå©ã§ãããèŠèŠå質ãåäžãããå¿ èŠãªåž¯åå¹ ãåæžããŸãã
- ãŠã§ãããŒã¹ã®ãããªç·šéïŒ ãã©ãŠã¶å ãããªãšãã£ã¿ã®æ©èœãšããŠããã€ãºé€å»ããã£ã«ã¿ãŒãæäŸãããŠãŒã¶ãŒããµãŒããŒãµã€ãã®åŠçãªãã§ã¢ããããŒãããæ åãã¯ãªãŒã³ã¢ããã§ããããã«ããŸãã
- ã¯ã©ãŠãã²ãŒãã³ã°ãšãªã¢ãŒããã¹ã¯ãããïŒ å ¥åããããããªã¹ããªãŒã ãã¯ãªãŒã³ã¢ããããŠãå§çž®ã¢ãŒãã£ãã¡ã¯ããæžãããããã¯ãªã¢ã§å®å®ããç»åãæäŸããŸãã
- ã³ã³ãã¥ãŒã¿ããžã§ã³ååŠçïŒ ãŠã§ãããŒã¹ã®AI/MLã¢ããªã±ãŒã·ã§ã³ïŒãªããžã§ã¯ã远跡ãé¡èªèãªã©ïŒã§ã¯ãå ¥åãããªã®ãã€ãºãé€å»ããããšã§ããŒã¿ãå®å®ããããæ£ç¢ºã§ä¿¡é Œæ§ã®é«ãçµæã«ã€ãªããå¯èœæ§ããããŸãã
課é¡ãšå°æ¥ã®æ¹åæ§
匷åã§ããäžæ¹ããã®ã¢ãããŒãã«ã¯èª²é¡ããªãããã§ã¯ãããŸãããéçºè ã¯ä»¥äžã«æ³šæããå¿ èŠããããŸãïŒ
- ããã©ãŒãã³ã¹ïŒ HDãŸãã¯4Kãããªã®ãªã¢ã«ã¿ã€ã åŠçã¯èŠæ±ãé«ãã§ããå¹ççãªå®è£ ãéåžžã¯WebAssemblyã䜿çšããããšãå¿ é ã§ãã
- ã¡ã¢ãªïŒ 1ã€ä»¥äžã®åã®ãã¬ãŒã ãéå§çž®ãããã¡ãšããŠä¿åãããšã倧éã®RAMãæ¶è²»ããŸããæ³šææ·±ã管çãäžå¯æ¬ ã§ãã
- ã¬ã€ãã³ã·ãŒïŒ ãã¹ãŠã®åŠçã¹ãããã¯ã¬ã€ãã³ã·ãŒã远å ããŸãããªã¢ã«ã¿ã€ã ã³ãã¥ãã±ãŒã·ã§ã³ã®ããã«ã¯ããã®ãã€ãã©ã€ã³ã¯é¡èãªé å»¶ãé¿ããããã«é«åºŠã«æé©åãããªããã°ãªããŸããã
- WebGPUãšã®æªæ¥ïŒ æ°ãã«ç»å ŽããWebGPU APIã¯ããã®çš®ã®äœæ¥ã«æ°ããªããã³ãã£ã¢ãæäŸããã§ããããããã«ããããããã®ãã¯ã»ã«ããšã®ã¢ã«ãŽãªãºã ãã·ã¹ãã ã®GPUäžã§é«åºŠã«äžŠååãããã³ã³ãã¥ãŒãã·ã§ãŒããŒãšããŠå®è¡ã§ããããã«ãªããCPUäžã®WebAssemblyãããäžåãããã©ãŒãã³ã¹ã®å€§ããªé£èºãããããããŸãã
çµè«
WebCodecs APIã¯ããŠã§ãäžã§ã®é«åºŠãªã¡ãã£ã¢åŠçã®æ°æä»£ãåããŸããããã¯åŸæ¥ã®ãã©ãã¯ããã¯ã¹ã§ãã<video>
èŠçŽ ã®éå£ãåãæããéçºè
ã«çã«ãããã§ãã·ã§ãã«ãªãããªã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã«å¿
èŠãªãã现ããå¶åŸ¡ãäžããŸããæéçãã€ãºãªãã¯ã·ã§ã³ã¯ããã®åã®å®ç§ãªäŸã§ãïŒãŠãŒã¶ãŒãç¥èŠããå質ãšåºç€ãšãªãæè¡çå¹çã®äž¡æ¹ã«çŽæ¥å¯ŸåŠããæŽç·Žãããæè¡ã§ãã
åã
ã®VideoFrame
ãªããžã§ã¯ããååããããšã§ããã€ãºãäœæžããå§çž®æ§ãåäžãããåªãããããªäœéšãæäŸããããã®åŒ·åãªãã£ã«ã¿ãªã³ã°ããžãã¯ãå®è£
ã§ããããšãèŠãŠããŸãããåçŽãªJavaScriptå®è£
ã¯çŽ æŽãããåºçºç¹ã§ãããæ¬çªç°å¢ã§äœ¿ãããªã¢ã«ã¿ã€ã ãœãªã¥ãŒã·ã§ã³ãžã®éã¯ãWebAssemblyã®ããã©ãŒãã³ã¹ããããŠå°æ¥çã«ã¯WebGPUã®äžŠååŠçèœåãéããŠç¶ããŸãã
次ã«ãŠã§ãã¢ããªã§ãããããããããªãèŠããšãããããä¿®æ£ããããŒã«ãä»ãåããŠãŠã§ãéçºè ã®æã«çŽæ¥æž¡ãããŠããããšãæãåºããŠãã ããããŠã§ãäžã§ãããªãæ§ç¯ããã®ã«ãšããµã€ãã£ã³ã°ãªæä»£ã§ãã