ブラウザで写真が撮れたらなぁという場面に出くわしたので、APIを確認したところ、navigator.getUserMedia()は非推奨で、今度からはnavigator.mediaDevices.getUserMedia()を使うようにとのこと
音声、画像をキャプチャするMedia Capture and Streams APIでスナップショットを撮ってみました。
ガワの準備
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MediaDevices.getUserMedia()</title>
</head>
<body>
<button onclick="startVideo()">ビデオ開始!</button>
<button onclick="stopVideo()">ビデオ終了!</button>
<button onclick="snapshot()">写真撮影!</button>
<h2>JavaScriptでカメラ表示(・8・)</h2>
<video autoplay width="320" height="240"></video>
<h2>スナップショット!</h2>
<img/>
<canvas style="display: none" width="320" height="240"></canvas>
<script src="app.js"></script>
</body>
</html>
- クリックイベント用のボタンと
- キャプチャー用のvideoタグと
- スナップショットの結果を持たせるcanvasタグと
- (imgタグに流す画像を一時的に持たせるスペースのつもりなので、非表示のstyleにしてます)
- スナップショットの結果を表示させるimgタグです。
あれ、imgいらなかったかも
そういえば、video要素のautoplayがないと、ビデオは再生されるけどすごくカクカクになる。なんでだろう
動画をキャプチャする
ビデオ開始!ボタンで走るやつ
function startVideo() {
console.info('入出力デバイスを確認してビデオを開始するよ!');
Promise.resolve()
.then(function () {
return navigator.mediaDevices.enumerateDevices();
})
.then(function (mediaDeviceInfoList) {
console.log('使える入出力デバイスs->', mediaDeviceInfoList);
var videoDevices = mediaDeviceInfoList.filter(function (deviceInfo) {
return deviceInfo.kind == 'videoinput';
});
if (videoDevices.length < 1) {
throw new Error('ビデオの入力デバイスがない、、、、、。');
}
return navigator.mediaDevices.getUserMedia({
audio: false,
video: {
deviceId: videoDevices[0].deviceId
}
});
})
.then(function (mediaStream) {
console.log('取得したMediaStream->', mediaStream);
videoStreamInUse = mediaStream;
document.querySelector('video').src = window.URL.createObjectURL(mediaStream);
// 対応していればこっちの方が良い
// document.querySelector('video').srcObject = mediaStream;
})
.catch(function (error) {
console.error('ビデオの設定に失敗、、、、', error);
});
}
今回使うnavigator.mediaDevices.enumerateDevices()
とnavigator.mediaDevices.getUserMedia()
はPromiseを返してくるので、Promise.resolve().then()......then()の形で順番に、
- 端末が使用できる入出力デバイスを取る
- ビデオのMediaStreamをとる
- video要素に突っ込む
をしています。
enumerateDevices()
が、返すMediaDeviceInfo(の配列)のプロパティkindは
- videoinput
- audioinput
- audiooutput
のどれかと決まっているので、これをみて何のデバイスかを確認します。今回のお目当はdeviceInfo.kind == 'videoinput';
なやつ
getUserMedia()
引数で指定したStreamを取ってきてくれます。
{ audio: true, video: false }
とか
{ audio: false, video: { width: 1280, height: 720 } }
で、取ってきてほしいデバイスの希望?を出します。audio, video両方trueにしたら、何が返ってくるんだ?
今回は↑で取ってきたdeviceIdを持ってきて、video: {deviceId: hogehoge }
の形でデバイスを指定します。
本当はフロント、バックそれぞれにカメラがある端末の場合、
{ audio: true, video: { facingMode: { exact: "environment" } } }
とか
{ audio: true, video: { facingMode: "environment" } }
と書くといいらしいのですが、手元のスマホ端末だとデフォルト?のフロント側のカメラのStreamしか取得してくれなかった、、、.。これが出来ればわざわざdeviceIdを取ってこなくてもいいと思うのですが、、、、、、fmmm
動画を停止する
案外スマートフォンでキャプチャしたままにするとまぁあったかくなってしまうので、止める処理も書いてみます。っといってもここのやつそのまま
//ビデオ停止!ボタンで走るやつ
function stopVideo() {
console.info('ビデオを止めるよ!');
videoStreamInUse.getVideoTracks()[0].stop();
if (videoStreamInUse.active) {
console.error('停止できかた、、、', videoStreamInUse);
} else {
console.log('停止できたよ!', videoStreamInUse);
}
}
スナップショットを取る
videoタグの内容をCanvasRenderingContext2D.drawImage()
でcanvas要素に流し込めるので、これとHTMLCanvasElement.toDataURL()
を組み合わせて、DataURI形式の画像をimgタグで表示させてみます。
function snapshot() {
console.info('スナップショットをとるよ!');
var videoElement = document.querySelector('video');
var canvasElement = document.querySelector('canvas');
var context = canvasElement.getContext('2d');
context.drawImage(videoElement, 0, 0, videoElement.width, videoElement.height);
document.querySelector('img').src = canvasElement.toDataURL('image/webp');
}
初めてアクセスした時は権限が聞かれるので許可して、、、、、
やったー!(・8・)