H5调用摄像头并切换摄像头

文章摘自:https://blog.csdn.net/weixin_43864427/article/details/105771863

webRTC polyfill 的库: https://github.com/webrtcHacks/adapter

第一次启用成功调用前置摄像头,第二次需要调用后置却黑屏或者失败

失败的原因很多,列举两个一开始我遇到的问题

  1. 前置摄像头调用后,摄像功能需要关闭后才能正常执行第二次调用,否则会报错:设备被占用。解决方法,在每次执行调用方法前,先关闭摄像设备。
1
2
3
4
5
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
})
}
  1. 调用后置API的方法还是无法唤醒后置摄像头,找到另外一个方法,通过查看手机摄像头ID,来直接唤醒后置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var deviceInfoId = "", // 摄像头ID    
num = 0, // 摄像头数量
carema = []; // 摄像头ID数组
// 在页面加载完成后获得设备ID数组
window.onload = navigator.mediaDevices.enumerateDevices().then(gotDevices);

function gotDevices(deviceInfos) {
for (let i = 0; i < deviceInfos.length; ++i) {
if (deviceInfos[i].kind === 'videoinput') {
carema.push(deviceInfos[i].deviceId)
}
}
deviceInfoId = carema[后置位置];
}

var constraints = {
audio: false,
video: {
deviceId: deviceInfoId,
// 放在app里面需要下面配置一下
"permissions": {
"audio-capture": {
"description": "Required to capture audio using getUserMedia()"
},
"video-capture": {
"description": "Required to capture video using getUserMedia()"
}
}
}
};

navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
var video = document.getElementById('video');
try {
window.stream = stream;
video.srcObject = stream;
} catch (error) {
video.src = window.URL.createObjectURL(stream);
}
this.localMediaStream = stream;
// 这个加不加好像没有影响
// video.play();
}).catch(function (err) {
console.log(err.name + ": " + err.message);
});

如果只是一部手机可以这样,但是测试了多部手机发现摄像头数组毫无规律可循,这个方法慎用。

如果页面上添加选择摄像设备的按钮的话,这个方法还是不错的。查看设备能调用几个摄像头链接如下:
https://webrtc.github.io/samples/src/content/devices/input-output/

部分手机打开摄像头以后是黑屏,但是拍照仍然可以成像

解决方案:做了很多处理都没用,最后发现只要把参数中的分辨率(width,height)删除掉,就解决了黑屏问题

成功调用后用canvas实现成像并适应屏幕大小

我这里的代码是取video的宽高然后复制给canvas,这样可以让canvas和video保持一致,只用给video设置宽度100%,高度调节成合适的值,就实现了适应手机屏幕。

1
2
3
4
5
6
7
8
9
10
11
12
13
var video = document.getElementById('video');
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
CHeight = video.clientHeight, //获取屏幕大小让canvas自适应
CWidth = video.clientWidth;
canvas.width = CWidth;
canvas.height = CHeight;
//localMediaStream 在data里定义一个{}
if (localMediaStream) {
ctx.drawImage(video, 0, 0, CWidth, CHeight);
var dataURL = canvas.toDataURL('image/jpeg'); //dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA'
img.src = dataURL;
}

video 成像镜像问题

API唤醒的前置摄像头是相反的,很不舒服很不舒服。

之后用css处理一下给video添加 transform: rotate(180deg),可以实现反转,但是还是没有达到和手机一样的效果。

这时候可以选择通过设备ID调用前置摄像头,前置摄像头的laval一直都是“default”,也有的是空值,但是也能实现。

配置代码如下:

1
2
3
4
5
6
7
 var constraints = window.constraints = {
audio: false,
video: {
sourceId: 'default',
facingMode: { exact: "user" }
}
};

完美调用自己手机的前置摄像头!!!

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11

<div @click='moveToCameraAVG()' v-cloak>
<img v-if="imginfo!==''" :src="imginfo"/>
<div class="warm_title2">点击自拍一张头像</div>
</div>
<video id="video" class="pic_video" playsinline autoplay x5-video-player-type="h5" style='object-fit:fill'></video>
<canvas id="canvas" class="canvas_pic" style='margin: 0;padding: 0;'></canvas>
<div class="bottom_div">
<div>拍照</div>
<img src='images/pic_btn.png' class="capture-btn" @click='captureAvg'/>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
export default {
// 头像相机
moveToCameraAVG() {
var self = this;
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
var constraints = window.constraints = {
audio: false,
video: {
sourceId: 'default',
facingMode: { exact: "user" }
}
};
navigator.mediaDevices.getUserMedia(constraints)
.then(function (stream) {
var video = document.getElementById('video');
try {
window.stream = stream;
video.srcObject = stream;
} catch (error) {
video.src = window.URL.createObjectURL(stream);
}
self.localMediaStream = stream;
video.play();
})
.catch(function (err) {
alert(err.name + ": " + err.message);
});
},
// 停止摄像机
stopCapture: function () {
var video = document.getElementById('video');
if (!video.srcObject) return
let stream = video.srcObject
let tracks = stream.getTracks();
tracks.forEach(track => {
track.stop()
})
},
// 头像照片
captureAvg() {
var vm = this;
var video = document.getElementById('video');
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
CHeight = video.clientHeight, //获取屏幕大小让canvas自适应
CWidth = video.clientWidth;
canvas.width = CWidth;
canvas.height = CHeight;
if (vm.localMediaStream) {
ctx.drawImage(video, 0, 0, CWidth, CHeight);
var dataURL = canvas.toDataURL('image/jpeg'); //dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA'
vm.imginfo = dataURL;
// 停止摄像机
video.pause();
this.stopCapture();
}
}
}
作者

spencer17x

发布于

2022-02-18

更新于

2024-03-12

许可协议

评论