公司須要一個js原生的視頻錄像功能,以前沒用作了,寫個筆記記錄下。html
既然要錄像,就要獲取攝像頭權限。那咱們就先實現這一步再說。經過參閱一些資料得知,js可使用navigator.mediaDevices來獲取攝像頭。node
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo</title> </head> <body> <video id="video"></video> <button id="button">開始</button> </body> <script> const video = document.getElementById("video") button.addEventListener("click", function() { openCam() }) // 打開攝像頭 const openCam = function() { const constraints = { video: true, audio: false }; navigator.mediaDevices.getUserMedia(constraints) .then(function(stream) { /* 使用這個stream stream */ video.src = stream video.play() }) .catch(function(err) { console.log(err) /* 處理error */ }); } </script> </html>
在手機運行demo,會發現navigator.mediaDevices老是undefined,緣由是navigator.mediaDevices() 只有在如下三種環境中才能獲取到:git
一、localhost 域
二、開啓了 HTTPS 的域
三、使用 file:// 協議打開的本地文件
由於我是用的pc開發,也沒有攝像頭,手機沒發用localhost訪問,就用node搭建了一個https服務
a、生成證書文件github
一、打開git bash檢測openssl是否安裝:openssl version -a
二、生成私鑰key文件:openssl genrsa -out privatekey.pem 1024
三、經過私鑰生成CSR證書籤名:openssl req -new -key privatekey.pem -out certrequest.csr
四、經過私鑰和證書籤名生成證書文件:openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
b、編寫node代碼web
let https = require('https'); let fs = require('fs'); let express = require('express'); let app = express(); app.use(express.static('./public')); let options = { key: fs.readFileSync('./privatekey.pem'), cert: fs.readFileSync('./certificate.pem') } let https_server = https.createServer(options, app); https_server.listen(8089)
對於使用筆記本的同窗來講,直接用手機訪問localhost就能夠了。下面在提供一種在Chrome中使用http訪問的方法,在手機端測試下貌似不行,只有在pc上能夠。chrome
打開 chrome://flags/#unsafely-treat-insecure-origin-as-secure
將該 flag 切換成 enable 狀態
輸入框中填寫須要開啓的域名,譬如 http://example.com",多個以逗號分隔。
可是這是咱們並不能正常運行代碼,點擊開是會獲得一條報錯:8089/[object%20MediaStream] 404 (Not Found)。將then方法替換爲下面的代碼就好了express
.then(function(stream) { // 將攝像頭拍到的東西,經過video展示出來 video.srcObject = stream; video.onloadedmetadata = function(e) { video.play(); }; })
如今咱們能正常調起攝像頭了,可是默認打開的是後置攝像頭,只須要修改constraints就能夠了canvas
//修改constraints const constraints = { video: { facingMode: 'environment', }, audio: false, };
根據需求還須要一個轉換攝像頭的按鈕。總體思路比較簡單,當點擊按鈕是就關閉當前媒體,再從新打開一個相反的攝像頭bash
const change = document.getElementById('change') // environment:後置 user:前置 let facingMode = "environment" // 保存媒體流 let currentStream = null change.addEventListener("click", function() { facingMode = facingMode === "user" ? "environment" : "user" openCam() }) // 中止全部的媒體 function stopMediaTracks(stream) { stream.getTracks().forEach(track => { track.stop(); }); } // 修改openCam function openCam() { if (currentStream) stopMediaTracks(currentStream); const constraints = { video: { facingMode: facingMode, }, audio: false, }; navigator.mediaDevices.getUserMedia(constraints) .then(function(stream) { currentStream = stream ...... } }
基本的事情咱們都作到了,接下來就要錄製視屏, 代碼實現app
let allChunks = []; // 捕捉canvas的內容,轉化爲MediaStream對象 const stream2 = canvas.captureStream(60); // 建立一個錄製MediaRecorder 對象 const recorder = new MediaRecorder(stream2, { mimeType: 'video/webm;codecs=vp9' }); //開始錄製 function startRecording() { // 每10ms記錄一次,也就是執行ondataavailable recorder.start(10); } // 中止錄製 function stopAndblobDownload() { recorder.stop(); // 將錄製的數據流轉化爲Blob const fullBlob = new Blob(allChunks); // 生成一個路徑,可上傳下載 const downloadUrl = window.URL.createObjectURL(fullBlob); } // 將錄製的數據流保存起來 recorder.ondataavailable = e => { allChunks.push( e.data ); } function openCam() { ...... then(res => { navigator.mediaDevices.getUserMedia(constraints) .then(function(stream) { ...... // 把video寫入到canvas中 setInterval(() => { context.drawImage(video, 0, 0, 640, 480); }, Math.floor(1000 / 60)); } } }) }
以上就基本實現了錄製視頻功能。
首先咱們得用navigator.mediaDevices.getUserMedia獲取攝像頭權限,這裏注意mediaDevices使用的條件。將getUserMedia的成功回調函數(then)中獲得的媒體流,用video呈現,再用canvas(drawImage API)繪製vidoe。轉換(captureStream API)canvas爲MediaStream對象。經過MediaRecorder對象進行錄製。其實captureStream是HTMLMediaElement原型上的方法,全部HTMLMediaElement都能調用,也就是說video能直接轉換爲MediaStream對象。修改代碼:
// const stream2 = canvas.captureStream(60); const stream2 = video.captureStream(60); ... // setInterval(() => { // context.drawImage(stream, 0, 0, 640, 480); // }, Math.floor(1000 / 60)); ...
這樣咱們就不須要canvas。
第一次寫這種東西,不喜勿噴。完成demo