本文是講述如何在瀏覽器中打開攝像頭,並且實時顯示在頁面上。想要實現這一功能,需要依賴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; }