ããã³ããšã³ãéçºã«ãããMediaStream Trackã®è€éããæ¢æ±ããŸããå ç¢ãªã¡ãã£ã¢ã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®äœæãæäœãå¶çŽãé«åºŠãªãã¯ããã¯ãç¶²çŸ ããŠããŸãã
ããã³ããšã³ã MediaStream Track: ã¡ãã£ã¢ãã©ãã¯ç®¡çã®å æ¬çã¬ã€ã
MediaStreamTrackã€ã³ã¿ãŒãã§ãŒã¹ã¯ãMediaStreamå
ã®åäžã®ã¡ãã£ã¢ãã©ãã¯ã衚ããŸãããã®ãã©ãã¯ã«ã¯é³å£°ãŸãã¯æ åã®ãããããå«ããããšãã§ããŸãããããã®ãã©ãã¯ã管çããæ¹æ³ãçè§£ããããšã¯ãå
ç¢ã§ã€ã³ã¿ã©ã¯ãã£ããªWebã¡ãã£ã¢ã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã«äžå¯æ¬ ã§ãããã®å
æ¬çãªã¬ã€ãã§ã¯ãããã³ããšã³ãéçºã«ãããMediaStream Trackã®äœæãæäœã管çã«ã€ããŠè©³ãã説æããŸãã
MediaStream Trackãšã¯ïŒ
MediaStreamã¯ã¡ãã£ã¢ã³ã³ãã³ãã®ã¹ããªãŒã ã§ãããè€æ°ã®MediaStreamTrackãªããžã§ã¯ããå«ãããšãã§ããŸããåãã©ãã¯ã¯ãé³å£°ãŸãã¯æ åã®åäžã®ãœãŒã¹ã衚ããŸããé³å£°ãŸãã¯æ åããŒã¿ã®ããããã®ã¹ããªãŒã ãä¿æããã³ã³ãããšèããŠãã ããã
äž»èŠãªããããã£ãšã¡ãœãã
kind: ãã©ãã¯ã®çš®é¡ïŒ"audio"ãŸãã¯"video"ïŒã瀺ãèªã¿åãå°çšã®æååãid: ãã©ãã¯ã®äžæã®èå¥åã衚ãèªã¿åãå°çšã®æååãlabel: ãã©ãã¯ã®äººéãèªããã©ãã«ãæäŸããèªã¿åãå°çšã®æååãenabled: ãã©ãã¯ãçŸåšæå¹ãã©ããã瀺ãããŒã«å€ããããfalseã«èšå®ãããšããã©ãã¯ã忢ããã«ãã¥ãŒããŸãã¯ç¡å¹åããŸããmuted: ã·ã¹ãã ã¬ãã«ã®å¶çŽããŠãŒã¶ãŒèšå®ã«ãããã©ãã¯ããã¥ãŒããããŠãããã©ããã瀺ãèªã¿åãå°çšã®ããŒã«å€ãreadyState: ãã©ãã¯ã®çŸåšã®ç¶æ ïŒ"live"ã"ended"ïŒã瀺ãèªã¿åãå°çšã®æååãgetSettings(): ãã©ãã¯ã®çŸåšã®èšå®ã®èŸæžãè¿ããŸããgetConstraints(): ãã©ãã¯ãäœæããããšãã«é©çšãããå¶çŽã®èŸæžãè¿ããŸããapplyConstraints(constraints): ãã©ãã¯ã«æ°ããå¶çŽãé©çšããããšè©Šã¿ãŸããclone(): å ã®ãã©ãã¯ã®ã¯ããŒã³ã§ããæ°ããMediaStreamTrackãªããžã§ã¯ããè¿ããŸããstop(): ãã©ãã¯ã忢ããã¡ãã£ã¢ããŒã¿ã®æµããçµäºãããŸããaddEventListener():endedãmuteãªã©ããã©ãã¯äžã®ã€ãã³ãããªãã¹ã³ã§ããŸãã
MediaStream Trackã®ååŸ
MediaStreamTrackãªããžã§ã¯ããååŸããäž»ãªæ¹æ³ã¯ãgetUserMedia() APIã䜿çšããããšã§ãããã®APIã¯ããŠãŒã¶ãŒã«ã«ã¡ã©ãšãã€ã¯ãžã®ã¢ã¯ã»ã¹èš±å¯ãæ±ããèš±å¯ããããšãèŠæ±ããããã©ãã¯ãå«ãMediaStreamãè¿ããŸãã
getUserMedia()ã®äœ¿çš
以äžã¯ãgetUserMedia()ã䜿çšããŠãŠãŒã¶ãŒã®ã«ã¡ã©ãšãã€ã¯ã«ã¢ã¯ã»ã¹ããåºæ¬çãªäŸã§ãïŒ
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(function(stream) {
// ããã§ã¹ããªãŒã ã䜿çšããŸãã
const videoTrack = stream.getVideoTracks()[0];
const audioTrack = stream.getAudioTracks()[0];
// äŸïŒvideoèŠçŽ ã«æ åã衚瀺
const videoElement = document.getElementById('myVideo');
videoElement.srcObject = stream;
videoElement.play();
})
.catch(function(err) {
console.log("ãšã©ãŒãçºçããŸãã: " + err);
});
説æ:
navigator.mediaDevices.getUserMedia({ video: true, audio: true }): ããã¯æ åãšé³å£°ã®äž¡æ¹ã®å ¥åãžã®ã¢ã¯ã»ã¹ãèŠæ±ããŸããgetUserMediaã«æž¡ããããªããžã§ã¯ãã¯ãèŠæ±ãããã¡ãã£ã¢ã¿ã€ããå®çŸ©ããŸãã.then(function(stream) { ... }): ããã¯ããŠãŒã¶ãŒãèš±å¯ãäžããMediaStreamãæ£åžžã«ååŸããããšãã«å®è¡ãããŸããstream.getVideoTracks()[0]: ããã¯ã¹ããªãŒã ããæåã®ãããªãã©ãã¯ãååŸããŸããã¹ããªãŒã ã¯åãã¿ã€ãã®è€æ°ã®ãã©ãã¯ãå«ãããšãã§ããŸããstream.getAudioTracks()[0]: ããã¯ã¹ããªãŒã ããæåã®ãªãŒãã£ãªãã©ãã¯ãååŸããŸããvideoElement.srcObject = stream: ããã¯videoèŠçŽ ã®srcObjectãMediaStreamã«èšå®ããæ åã衚瀺ã§ããããã«ããŸããvideoElement.play(): æ åã®åçãéå§ããŸãã.catch(function(err) { ... }): ããã¯ããŠãŒã¶ãŒãèš±å¯ãæåŠããå Žåãªã©ããšã©ãŒãçºçããå Žåã«å®è¡ãããŸãã
å¶çŽ
å¶çŽã䜿çšãããšãè§£å床ããã¬ãŒã ã¬ãŒããé³å£°å質ãªã©ãã¡ãã£ã¢ãã©ãã¯ã®èŠä»¶ãæå®ã§ããŸããããã¯ãã¢ããªã±ãŒã·ã§ã³ãç¹å®ã®ããŒãºãæºããã¡ãã£ã¢ããŒã¿ãåä¿¡ããããšãä¿èšŒããããã«éèŠã§ããå¶çŽã¯getUserMedia()åŒã³åºãå
ã§æå®ã§ããŸãã
navigator.mediaDevices.getUserMedia({
video: {
width: { min: 640, ideal: 1280, max: 1920 },
height: { min: 480, ideal: 720, max: 1080 },
frameRate: { ideal: 30, max: 60 }
},
audio: {
echoCancellation: { exact: true },
noiseSuppression: { exact: true }
}
})
.then(function(stream) {
// ...
})
.catch(function(err) {
console.log("ãšã©ãŒãçºçããŸãã: " + err);
});
説æ:
width: { min: 640, ideal: 1280, max: 1920 }: ããã¯ããããªã®å¹ ãå°ãªããšã640ãã¯ã»ã«ãçæ³çã«ã¯1280ãã¯ã»ã«ãæå€§ã§1920ãã¯ã»ã«ã§ããããšãæå®ããŸãããã©ãŠã¶ã¯ãããã®å¶çŽããµããŒãããã«ã¡ã©ãèŠã€ããããšããŸããheight: { min: 480, ideal: 720, max: 1080 }: å¹ ãšåæ§ã«ãããã¯ãããªã®åžæã®é«ããå®çŸ©ããŸããframeRate: { ideal: 30, max: 60 }: ããã¯ãçæ³çã«ã¯æ¯ç§30ãã¬ãŒã ãæå€§ã§æ¯ç§60ãã¬ãŒã ã®ãã¬ãŒã ã¬ãŒããèŠæ±ããŸããechoCancellation: { exact: true }: ããã¯ããªãŒãã£ãªãã©ãã¯ã§ãšã³ãŒãã£ã³ã»ã«ãæå¹ã«ããããšãèŠæ±ããŸããexact: trueã¯ããã©ãŠã¶ããšã³ãŒãã£ã³ã»ã«ãæäŸ*ããªããã°ãªããªã*ããšãæå³ãããããªããã°ãªã¯ãšã¹ãã¯å€±æããŸããnoiseSuppression: { exact: true }: ããã¯ããªãŒãã£ãªãã©ãã¯ã§ãã€ãºæå¶ãæå¹ã«ããããšãèŠæ±ããŸãã
ãã©ãŠã¶ããã¹ãŠã®å¶çŽãæºããããšã¯éããªãããšã«æ³šæããããšãéèŠã§ããMediaStreamTrackã®getSettings()ã䜿çšããŠãå®éã«é©çšãããèšå®ã確èªã§ããŸãã
MediaStream Trackã®æäœ
MediaStreamTrackãååŸããããããŸããŸãªæ¹æ³ã§æäœããŠãé³å£°ãšæ åã®åºåãå¶åŸ¡ã§ããŸãã
ãã©ãã¯ã®æå¹åãšç¡å¹å
enabledããããã£ã䜿çšããŠãã©ãã¯ãæå¹ãŸãã¯ç¡å¹ã«ã§ããŸããenabledãfalseã«èšå®ãããšããªãŒãã£ãªãã©ãã¯ããã¥ãŒããããããããªãã©ãã¯ã忢ããã«ç¡å¹ã«ãããã§ããŸããtrueã«æ»ããšããã©ãã¯ãåã³æå¹ã«ãªããŸãã
const videoTrack = stream.getVideoTracks()[0];
// ãããªãã©ãã¯ãç¡å¹ã«ãã
videoTrack.enabled = false;
// ãããªãã©ãã¯ãæå¹ã«ãã
videoTrack.enabled = true;
ããã¯ããã¥ãŒããã¿ã³ããããªã®ãªã³/ãªãåãæ¿ããªã©ã®æ©èœãå®è£ ããã®ã«äŸ¿å©ã§ãã
äœæåŸã®å¶çŽã®é©çš
applyConstraints()ã¡ãœããã䜿çšããŠããã©ãã¯ãäœæãããåŸã«ãã®å¶çŽã倿Žã§ããŸããããã«ããããŠãŒã¶ãŒã®å¥œã¿ããããã¯ãŒã¯ã®ç¶æ
ã«åºã¥ããŠãé³å£°ãšæ åã®èšå®ãåçã«èª¿æŽã§ããŸãããã ãããã©ãã¯äœæåŸã«å€æŽã§ããªãå¶çŽããããŸããapplyConstraints()ãæåãããã©ããã¯ãåºç€ãšãªãããŒããŠã§ã¢ã®æ©èœãšãã©ãã¯ã®çŸåšã®ç¶æ
ã«äŸåããŸãã
const videoTrack = stream.getVideoTracks()[0];
videoTrack.applyConstraints({ frameRate: { ideal: 24 } })
.then(function() {
console.log("å¶çŽãæ£åžžã«é©çšãããŸããã");
})
.catch(function(err) {
console.log("å¶çŽã®é©çšã«å€±æããŸãã: " + err);
});
ãã©ãã¯ã®åæ¢
ãã©ãã¯ãå®å
šã«åæ¢ããåºç€ãšãªããªãœãŒã¹ãè§£æŸããã«ã¯ãstop()ã¡ãœããã䜿çšããŸããããã¯ãã«ã¡ã©ãšãã€ã¯ãäžèŠã«ãªã£ããšããç¹ã«ã¢ãã€ã«ããã€ã¹ã®ãããªãªãœãŒã¹ã«å¶çŽã®ããç°å¢ã§ãããããè§£æŸããããã«éèŠã§ããäžåºŠåæ¢ãããã©ãã¯ã¯åéã§ããŸãããgetUserMedia()ã䜿çšããŠæ°ãããã©ãã¯ãååŸããå¿
èŠããããŸãã
const videoTrack = stream.getVideoTracks()[0];
// ãã©ãã¯ã忢ãã
videoTrack.stop();
äœæ¥ãçµäºããããMediaStreamå
šäœã忢ããããšãè¯ãç¿æ
£ã§ãïŒ
stream.getTracks().forEach(track => track.stop());
é«åºŠãªãã¯ããã¯
åºæ¬ãè¶
ããŠãMediaStreamTrackãªããžã§ã¯ããããã«æäœããã³åŒ·åããããã«äœ¿çšã§ããããã€ãã®é«åºŠãªãã¯ããã¯ããããŸãã
ãã©ãã¯ã®ã¯ããŒã³äœæ
clone()ã¡ãœããã¯ãå
ã®ã³ããŒã§ããæ°ããMediaStreamTrackãªããžã§ã¯ããäœæããŸããã¯ããŒã³ããããã©ãã¯ã¯ãåãåºç€ãšãªãã¡ãã£ã¢ãœãŒã¹ãå
±æããŸããããã¯ãåããããªãè€æ°ã®videoèŠçŽ ã«è¡šç€ºãããªã©ãåãã¡ãã£ã¢ãœãŒã¹ãè€æ°ã®å Žæã§äœ¿çšããå¿
èŠãããå Žåã«äŸ¿å©ã§ãã
const originalTrack = stream.getVideoTracks()[0];
const clonedTrack = originalTrack.clone();
// ã¯ããŒã³ããããã©ãã¯ã§æ°ããMediaStreamãäœæ
const clonedStream = new MediaStream([clonedTrack]);
// ã¯ããŒã³ãããã¹ããªãŒã ãå¥ã®videoèŠçŽ ã«è¡šç€º
const videoElement2 = document.getElementById('myVideo2');
videoElement2.srcObject = clonedStream;
videoElement2.play();
å ã®ãã©ãã¯ã忢ãããšãã¯ããŒã³ããããã©ãã¯ã忢ããããšã«æ³šæããŠãã ããããªããªãããããã¯åãåºç€ãšãªãã¡ãã£ã¢ãœãŒã¹ãå ±æããŠããããã§ãã
é³å£°ãšæ åã®åŠç
MediaStreamTrackã€ã³ã¿ãŒãã§ãŒã¹èªäœã¯ãé³å£°ãæ åããŒã¿ãåŠçããããã®çŽæ¥çãªã¡ãœãããæäŸããŠããŸããããã ããWeb Audio APIãCanvas APIãªã©ã®ä»ã®Web APIã䜿çšããŠãããå®çŸã§ããŸãã
Web Audio APIã«ããé³å£°åŠç
Web Audio APIã䜿çšããŠãMediaStreamTrackããã®é³å£°ããŒã¿ãåæããã³æäœã§ããŸããããã«ãããé³å£°ã®èŠèŠåããã€ãºãªãã¯ã·ã§ã³ããªãŒãã£ãªãšãã§ã¯ããªã©ã®ã¿ã¹ã¯ãå®è¡ã§ããŸãã
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
// é³å£°ããŒã¿ãæœåºããããã®ã¢ãã©ã€ã¶ãŒããŒããäœæ
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
// ãœãŒã¹ãã¢ãã©ã€ã¶ãŒã«æ¥ç¶
source.connect(analyser);
analyser.connect(audioContext.destination);
function draw() {
requestAnimationFrame(draw);
// åšæ³¢æ°ããŒã¿ãååŸ
analyser.getByteFrequencyData(dataArray);
// dataArrayã䜿çšããŠé³å£°ãèŠèŠå
// (äŸïŒcanvasã«åšæ³¢æ°ã¹ãã¯ãã«ãæç»)
console.log(dataArray);
}
draw();
説æ:
new AudioContext(): æ°ããWeb Audio APIã³ã³ããã¹ããäœæããŸããaudioContext.createMediaStreamSource(stream):MediaStreamãããªãŒãã£ãªãœãŒã¹ããŒããäœæããŸããaudioContext.createAnalyser(): é³å£°ããŒã¿ãæœåºããããã«äœ¿çšã§ããã¢ãã©ã€ã¶ãŒããŒããäœæããŸããanalyser.fftSize = 2048: é«éããŒãªãšå€æïŒFFTïŒã®ãµã€ãºãèšå®ããŸããããã¯åšæ³¢æ°ãã³ã®æ°ã決å®ããŸããanalyser.getByteFrequencyData(dataArray):dataArrayã«åšæ³¢æ°ããŒã¿ãæ ŒçŽããŸããdraw()颿°ã¯requestAnimationFrame()ã䜿çšããŠç¹°ãè¿ãåŒã³åºãããé³å£°ã®èŠèŠåãç¶ç¶çã«æŽæ°ããŸãã
Canvas APIã«ããæ ååŠç
Canvas APIã䜿çšããŠãMediaStreamTrackããã®ãããªãã¬ãŒã ãæäœã§ããŸããããã«ããããã£ã«ã¿ãŒã®é©çšããªãŒããŒã¬ã€ã®è¿œå ããªã¢ã«ã¿ã€ã ã®æ ååæãªã©ã®ã¿ã¹ã¯ãå®è¡ã§ããŸãã
const videoElement = document.getElementById('myVideo');
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
function drawFrame() {
requestAnimationFrame(drawFrame);
// çŸåšã®ãããªãã¬ãŒã ãcanvasã«æç»
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
// canvasããŒã¿ãæäœ (äŸïŒãã£ã«ã¿ãŒãé©çš)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// ç°¡åãªã°ã¬ãŒã¹ã±ãŒã«ãã£ã«ã¿ãŒãé©çš
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // èµ€
data[i + 1] = avg; // ç·
data[i + 2] = avg; // é
}
// 倿ŽããããŒã¿ãcanvasã«æ»ã
ctx.putImageData(imageData, 0, 0);
}
videoElement.addEventListener('play', drawFrame);
説æ:
drawFrame()颿°ã¯requestAnimationFrame()ã䜿çšããŠç¹°ãè¿ãåŒã³åºãããcanvasãç¶ç¶çã«æŽæ°ããŸããctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height): çŸåšã®ãããªãã¬ãŒã ãcanvasã«æç»ããŸããctx.getImageData(0, 0, canvas.width, canvas.height): canvasããç»åããŒã¿ãååŸããŸãã- ã³ãŒãã¯ãã¯ã»ã«ããŒã¿ãå埩åŠçããã°ã¬ãŒã¹ã±ãŒã«ãã£ã«ã¿ãŒãé©çšããŸãã
ctx.putImageData(imageData, 0, 0): 倿Žãããç»åããŒã¿ãcanvasã«æ»ããŸãã
WebRTCã§ã®MediaStream Trackã®äœ¿çš
MediaStreamTrackãªããžã§ã¯ãã¯ããã©ãŠã¶éã§çŽæ¥ãªã¢ã«ã¿ã€ã ã®é³å£°ããã³æ åéä¿¡ãå¯èœã«ããWebRTCïŒWeb Real-Time CommunicationïŒã®åºæ¬ã§ããWebRTCã®RTCPeerConnectionã«MediaStreamTrackãªããžã§ã¯ãã远å ããŠããªã¢ãŒããã¢ã«é³å£°ããã³æ åããŒã¿ãéä¿¡ã§ããŸãã
const peerConnection = new RTCPeerConnection();
// é³å£°ãã©ãã¯ãšãããªãã©ãã¯ããã¢æ¥ç¶ã«è¿œå
stream.getTracks().forEach(track => {
peerConnection.addTrack(track, stream);
});
// ãã®åŸã«ãæ®ãã®WebRTCã·ã°ããªã³ã°ãšæ¥ç¶ç¢ºç«ããã»ã¹ãç¶ããŸãã
ããã«ããããããªäŒè°ã¢ããªã±ãŒã·ã§ã³ãã©ã€ãã¹ããªãŒãã³ã°ãã©ãããã©ãŒã ããã®ä»ã®ãªã¢ã«ã¿ã€ã ã³ãã¥ãã±ãŒã·ã§ã³ããŒã«ãæ§ç¯ã§ããŸãã
ãã©ãŠã¶ã®äºææ§
MediaStreamTrack APIã¯ãChromeãFirefoxãSafariãEdgeãªã©ã®ææ°ã®ãã©ãŠã¶ã§åºããµããŒããããŠããŸãããã ããMDN Web Docsã®ãããªãªãœãŒã¹ã§ææ°ã®ãã©ãŠã¶äºææ§æ
å ±ã確èªããããšããå§ãããŸãã
ãã¹ããã©ã¯ãã£ã¹
- èš±å¯ãæ éã«åŠçãã: ã«ã¡ã©ãšãã€ã¯ãžã®ã¢ã¯ã»ã¹ã«å¯ŸãããŠãŒã¶ãŒã®èš±å¯ã¯ãåžžã«äžéã«åŠçããŠãã ãããã¢ããªã±ãŒã·ã§ã³ããããã®ããã€ã¹ãžã®ã¢ã¯ã»ã¹ãå¿ èŠãšããçç±ãæç¢ºã«èª¬æããŸãã
- äžèŠã«ãªã£ãããã©ãã¯ã忢ãã: 䜿çšãããªããªã£ããã©ãã¯ã¯åæ¢ããŠãã«ã¡ã©ãšãã€ã¯ã®ãªãœãŒã¹ãè§£æŸããŸãã
- å¶çŽãæé©åãã: å¶çŽã䜿çšããŠãã¢ããªã±ãŒã·ã§ã³ã«æé©ãªã¡ãã£ã¢èšå®ãèŠæ±ããŸããå¿ èŠã§ãªãå Žåã¯ãé床ã«é«ãè§£å床ããã¬ãŒã ã¬ãŒããèŠæ±ããªãã§ãã ããã
- ãã©ãã¯ã®ç¶æ
ãç£èŠãã:
endedãmuteãªã©ã®ã€ãã³ãããªãã¹ã³ããŠããã©ãã¯ã®ç¶æ ã®å€åã«å¯Ÿå¿ããŸãã - ç°ãªãããã€ã¹ã§ãã¹ããã: ããŸããŸãªããã€ã¹ããã©ãŠã¶ã§ã¢ããªã±ãŒã·ã§ã³ããã¹ãããŠãäºææ§ã確ä¿ããŸãã
- ã¢ã¯ã»ã·ããªãã£ãèæ ®ãã: éãããæã€ãŠãŒã¶ãŒãã¢ã¯ã»ã¹ã§ããããã«ã¢ããªã±ãŒã·ã§ã³ãèšèšããŸãã代æ¿ã®å ¥åæ¹æ³ãæäŸããé³å£°ãšæ åã®åºåãæç¢ºã§çè§£ããããããšã確èªããŸãã
çµè«
MediaStreamTrackã€ã³ã¿ãŒãã§ãŒã¹ã¯ãã¡ãã£ã¢ãªãããªWebã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®åŒ·åãªããŒã«ã§ããã¡ãã£ã¢ãã©ãã¯ã®äœæãæäœãç®¡çæ¹æ³ãçè§£ããããšã§ããŠãŒã¶ãŒã«é
åçã§ã€ã³ã¿ã©ã¯ãã£ããªäœéšãæäŸã§ããŸãããã®å
æ¬çãªã¬ã€ãã§ã¯ãgetUserMedia()ã䜿çšãããã©ãã¯ã®ååŸãããé³å£°ãæ ååŠçãªã©ã®é«åºŠãªãã¯ããã¯ãŸã§ãMediaStreamTrack管çã®éèŠãªåŽé¢ãç¶²çŸ
ããŸãããã¡ãã£ã¢ã¹ããªãŒã ãæ±ãéã¯ããŠãŒã¶ãŒã®ãã©ã€ãã·ãŒãåªå
ããããã©ãŒãã³ã¹ãæé©åããããšãå¿ããªãã§ãã ãããWebRTCããã³é¢é£æè¡ãããã«æ¢æ±ããããšã§ããã®åºæ¿çãªWebéçºåéã§ã®ããªãã®èœåã¯å€§å¹
ã«åäžããã§ãããã