ã¡ãã£ã¢è¦ç´ ã§ã®åé²
MediaStream åé² API ã®ä½¿ç¨ã®è¨äºã§ã¯ã MediaRecorder ã¤ã³ã¿ã¼ãã§ã¤ã¹ã使ç¨ã㦠navigator.mediaDevices.getUserMedia() ããããã¼ãã¦ã§ã¢ããã¤ã¹ã«ãã£ã¦çæããã MediaStream ããã£ããã£ããæ¹æ³ã«ã¤ãã¦èª¬æãã¾ããããè¨é²ãã MediaStream ã®ã½ã¼ã¹ã¨ã㦠HTML ã¡ãã£ã¢è¦ç´ ï¼<audio> ã¾ã㯠<video>ï¼ã使ç¨ã§ãã¾ãã ãã®è¨äºã§ã¯ããããå®ç¾ããä¾ãè¦ã¦ããã¾ãã
ã¡ãã£ã¢è¦ç´ ã®åé²ã®ä¾
>HTML
ã¾ã㯠HTML ã®è¦ç¹ãè¦ã¦ã¿ã¾ãããã ãã以ä¸ã®ãã®ã¯ããã¾ããããã¢ããªã®ã³ã¢æä½ã®ä¸é¨ã§ã¯ãªããåãªãæ å ±æä¾ã§ãã
<div class="left">
<div id="startButton" class="button">é²ç»ãéå§</div>
<h2>Preview</h2>
<video id="preview" width="160" height="120" autoplay muted></video>
</div>
2 ã¤ã®æ¬ã§ä¸»è¦ãªã¤ã³ã¿ã¼ãã§ã¤ã¹ãæç¤ºãã¾ãã å·¦æ¬ã«ã¯ãStartï¼éå§ï¼ãã¿ã³ã¨åç»ãã¬ãã¥ã¼ã表示ãã <video> è¦ç´ ãããã¾ãã ããã¯ãã¦ã¼ã¶ã¼ã®ã«ã¡ã©ãè¦ã¦ããåç»ã§ãã autoplay 屿§ã¯ãã«ã¡ã©ããã¹ããªã¼ã ãå°çãããããã«è¡¨ç¤ºããããã«ä½¿ç¨ããmuted 屿§ã¯ãã¦ã¼ã¶ã¼ã®ãã¤ã¯ããã®é³å£°ãã¹ãã¼ã«ã¼ã«åºåããªãããã«ä½¿ç¨ãã¦ãããã¨ã«æ³¨æãã¦ãã ããã åºåããã¨éããã£ã¼ãããã¯ã«ã¼ãï¼ãã¦ãªã³ã°ï¼ãå¼ãèµ·ããã¾ãã
<div class="right">
<div id="stopButton" class="button">é²ç»ã忢</div>
<h2>Recording</h2>
<video id="recording" width="160" height="120" controls></video>
<a id="downloadButton" class="button">ãã¦ã³ãã¼ã</a>
</div>
峿¬ã«ã¯ãStopï¼åæ¢ï¼ãã¿ã³ã¨åé²ãããåç»ã®åçã«ä½¿ç¨ãã <video> è¦ç´ ãããã¾ãã åçããã«ã«ã¯ autoplay ãè¨å®ããã«ï¼ã¡ãã£ã¢ãå°çãã¦ãåçãéå§ãããªãï¼ãcontrols ãè¨å®ãã¦ãåçã䏿忢ãªã©ã®ã¦ã¼ã¶ã¼ã³ã³ããã¼ã«ã表示ããããã«æç¤ºãã¦ãã¾ãã
åçè¦ç´ ã®ä¸ã«ã¯ãåé²ããåç»ããã¦ã³ãã¼ãããããã®ãã¿ã³ãããã¾ãã
ããã§ã¯ãJavaScript ã³ã¼ããè¦ã¦ã¿ã¾ãããã çµå±ã®ã¨ããããããã¢ã¯ã·ã§ã³ã®å¤§é¨åãèµ·ããã¨ããã§ãï¼
ã°ãã¼ãã«å¤æ°ã®è¨å®
å¿ è¦ãªã°ãã¼ãã«å¤æ°ãããã¤ãè¨å®ãããã¨ããå§ãã¾ãã
let preview = document.getElementById("preview");
let recording = document.getElementById("recording");
let startButton = document.getElementById("startButton");
let stopButton = document.getElementById("stopButton");
let downloadButton = document.getElementById("downloadButton");
let logElement = document.getElementById("log");
let recordingTimeMS = 5000;
ãããã®ã»ã¨ãã©ã¯ãç§ãã¡ãåãçµãå¿
è¦ãããè¦ç´ ã¸ã®åç
§ã§ãã æå¾ã® recordingTimeMS 㯠5000 ããªç§ï¼5 ç§ï¼ã«è¨å®ããã¦ãã¾ãã ããã¯ãåé²ããåç»ã®é·ããæå®ãã¾ãã
ã¦ã¼ãã£ãªãã£é¢æ°
次ã«ãå¾ã§ä½¿ç¨ããã¦ã¼ãã£ãªãã£é¢æ°ãããã¤ã使ãã¾ãã
function log(msg) {
logElement.innerText += `${msg}\n`;
}
log() 颿°ã¯ãã¦ã¼ã¶ã¼ã¨æ
å ±ãå
±æã§ããããã«ãããã¹ãæååã <div> ã«åºåããããã«ä½¿ç¨ãã¾ãã ããã»ã©ãããã§ã¯ããã¾ãããããã®ä»äºã¯ç§ãã¡ã®ç®çã®ããã«è¡ããã¾ãã
function wait(delayInMS) {
return new Promise((resolve) => setTimeout(resolve, delayInMS));
}
wait() 颿°ã¯ãæå®ããããªç§æ°ãçµéããã¨è§£æ±ºããæ°ãã Promise ãè¿ãã¾ãã ã¿ã¤ã ã¢ã¦ããã³ãã©ã¼é¢æ°ã¨ãã¦ãããã¹ã®è§£æ±ºãã³ãã©ã¼ãæå®ãã¦ãsetTimeout() ãå¼ã³åºãã¢ãã¼é¢æ°ã使ç¨ãã¦åä½ãã¾ãã ããã«ãããã¿ã¤ã ã¢ã¦ãã使ç¨ããã¨ãã«ãããã¹æ§æã使ç¨ã§ãã¾ãã ããã¯ãå¾ã§èª¬æããããã«ããããã¹ãé£éãããã¨ãã«é常ã«ä¾¿å©ã§ãã
ã¡ãã£ã¢åé²ã®éå§
startRecording() 颿°ã¯åé²ããã»ã¹ã®éå§ãå¦çãã¾ãã
function startRecording(stream, lengthInMS) {
let recorder = new MediaRecorder(stream);
let data = [];
recorder.ondataavailable = (event) => data.push(event.data);
recorder.start();
log(`${recorder.state} for ${lengthInMS / 1000} secondsâ¦`);
let stopped = new Promise((resolve, reject) => {
recorder.onstop = resolve;
recorder.onerror = (event) => reject(event.name);
});
let recorded = wait(lengthInMS).then(() => {
if (recorder.state === "recording") {
recorder.stop();
}
});
return Promise.all([stopped, recorded]).then(() => data);
}
startRecording() 㯠2 ã¤ã®å
¥å弿°ãåãã¾ãã åé²å
ã® MediaStream ã¨è¨é²ããããªç§åä½ã®é·ãã§ãã æå®ãããããªç§æ°ä»¥ä¸ã®ã¡ãã£ã¢ã常ã«åé²ãã¾ããããã®æéã«éããåã«ã¡ãã£ã¢ã忢ããã¨ãMediaRecorder ãèªåçã«åé²ã忢ãã¾ãã
- ã¾ããå
¥åã¹ããªã¼ã (
stream) ã®åé²ãå¦çããMediaRecorderã使ãã¾ãã - åæç¶æ
ã§ç©ºã®é
å
dataã使ãã¾ãã ããã¯ãondataavailableã¤ãã³ããã³ãã©ã¼ã«æä¾ãããã¡ãã£ã¢ãã¼ã¿ã®Blobãä¿æããããã«ä½¿ç¨ãã¾ãã ondataavailableã«ä»£å ¥ãããã¨ã§ãdataavailableã¤ãã³ãã®ãã³ãã©ã¼ãè¨å®ãã¾ãã åä¿¡ããã¤ãã³ãã®dataããããã£ã¯ã¡ãã£ã¢ãã¼ã¿ãå«ãBlobã§ãã ã¤ãã³ããã³ãã©ã¼ã¯åç´ã«Blobãdataé åã«ããã·ã¥ï¼æ«å°¾ã«è¿½å ï¼ãã¾ããrecorder.start()ãå¼ã³åºãã¦åé²å¦çãéå§ããrecorderã®æ´æ°ãããç¶æ ã¨åé²ãããç§æ°ã¨ã¨ãã«ã¡ãã»ã¼ã¸ããã°ã«åºåãã¾ããstoppedã¨ããååã®æ°ããPromiseã使ãã¾ãã ããã¯ãMediaRecorderã®onstopã¤ãã³ããã³ãã©ã¼ãå¼ã³åºãããã¨è§£æ±ºããonerrorã¤ãã³ããã³ãã©ã¼ãå¼ã³åºãããã¨æå¦ãã¾ãã æå¦ãã³ãã©ã¼ã¯ãçºçããã¨ã©ã¼ã®ååãå ¥åã¨ãã¦åãåãã¾ããrecordedã¨ããååã®ããä¸ã¤ã®Promiseã使ãã¾ãã ããã¯ãæå®ãããããªç§æ°ãçµéããã¨è§£æ±ºãã¾ãã 解決ããã¨ãMediaRecorderãåé²ä¸ã®å ´åã¯åæ¢ãã¾ãã- æå¾ã«ã
Promise.allã使ç¨ãã¦Promiseï¼stoppedã¨recordedï¼ã®ä¸¡æ¹ã解決ããã¨ãã«æºããããæ°ããPromiseã使ãã¾ãã ããã解決ããã¨ãé åãã¼ã¿ã¯startRecording()ã«ãã£ã¦ãã®å¼ã³åºãå ã«è¿ããã¾ãã
å ¥åã¹ããªã¼ã ã®åæ¢
stop() 颿°ã¯åã«å
¥åã¡ãã£ã¢ã忢ãã¾ãã
function stop(stream) {
stream.getTracks().forEach((track) => track.stop());
}
ãã㯠MediaStream.getTracks() ãå¼ã³åºããforEach() ã使ç¨ãã¦ã¹ããªã¼ã å
ã®åãã©ãã¯ã® MediaStreamTrack.stop() ãå¼ã³åºããã¨ã«ãã£ã¦æ©è½ãã¾ãã
å ¥åã¹ããªã¼ã ãåå¾ãã¦ã¬ã³ã¼ãã¼ãè¨å®
ããã§ã¯ããã®ä¾ã§æãè¤éãªã³ã¼ããè¦ã¦ã¿ã¾ãããã éå§ãã¿ã³ãã¯ãªãã¯ããã¨ãã®ã¤ãã³ããã³ãã©ã¼ã§ãã
startButton.addEventListener(
"click",
() => {
navigator.mediaDevices
.getUserMedia({
video: true,
audio: true,
})
.then((stream) => {
preview.srcObject = stream;
downloadButton.href = stream;
preview.captureStream =
preview.captureStream || preview.mozCaptureStream;
return new Promise((resolve) => (preview.onplaying = resolve));
})
.then(() => startRecording(preview.captureStream(), recordingTimeMS))
.then((recordedChunks) => {
let recordedBlob = new Blob(recordedChunks, { type: "video/webm" });
recording.src = URL.createObjectURL(recordedBlob);
downloadButton.href = recording.src;
downloadButton.download = "RecordedVideo.webm";
log(
`Successfully recorded ${recordedBlob.size} bytes of ${recordedBlob.type} media.`,
);
})
.catch((error) => {
if (error.name === "NotFoundError") {
log("Camera or microphone not found. Can't record.");
} else {
log(error);
}
});
},
false,
);
click ã¤ãã³ããçºçããã¨ã次ã®ããã«ãªãã¾ãã
-
MediaDevices.getUserMediaã¯ãåç»ãã©ãã¯ã¨é³å£°ãã©ãã¯ã®ä¸¡æ¹ãæã¤æ°ããMediaStreamãè¦æ±ããããã«å¼ã³åºãã¾ãã ãããåé²ããã¹ããªã¼ã ã§ãã -
getUserMedia()ããè¿ããããããã¹ã解決ããã¨ããã¬ãã¥ã¼ã®<video>è¦ç´ ã®srcObjectããããã£ãå ¥åã¹ããªã¼ã ã«è¨å®ããã¦ã¼ã¶ã¼ã®ã«ã¡ã©ã§ãã£ããã£ãã¦ããåç»ããã¬ãã¥ã¼ããã¯ã¹ã«è¡¨ç¤ºãã¾ãã<video>è¦ç´ ã¯ãã¥ã¼ããã¦ããã®ã§ãé³å£°ã¯åçãã¾ããã "Download"ï¼ãã¦ã³ãã¼ãï¼ãã¿ã³ã®ãªã³ã¯ããã¹ããªã¼ã ãåç §ããããã«è¨å®ãã¾ãã æ¬¡ã«ãpreview.captureStream()ã§preview.mozCaptureStream()ãå¼ã³åºãããã«æé ãã¦ãã³ã¼ãã Firefox ã§åä½ããããã«ãã¾ããHTMLMediaElement.captureStream()ã¡ã½ãããæ¥é è¾ä»ãã ããã§ãããã®å¾ããã¬ãã¥ã¼åç»ã®åçéå§æã«è§£æ±ºããæ°ããPromiseã使ãã¦è¿ãã¾ãã -
ãã¬ãã¥ã¼åç»ã®åçãéå§ãããã¨ãåé²ããã¡ãã£ã¢ããããã¨ããããã¾ãã ãããã£ã¦ãå ã»ã©ä½æãã
startRecording()颿°ãå¼ã³åºãããã¬ãã¥ã¼åç»ã¹ããªã¼ã ï¼åé²ããã½ã¼ã¹ã¡ãã£ã¢ã¨ãã¦ï¼ã¨ãrecordingTimeMSï¼åé²ããã¡ãã£ã¢ã®ããªç§æ°ã¨ãã¦ï¼ã渡ãã¾ãã åè¿°ã®ããã«ãstartRecording()ã¯ãåé²ãå®äºããã¨ã解決ãã³ãã©ã¼ãå¼ã³åºãããPromiseï¼åé²ãããã¡ãã£ã¢ãã¼ã¿ã®ãã£ã³ã¯ãå«ãBlobãªãã¸ã§ã¯ãã®é åãå ¥åã¨ãã¦åãåãï¼ãè¿ãã¾ãã -
åé²ããã»ã¹ã®è§£æ±ºãã³ãã©ã¼ã¯ããã¼ã«ã«ã«
recordedChunksã¨ãã¦ç¥ãããã¡ãã£ã¢ãã¼ã¿ã®Blobã®é åãå ¥åã¨ãã¦åãåãã¾ãã æåã«ãããã¨ã¯ãBlobã³ã³ã¹ãã©ã¯ã¿ã¼ããªãã¸ã§ã¯ãã®é åã 1 ã¤ã®ãªãã¸ã§ã¯ãã«é£çµããã¨ããäºå®ãå©ç¨ãã¦ããã£ã³ã¯ã MIME ã¿ã¤ãã"video/webm"ã®åä¸ã®Blobã«ãã¼ã¸ãããã¨ã§ãã æ¬¡ã«ãURL.createObjectURL()ã使ç¨ãã¦Blobãåç §ãã URL ã使ãã¾ãã ããã¯ããã¦ã³ãã¼ããããåç»åçè¦ç´ ã®src屿§ã®å¤ï¼Blobããåç»ãåçã§ããããã«ããï¼ã¨ãã¦ã³ãã¼ããã¿ã³ã®ãªã³ã¯ã®ã¿ã¼ã²ããã«ãªãã¾ãããã®å¾ããã¦ã³ãã¼ããã¿ã³ã®
download屿§ãè¨å®ããã¾ããdownload屿§ã¯è«çå¤ã«ãããã¨ãã§ãã¾ããããã¦ã³ãã¼ããããã¡ã¤ã«ã®ååã¨ãã¦ä½¿ç¨ããæååã«è¨å®ãããã¨ãã§ãã¾ãã ãã®ããããã¦ã³ãã¼ããªã³ã¯ã®download屿§ã"RecordedVideo.webm"ã«è¨å®ãããã¨ã§ããã¿ã³ãã¯ãªãã¯ããã¨å 容ãåé²ãããåç»ã§ãã"RecordedVideo.webm"ã¨ããååã®ãã¡ã¤ã«ããã¦ã³ãã¼ãããããã«ãã©ã¦ã¶ã¼ã«æç¤ºãã¾ãã -
è¨é²ãããã¡ãã£ã¢ã®ãµã¤ãºã¨ç¨®é¡ã¯ã2 ã¤ã®åç»ã¨ãã¦ã³ãã¼ããã¿ã³ã®ä¸ã®ãã°é åã«åºåããã¾ãã
-
ãã¹ã¦ã®
Promiseã®catch()ã¯ãlog()颿°ãå¼ã³åºããã¨ã«ãã£ã¦ã¨ã©ã¼ããã°é åã«åºåãã¾ãã
忢ãã¿ã³ã®å¦ç
æå¾ã®ã³ã¼ãã§ã¯ãaddEventListener() ã使ç¨ãã¦åæ¢ãã¿ã³ã® click ã¤ãã³ãã®ãã³ãã©ã¼ã追å ãã¾ãã
stopButton.addEventListener(
"click",
() => {
stop(preview.srcObject);
},
false,
);
ããã¯å
ã»ã©èª¬æãã stop() 颿°ãå¼ã³åºãã ãã§ãã
çµæ
æ®ãã® HTML ã¨ä¸ã«ç¤ºããã¦ããªã CSS ããã¹ã¦ã¾ã¨ããã¨ã次ã®ããã«ãªããåä½ãã¾ãã
ãã¡ãã§ãã¢å ¨ä½ãè¦ããã¨ãã§ãããã©ã¦ã¶ã¼ã®éçºè ãã¼ã«ã使ã£ã¦ãã¼ã¸ãæ¤æ»ãã API ã使ç¨ããã¦ããæ¹æ³ã®èª¬æã«ã¯éè¦ã§ã¯ãªãã®ã§ä¸ã«è¡¨ç¤ºãããªãé¨åãå«ãã¦ããã¹ã¦ã®ã³ã¼ããè¦ã¦ã¿ããã¨ãã§ãã¾ãã
é¢é£æ å ±
- ã¡ãã£ã¢ãã£ããã£ã¨ã¹ããªã¼ã API ã©ã³ãã£ã³ã°ãã¼ã¸
MediaDevices.getUserMedia()