WebView调用getUserMedia触发NotReadableError及过滤指定摄像头的技术咨询
我先理清楚你的场景和核心问题:Windows机器上接了两个摄像头,一个是通过SDK嵌入WPF应用的扫描器摄像头,另一个是WebView里用于年龄验证的普通摄像头。之前WebView会误选扫描器摄像头,你加了先请求媒体权限再枚举过滤的代码,但现在遇到的问题是——当扫描器SDK在占用摄像头时,navigator.mediaDevices.getUserMedia({ video: true }) 会直接触发NotReadableError,导致后续无法枚举设备。你想知道有没有办法直接过滤掉指定摄像头,或者在扫描器占用时也能正常获取可用设备列表。
一、无需提前调用getUserMedia就能过滤指定摄像头的思路
浏览器的设备枚举机制有个特点:在用户没有授予媒体权限前,enumerateDevices() 返回的设备label是空字符串,只有调用过getUserMedia并授权后,才会返回真实的设备名称。但你现在的问题是扫描器占用摄像头时,getUserMedia会失败,拿不到权限也就拿不到label来过滤。
这里有个替代方案:
- 你可以在扫描器SDK不占用摄像头的情况下,提前枚举一次所有设备,把扫描器摄像头的deviceId存下来(比如存在本地存储或者你的应用配置里)
- 后续枚举设备时,直接根据deviceId过滤,不用依赖label判断,示例代码如下:
// 提前存储的扫描器摄像头deviceId const SCANNER_DEVICE_ID = "xxx-xxx-xxx"; const devices = await navigator.mediaDevices.enumerateDevices(); const cameras = devices.filter( (d) => d.kind === 'videoinput' && d.deviceId !== SCANNER_DEVICE_ID );
这个方法的前提是扫描器的deviceId不会频繁变化,一般固定嵌入的扫描器deviceId是稳定的,除非你重新插拔或者更新驱动。
二、解决扫描器占用时getUserMedia报错的优化方案
当摄像头被其他进程(你的扫描器SDK)独占占用时,浏览器确实无法获取该摄像头的资源,这时候getUserMedia抛出NotReadableError是操作系统层面的资源独占限制,浏览器本身绕不开。不过你可以优化代码逻辑,减少对getUserMedia的依赖:
先尝试直接枚举设备
先调用enumerateDevices(),如果返回的设备列表里有真实的label(说明之前已经授权过媒体权限),直接过滤即可,不用再调用getUserMedia。针对性请求指定摄像头
如果你已经知道目标普通摄像头的deviceId,可以直接请求该摄像头,而不是请求所有视频设备,这样即使扫描器被占用,也不会影响普通摄像头的请求(只要它没被占用):
// 提前存储的目标普通摄像头deviceId const TARGET_CAMERA_ID = "yyy-yyy-yyy"; try { const permissionStream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: TARGET_CAMERA_ID } } }); permissionStream.getTracks().forEach(t => t.stop()); // 后续枚举设备或直接使用该摄像头 } catch (err) { if (err.name === 'NotReadableError') { console.log('目标摄像头或扫描器摄像头正在被占用,请检查后重试'); // 这里可以给用户弹出可视化提示,引导操作 } }
- 和扫描器SDK团队沟通资源共享方案
最根本的解决方法是和扫描器SDK的开发团队确认:是否支持摄像头资源的共享访问,或者是否提供临时释放/重新占用摄像头的API。比如在WebView需要使用摄像头时,调用SDK的释放接口,用完后再重新占用,这样就能避免资源冲突。
三、额外的调试小技巧
- 检查Windows系统权限:打开「设置-隐私和安全性-相机」,确认你的WPF应用以及WebView对应的浏览器内核(比如Edge内核)已经被授予相机访问权限
- 查看浏览器支持的媒体约束:调用
navigator.mediaDevices.getSupportedConstraints(),可以确认你使用的deviceId等约束是否被当前WebView支持
内容来源于stack exchange

