当前位置: 首页 > 知识库问答 >
问题:

javascript - 前端调用高拍仪报错OverconstrainedError?

柳项明
2024-12-04

前端调用高拍仪的时候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个答案

房育
2024-12-04

回答

对于你遇到的问题,以下是可能的解决方案和优化建议:

  1. 处理 OverconstrainedError

    OverconstrainedError 通常表示请求的媒体约束(如分辨率、帧率等)超出了设备的支持范围。为了解决这个问题,你可以:

    • 使用更灵活的约束:在请求摄像头时,尽量使用 ideal 而不是 exact 约束,这样可以允许浏览器选择最接近请求值的可用设置。
    • 添加回退机制:如果首次请求失败,可以尝试使用更宽松的约束再次请求。
    • 检测摄像头能力:在请求摄像头之前,可以使用 getCapabilities() 方法检测摄像头的支持能力,并据此设置合理的约束。
  2. 优化调用速度

    • 减少不必要的请求:避免在初始化过程中进行不必要的 getUserMedia 请求,例如,你可以在用户选择摄像头后再请求视频流。
    • 异步加载:使用异步操作(如 async/await)来避免阻塞主线程,提高应用性能。
    • 减少 DOM 操作:尽量减少 DOM 操作的频率,特别是在循环中,这可以显著提高页面渲染速度。
  3. 适配多种类型的高拍仪

    • 使用 enumerateDevices():通过 enumerateDevices() 方法列出所有可用的媒体输入和输出设备,让用户选择他们想要使用的设备。
    • 检测设备类型:在列出设备时,可以检查 device.kind 属性来确定设备类型(如视频输入设备、音频输入设备等),并据此提供不同的选项或界面。
    • 灵活的约束设置:对于不同类型的设备,根据其能力设置合理的约束。例如,对于低分辨率的摄像头,可以设置较低的分辨率和帧率。
  4. 代码优化

    • 合并函数:你的代码中有很多重复的代码块(如错误处理和设备选择),可以考虑将这些代码块提取到单独的函数中,以减少代码冗余并提高可读性。
    • 使用现代 API:你的代码中同时使用了 navigator.mediaDevices.getUserMedia 和一些过时的 API(如 navigator.webkitGetUserMedianavigator.mozGetUserMedia)。建议只使用标准的 navigator.mediaDevices.getUserMedia API,因为现代浏览器都支持这个 API。

综上所述,通过优化约束设置、减少不必要的请求、异步加载、减少 DOM 操作以及适配多种设备类型,你可以提高前端调用高拍仪的速度和兼容性。

 类似资料: