前端调用高拍仪的时候requestMediaPermissions先去获取权限,但是navigator.mediaDevices.getUserMedia调用参数如果改成video: true会报错OverconstrainedError,如果将参数改成 video: {width: 500, height: 500},有的摄像头又不支持也依然报错OverconstrainedError。
以下是我的代码,初始化的时候调用changeDevice() 这个方法
async function requestMediaPermissions() {
if (navigator.mediaDevices === undefined) {
layer.alert("未检测到设备,请检查是否已接入拍摄仪器或已安装驱动!", { icon: 0 });
return Promise.reject(new Error("设备未检测到"));
}
try {
const stream = await navigator.mediaDevices.getUserMedia(
{
audio: false,
video: {
width: { ideal: 4096 },
height: { ideal: 2160 }
}
}
);
console.log("摄像头权限获取成功!");
return stream;
} catch (err) {
console.error("获取摄像头权限失败:", err);
if (err.name === 'OverconstrainedError') {
layer.alert("摄像头权限获取失败,请检查设备是否已连接或已被其他应用占用!", { icon: 0 });
} else {
layer.alert("获取摄像头权限失败,请检查设备或允许权限!", { icon: 0 });
}
return Promise.reject(err);
}
}
async function getMediaStream(constraints) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
return stream;
} catch (err) {
console.error("获取摄像头权限失败:", err);
layer.alert("获取摄像头权限失败,请检查设备或允许权限!", {icon: 0});
return Promise.reject(err);
}
}
async function changeDevice() {
tcindex = layer.msg("电子设备加载检测中...", {shade: 0.3});
try {
await requestMediaPermissions();
const devices = await navigator.mediaDevices.enumerateDevices();
const cameraSelect = document.getElementById('cameraSelect');
cameraSelect.innerHTML = '';
const cameraDevices = await Promise.all(devices
.filter(device => device.kind === 'videoinput')
.map(async device => {
try {
const stream = await getMediaStream({video: {deviceId: {exact: device.deviceId}}});
const tracks = stream.getVideoTracks();
if (tracks.length > 0) {
let resolution = '未知';
// 检查 getCapabilities 是否存在
if (typeof tracks[0].getCapabilities === 'function') {
const capabilities = tracks[0].getCapabilities();
if (capabilities.width && capabilities.height) {
resolution = `${capabilities.width.max}x${capabilities.height.max}`;
}
} else {
// 如果 getCapabilities 不存在,尝试其他方式获取分辨率
const settings = tracks[0].getSettings();
if (settings.width && settings.height) {
resolution = `${settings.width}x${settings.height}`;
}
}
stream.getTracks().forEach(track => track.stop());
return {device, resolution};
}
return {device, resolution: '未知'};
} catch (err) {
console.error("获取摄像头设备信息失败:", err);
return {device, resolution: '未知'};
}
}));
cameraDevices.sort((a, b) => {
const resolutionA = a.resolution.split('x')[0];
const resolutionB = b.resolution.split('x')[0];
return resolutionB - resolutionA;
});
cameraDevices.forEach(camera => {
const option = document.createElement('option');
option.value = camera.device.deviceId;
option.text = camera.device.label || `无名摄像头 (${camera.resolution})`;
cameraSelect.appendChild(option);
});
layui.use('form', function () {
layui.form.render('select');
const selectElem = $('#cameraSelect');
if (selectElem.find('option').length >= 1) {
const firstOptionValue = selectElem.find('option:first').val();
console.log('摄像头ID:', firstOptionValue);
setCurrentDevice(firstOptionValue);
}
});
} catch (err) {
layer.alert(err.name + ": " + err.message, {icon: 2});
}
}
async function setCurrentDevice(deviceId) {
console.log("ID:" + deviceId)
const videoConstraints = {};
if (deviceId === '') {
videoConstraints.facingMode = 'environment';
} else {
videoConstraints.deviceId = {ideal: deviceId}; // 使用 ideal 而不是 exact
}
try {
const stream = await getMediaStream({video: videoConstraints});
const tracks = stream.getVideoTracks();
if (tracks.length > 0) {
var capabilities;
var width;
var height;
if (typeof tracks[0].getCapabilities === 'function') {
capabilities = tracks[0].getCapabilities();
width = capabilities.width.max;
ttWidth = width;
height = capabilities.height.max;
ttHeight = height;
} else {
// 如果 getCapabilities 不存在,尝试其他方式获取分辨率
capabilities = tracks[0].getSettings();
width = capabilities.width;
ttWidth = width;
height = capabilities.height;
ttHeight = height;
}
console.log(`摄像头分辨率:${width}x${height}`);
stream.getTracks().forEach(track => track.stop());
const constraints = {
video: {
...videoConstraints,
width: {exact: width},
height: {exact: height},
frameRate: {ideal: FPS}
}
};
// 添加回退选项
if (!await getUserMedia(constraints)) {
constraints.video.width = {ideal: width};
constraints.video.height = {ideal: height};
await getUserMedia(constraints);
}
}
} catch (err) {
layer.alert(err.name + ": " + err.message, {icon: 2});
}
}
async function getUserMedia(constraints) {
return new Promise((resolve, reject) => {
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints)
.then(function (stream) {
videoElement.srcObject = stream;
videoElement.play();
layer.close(tcindex);
resolve(true);
}).catch(function (error) {
console.log(error);
layer.alert('摄像头开启失败,请检查摄像头是否可用!', {icon: 2});
reject(error);
});
} else if (navigator.webkitGetUserMedia) {
navigator.webkitGetUserMedia(constraints, function (stream) {
videoElement.srcObject = stream;
videoElement.play();
layer.close(tcindex);
resolve(true);
}, function (error) {
console.log(error);
layer.alert('摄像头开启失败,请检查摄像头是否可用!', {icon: 2});
reject(error);
});
} else if (navigator.mozGetUserMedia) {
navigator.mozGetUserMedia(constraints, function (stream) {
videoElement.srcObject = stream;
videoElement.play();
layer.close(tcindex);
resolve(true);
}, function (error) {
console.log(error);
layer.alert('摄像头开启失败,请检查摄像头是否可用!', {icon: 2});
reject(error);
});
} else if (navigator.getUserMedia) {
navigator.getUserMedia(constraints, function (stream) {
videoElement.srcObject = stream;
videoElement.play();
layer.close(tcindex);
resolve(true);
}, function (error) {
console.log(error);
layer.alert('摄像头开启失败,请检查摄像头是否可用!', {icon: 2});
reject(error);
});
} else {
reject(new Error('浏览器不支持getUserMedia'));
}
});
}
调用摄像头如何速度方面加快一些,能够适配多种类型的高拍仪
1.动态调整分辨率: 在获取摄像头权限时,先尝试使用较低的分辨率启动摄像头,然后根据支持的分辨率进行设置。
2.逐步降级分辨率: 如果高分辨率不支持,可以逐步降低分辨率,直到找到一个支持的分辨率。
3.减少不必要的操作: 直接获取设备列表并选择合适的设备,避免重复操作。