本文是讲述如何在浏览器中打开摄像头, 并且实时显示在页面上. 想要实现这一功能, 需要依赖 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;
- }
来源: https://www.cnblogs.com/zzmiaow/p/9033894.html