Nuxt.jsでWebカメラで映像を撮影 & 画像の取得をしてみる
案件でブラウザからWebカメラにアクセスし、画像を取得する実装を刷ることになったのでサンプルを作ってみた。
Nuxt.js
初めて使ったけど学習コスト低くていいですね。もっと勉強したい。
環境
- MacOS: 10.15.6
- node.js: v14.12.0
- yarn: v1.22.5
- Nuxt.js: v.2.14.6
手順
1. Nuxt.jsでプロジェクトを作成する
$ yarn create nuxt-app webrtc-sample yarn create v1.22.5 [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies... [4/4] 🔨 Building fresh packages... success Installed "create-nuxt-app@3.3.0" with binaries: - create-nuxt-app create-nuxt-app v3.3.0 ✨ Generating Nuxt.js project in webrtc-sample ? Project name: webrtc-sample ? Programming language: JavaScript ? Package manager: Yarn ? UI framework: Buefy ? Nuxt.js modules: Axios ? Linting tools: ? Testing framework: None ? Rendering mode: Single Page App ? Deployment target: Server (Node.js hosting) ? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Version control system: Git 🎉 Successfully created project webrtc-sample
プロジェクトができたら yarn dev
で起動
これで http://localhost:3000
でブラウザで表示ができるようになる
この時点ではデフォルトのページが表示される
$ yarn dev
ソースコード
新規でページを作成し、そこにソースコードを記述する(index.vueを編集しても可)
今回は /pages/webrtc.vue
を作成
<template> <section class="section"> <div class="columns"> <div class="column"><video id="localvideo" ref="localVideo" autoplay v-bind:srcObject.prop="localstream" width="500" height="500"></video></div> </div> <div class="columns"> <div class="column"><canvas id="localPic" ref="localPicCanvas" width="640" height="350"></canvas></div> </div> <div class="buttons"> <b-button @click="getUserMedia">映像を取得する</b-button> <b-button @click="getUserPic">写真を撮る</b-button> </div> </section> </template> <script> export default { data: function() { return { localstream: undefined, canvas: undefined, movie: undefined, streamConst: { video: { width: 1280, height: 700 } } } }, mounted: function() { this.canvas = this.$refs.localPicCanvas; this.movie = this.$refs.localVideo; }, methods: { getLocalMediaStream: function(mediaStream) { this.localstream = mediaStream; }, handleLocalMediaStreamError: function(error) { console.error(`navigator.getUserMedia error: ${error}`); }, getUserMedia: function() { navigator.mediaDevices .getUserMedia(this.streamConst) .then(this.getLocalMediaStream) .catch(this.handleLocalMediaStreamError); }, getUserPic: function() { const ctx = this.canvas.getContext('2d'); ctx.drawImage(this.movie, 0, 0, 640, 350); } } } </script>
Webカメラで撮影する部分と写真を撮る部分は以下の通り
Webカメラで撮影する部分
<template> ... <div class="column"><video id="localvideo" ref="localVideo" autoplay v-bind:srcObject.prop="localstream" width="500" height="500"></video></div> ... <b-button @click="getUserMedia">映像を取得する</b-button> ... </template> <script> export default { data: function() { return { localstream: undefined, streamConst: { video: { width: 1280, height: 700 } } } }, ... methods: { getLocalMediaStream: function(mediaStream) { this.localstream = mediaStream; }, handleLocalMediaStreamError: function(error) { console.error(`navigator.getUserMedia error: ${error}`); }, getUserMedia: function() { navigator.mediaDevices .getUserMedia(this.streamConst) .then(this.getLocalMediaStream) .catch(this.handleLocalMediaStreamError); }, ... } } </script>
ブラウザからWebカメラにアクセスするには navigator.mediaDevices.getUserMedia()
を利用する
navigator.mediaDevices.getUserMedia()
はpromise
が利用できるのでthen(), catch()
を利用することができる
ストリームが取得できた場合、video要素
にバインドすると映像が表示できるようになる
このとき、ストリームをv-bind:src
で指定してもよいが(ストリームをURL.createObjectURL
で変換する必要あり)、URL.createObjectURL
が廃止予定のメソッドであるためv-bind:srcObject.prop
で指定するほうが良いらしい
写真を撮る部分
<template> ... <div class="column"><canvas id="localPic" ref="localPicCanvas" width="640" height="350"></canvas></div> ... <b-button @click="getUserPic">写真を撮る</b-button> ... </template> <script> export default { data: function() { return { canvas: undefined, movie: undefined, } }, mounted: function() { this.canvas = this.$refs.localPicCanvas; this.movie = this.$refs.localVideo; }, methods: { ... getUserPic: function() { const ctx = this.canvas.getContext('2d'); ctx.drawImage(this.movie, 0, 0, 640, 350); } } } </script>
撮影する部分はcanvas要素
を利用して描画するのみ
setInterval
を利用すれば指定ミリ秒後に写真を取り続けるといったことも可能になる