本文是講述如何在瀏覽器中打開攝像頭,並且實時顯示在頁面上。想要實現這一功能,需要依賴WebRTC (Web Real-Time Communications) 這一實時通訊技術,它允許瀏覽器之間視頻流和音頻流或者其他任意數據的傳輸,當然其中包含了大量的API和協議,這些在這里都不做介紹,具體的標准還在完善之中,所以使用的方法有時候也需要考慮到兼容問題,那么回到主題,怎樣使用webRTC獲取視頻流。
首先對於html,我們需要一個video標簽來播放視頻(JS中添加也可以),當然畫布也是能夠實現的。使用video有些屬性必須要配置。
<video id="video" autoplay playsinline></video>
上面兩個屬性必須要添加,autoplay設置video自動播放,否則通信成功后頁面將會保留第一張靜止的畫面。playsinline是由於有些瀏覽器默認會開啟全屏播放,而全屏可能是畫面變成黑屏。
webRTC大部分瀏覽器支持,IE(Edge)15+, Safari 11+, IOS Safari 11.2+, Android 64+, QQ、百度部分支持,對於IOS必須系統在11以上,並且只有Safari支持,UC 瀏覽器不支持。
對於webRTC支持情況,可以采用以下代碼判斷。
if (navigator.mediaDevices === undefined ||
navigator.mediaDevices.enumerateDevices === undefined ||
navigator.mediaDevices.getUserMedia === undefined) {
if (navigator.mediaDevices === undefined) {
var fctName = 'navigator.mediaDevices'
} else if (navigator.mediaDevices.enumerateDevices === undefined) {
var fctName = 'navigator.mediaDevices.enumerateDevices'
} else if (navigator.mediaDevices.getUserMedia === undefined) {
var fctName = 'navigator.mediaDevices.getUserMedia'
} else {
console.assert(false)
}
alert('WebRTC issue-! ' + fctName + ' not present in your browser')
}
獲取video元素。
let video = document.querySelector('#video');
如果支持webRTC,那么就調用mediaDevice的方法來遍歷媒體對象,該方法返回一個promise對象,在第一個then方法的回調函數里面默認接收返回的媒體信息。
navigator.mediaDevices.enumerateDevices().then(function (sourceInfos) {
})
獲取sourceInfos,要拿到后置攝像頭的信息並不是通用的,PC、IOS、Android以及不同瀏覽器,處理方法都不同,這里只做IOS和Android區分。
if(!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {
}
如果不是IOS,那么就需要遍歷sourceinfos,獲取后置攝像頭再獲取視頻流。聲明一個exArray用來存放不同媒體設備的id,再通過id獲取攝像頭信息。
let exArray = [];
for (var i = 0; i < sourceInfos.length; ++i) {
if (sourceInfos[i].kind == 'videoinput') {
exArray.push(sourceInfos[i].deviceId);
}
}
這樣數組里面存放的都是攝像頭的id,而我們需要的是后置攝像頭,再調用獲取媒體信息的方法。
if (navigator.getUserMedia) {
// 該方法可以傳遞3個參數,分別為獲取媒體信息的配置,成功的回調函數和失敗的回調函數
navigator.getUserMedia({
audio: false, // 表明是否獲取音頻
video: { // 對視頻信息進行配置
optional: [{
'sourceId': exArray[1] // 下標為0是前置攝像頭,1為后置攝像頭,所以PC不能進入該判斷,否則畫面會保持在第一幀不動
}]
},
}, function successFunc(stream) {
// 對FireFox進行兼容,這里對返回流數據的處理不同
if (video.mozSrcObject !== undefined) {
//Firefox中,video.mozSrcObject最初為null,而不是未定義的,我們可以靠這個來檢測Firefox的支持
video.mozSrcObject = stream;
} else {
// 一般的瀏覽器需要使用createObjectURL對流數據進行處理,再交給video元素的src
video.src = window.URL && window.URL.createObjectURL(stream) || stream;
}
}, function errorFunc(e) {
alert('Error!' + e);
}); //success是獲取成功的回調函數
} else {
alert('Native device media streaming (getUserMedia) not supported in this browser.');
}
在成功的回調函數中,將返回的視頻流交給html中的video元素,這里src的方式實際是通過blob64的格式來傳輸的。
// ============================= IOS
在IOS中獲取視頻流現實的方式又不一樣了,我們需要調用mediaDevice的獲取媒體的方法,這也是最新的標准。首先需要些小小的配置:
// 這里對生成視頻進行配置
var userMediaConstraints = {
audio: false, // 是否獲取音頻
video: {
facingMode: 'environment', // 環境表示后置攝像頭,使用user表示采用前置
// 寬高的配置比較靈活,由於video一般都會顯示固定寬高比,所以使用ideal理想值即可
width: {
ideal: 1024,
// min: 1024,
// max: 1920
},
height: {
ideal: 768,
// min: 776,
// max: 1080
}
}
}
再調用方法獲取視頻就很簡單了,如下:
navigator.mediaDevices.getUserMedia(userMediaConstraints).then(function success(stream) {
video.srcObject = stream;
}).catch(function (error) {
alert(error.name + error.message)
});
整體如下:
<!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>web RTC</title>
</head>
<body>
<!-- 這里必須設置autoplay,否則視頻畫面靜止為第一張 -->
<!-- 必須設置為playsinline,默認全屏播放可能會導致黑屏 -->
<video id="video" autoplay playsinline></video>
<script>
// 習慣性寫在函數中,控制變量
;(function(){
// 由於IOS必須在版本11以上才能使用webrtc,並且只有Safari支持,所以做一個小小的判斷,限定在
if(/(iPhone|iPad|iPod|iOS)/i.test(window.navigator.userAgent) && navigator.vender.indexOf("apple") > -1) {
return;
}
/**
* =============實現在瀏覽器中打開攝像頭,並且將攝像頭內容顯示在頁面中
* 想要實現這一功能,需要了解webRTC(Web Real-Time Communication)網絡實時通話技術,它允許瀏覽器實現視頻、音頻、P2P文件分享等功能。
*/
// 開啟視頻功能,依賴window的navigator對象,采用getUserMedia方法,有版本差異,所以需要判斷區分
// 需要IE(Edge)15+, Safari 11+, IOS Safari 11.2+, Android 64+, UC 不支持, QQ、百度部分支持
// 所以首先需要對瀏覽器支持情況進行判斷
// 先判斷瀏覽器是否支持
if (navigator.mediaDevices === undefined ||
navigator.mediaDevices.enumerateDevices === undefined ||
navigator.mediaDevices.getUserMedia === undefined) {
// 再判斷具體是那個方法不支持,並向用戶顯示
if (navigator.mediaDevices === undefined) {
var fctName = 'navigator.mediaDevices'
} else if (navigator.mediaDevices.enumerateDevices === undefined) {
var fctName = 'navigator.mediaDevices.enumerateDevices'
} else if (navigator.mediaDevices.getUserMedia === undefined) {
var fctName = 'navigator.mediaDevices.getUserMedia'
} else {
console.assert(false)
}
alert('WebRTC issue-! ' + fctName + ' not present in your browser')
}
const video = document.querySelector('#video')
// 如果瀏覽器支持,該方法的更新是向后兼容,新版將所有功能都使用navigator.mediaDevices進行了封裝
navigator.mediaDevices.enumerateDevices().then(function (sourceInfos) {
// 如果支持新的方法,那么就使用新的方法來獲取,當然這是一種比較主流的判斷方法
// 如果是想舊的方法兼容,可以使用下面作為判斷條件,除IOS和PC以外,均使用舊的獲取方式
// !(navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) || !/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
/**
* 無論是舊的寫法還是新的標准,思路都是通過設備信息,獲取攝像頭的視頻流,通過轉換變成blob的格式交給video的src
*/
if (!navigator.mediaDevices.getUserMedia) {
// 聲明一個數組,用於裝載設備媒體設備的相關信息,由於回調中sourceInfos對象中攜帶有所有媒體對象的相關信息
// 這里對信息進行遍歷篩選,只選出攝像頭的Id並保存在數組中
var exArray = [];
for (var i = 0; i < sourceInfos.length; ++i) {
if (sourceInfos[i].kind == 'videoinput') {
exArray.push(sourceInfos[i].deviceId);
}
}
// 通過navigator的getUserMedia獲取攝像頭的視頻流,並在成功的回調中將視頻流交給video
getMedia();
function getMedia() {
if (navigator.getUserMedia) {
// 該方法可以傳遞3個參數,分別為獲取媒體信息的配置,成功的回調函數和失敗的回調函數
navigator.getUserMedia({
audio: false, // 表明是否獲取音頻
video: { // 對視頻信息進行配置
optional: [{
'sourceId': exArray[1] // 下標為0是前置攝像頭,1為后置攝像頭,所以PC不能進入該判斷,否則畫面會保持在第一幀不動
}]
},
}, successFunc, errorFunc); //success是獲取成功的回調函數
} else {
alert('Native device media streaming (getUserMedia) not supported in this browser.');
}
}
// 這里是獲取媒體信息成功的回調函數
function successFunc(stream) {
// 對FireFox進行兼容,這里對返回流數據的處理不同
if (video.mozSrcObject !== undefined) {
//Firefox中,video.mozSrcObject最初為null,而不是未定義的,我們可以靠這個來檢測Firefox的支持
video.mozSrcObject = stream;
} else {
// 一般的瀏覽器需要使用createObjectURL對流數據進行處理,再交給video元素的src
video.src = window.URL && window.URL.createObjectURL(stream) || stream;
}
}
// 獲取媒體信息失敗的回調
function errorFunc(e) {
alert('Error!' + e);
}
} else { // 當采用最新的標准方式獲取視頻時
// 這里對生成視頻進行配置
var userMediaConstraints = {
audio: false, // 是否獲取音頻
video: {
facingMode: 'environment' // 環境表示后置攝像頭,使用user表示采用前置
}
}
// 這里就采用新的方法來獲取視頻
navigator.mediaDevices.getUserMedia(userMediaConstraints).then(function success(stream) {
video.srcObject = stream;
}).catch(function (error) {
alert(error.name + error.message)
});
}
}).catch(function(error) {
alert(error.name + error.message)
})
})();
</script>
</body>
</html>
最后提供一個將視頻全屏顯示的方法,對於PC和要求不高的手機已經可以實現全屏
* {
margin: 0;
}
body {
overflow: hidden;
}
#video {
min-width: 100%;
min-height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
在手機上,這個還有一點點橫向滾動條,要想完全去掉,需要改動html,讓video自動適用全屏的div
html
<div id="wrapper"> <video id="video" autoplay playsinline></video> </div>
css
* {
margin: 0;
}
#wrapper {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
}

