如何在Javascript中选择合适的后置摄像头?

umuewwlo  于 2023-03-21  发布在  Java
关注(0)|答案(3)|浏览(203)

我正在使用navigator.mediaDevices.getUserMedia在Web浏览器中从相机设备打开MediaStream。我的应用程序希望在WebAssembly中进行一些实时图像处理,为此,我需要直接从相机提供实时图像流。
我的解决方案在大多数设备上都很好用,但是,我在带有多个后置摄像头的设备上遇到了一个问题,比如三星Galaxy S10在谷歌Chrome for Android上。

const constraints = {
    audio: false,
    video: {
        width: { min: 640, ideal: 1280, max: 1920 },
        height: { min: 480, ideal: 720, max: 1080 },
        facingMode: { ideal: 'environment' },
    }
};
const stream = await navigator.mediaDevices.getUserMedia( constraints );

我总是打开一个错误的相机-宽镜头相机不支持自动对焦,并提供图像太扭曲我的代码。宽镜头相机是好的风景摄影,但它是可怕的条形码和文本扫描。
如何使用MediaTrackConstraints选择正确的摄像头?我也试过添加

focusMode: { ideal: 'continuous' }

到约束(根据MDN documentation,这应该是图像轨道的可能约束),但似乎不起作用。
我也试过枚举所有设备(从this SO answer),但我不知道如何正确选择正确的相机。
值得注意的是,下面的代码片段:

const devices = await navigator.mediaDevices.enumerateDevices();
devices.forEach( ( device: MediaDeviceInfo ) => {
    console.log( "Found device: " + JSON.stringify( device ) );
});

生成以下控制台输出:

Found device: {"deviceId":"default","kind":"audioinput","label":"","groupId":"4852f187ff6a41e6d3fb3ba41c4897f46bd8ff153579da6fcb8f485432a32f66"}
Found device: {"deviceId":"86d4706b0bf160ff12fa75535173edcc68d4fa7ad5e00ec186cb1285ff22869d","kind":"audioinput","label":"","groupId":"c2cfc78763f7668263b0033c44d0f906ca0f33264ebfa6b96e9846265a21ff09"}
Found device: {"deviceId":"4d5fecf5a3eee5d41812bb6c34efe6d25342af9448628b006561c7385a22ca6c","kind":"audioinput","label":"","groupId":"7a86866423279d7b1e12dbe585a14a677a2f2df4e41ec5d388b6c90f7319e88d"}
Found device: {"deviceId":"b46cd34041256d2cf72ed6e8500f71beb698a01b8c47c7c04801c20c47630978","kind":"videoinput","label":"camera2 1, facing front","groupId":"b1bd1a6ed8a87cd07ca0fa84744ae515b1ab2bed61cc257765c37d3426269af7"}
Found device: {"deviceId":"39d63e8a9764261b73785c90beb58399997a5a4de56b3238fff6676c738331a6","kind":"videoinput","label":"camera2 3, facing front","groupId":"a23c2f0e311c0ca0c80a56a8b5ff7c1f8aa093df4f6ac080b051c1d95f60a94e"}
Found device: {"deviceId":"4d5fecf5a3eee5d41812bb6c34efe6d25342af9448628b006561c7385a22ca6c","kind":"videoinput","label":"camera2 2, facing back","groupId":"9b6b1a429e0db2d5094ddebe205d23309464650d8bcd585b2fe4ae8196b86f1c"}
Found device: {"deviceId":"86d4706b0bf160ff12fa75535173edcc68d4fa7ad5e00ec186cb1285ff22869d","kind":"videoinput","label":"camera2 0, facing back","groupId":"0de9556e1253763d7203b3b9e5db313cf89e05dd4bdd4ea0c5aff52d2952cf11"}
Found device: {"deviceId":"default","kind":"audiooutput","label":"","groupId":"default"}

正确的相机有标签camera2 0, facing back,出于某种原因,Chrome总是选择camera2 2, facing back
当然,我可以通过标签硬编码相机的选择,但这只适用于三星Galaxy S10,我希望我的代码可以在任何具有多个后置摄像头的设备上工作。
我还没有尝试在iPhone 11 Pro上运行我的页面(它有3个后置摄像头),但它在华为Mate 30 Pro上工作正常(4个后置摄像头)和Oppo里诺9.还有,这个问题似乎与Android上的Google Chrome有关.当我在Firefox for Android上打开我的页面时浏览器要求我在关闭摄像机权限对话框后选择应该使用哪个摄像机,并且只有当多个摄像机满足给定的约束条件时。这是相当公平的,因为它可以选择正确的相机来执行扫描。我还没有尝试在三星互联网和Opera浏览器上打开我的页面。
由于Google Chrome是Android上最受欢迎的Web浏览器,我甚至会对Chrome特定的解决方案感到满意,但当然,最好的答案是在任何地方都可以使用。

编辑

根据jib的评论,我还尝试使用focusDistance

focusDistance: { min: 0.05, ideal: 0.12, max: 0.3 }

但没有用。
我还尝试使用以下代码片段记录getSettingsgetCapabilities的输出:

const devices = await navigator.mediaDevices.enumerateDevices();
let videoDevices: Array< MediaDeviceInfo > = [];
devices.forEach( ( device: MediaDeviceInfo ) => {
    if ( device.kind == 'videoinput' ) {
        console.log( "Found video device: " + JSON.stringify( device ) );
        videoDevices.push( device );
    }
});

console.log( '' );

// open every video device and dump its characteristics
for ( let i in videoDevices ) {
    const device = videoDevices[ i ];
    console.log( "Opening video device " + device.deviceId + " (" + device.label + ")" );
    const stream = await navigator.mediaDevices.getUserMedia( { video: { deviceId: { exact: device.deviceId } } } );
    stream.getVideoTracks().forEach( track => {
            const capabilities = track.getCapabilities();
            console.log( "Track capabilities: " + JSON.stringify( capabilities ) );
            const settings = track.getSettings();
            console.log( "Track settings: " + JSON.stringify( settings ) );
            console.log( '' );
        }
    )

    stream.getTracks().forEach( track => track.stop() );
}

输出如下所示:

Found video device: {"deviceId":"b46cd34041256d2cf72ed6e8500f71beb698a01b8c47c7c04801c20c47630978","kind":"videoinput","label":"camera2 1, facing front","groupId":"500dd57c6795399100a5ca8bf7f0cc4d7ed8b1bcb0877101d1bef7eb74921868"}
Found video device: {"deviceId":"39d63e8a9764261b73785c90beb58399997a5a4de56b3238fff6676c738331a6","kind":"videoinput","label":"camera2 3, facing front","groupId":"245693c8d34be77fe2f15be31b6054a19edb8ea9ed4116d966d2a03695bebebe"}
Found video device: {"deviceId":"4d5fecf5a3eee5d41812bb6c34efe6d25342af9448628b006561c7385a22ca6c","kind":"videoinput","label":"camera2 2, facing back","groupId":"c219595df2c2a430aea7007f64e6ce8fbfa783b038cf53069336361cc07e71af"}
Found video device: {"deviceId":"86d4706b0bf160ff12fa75535173edcc68d4fa7ad5e00ec186cb1285ff22869d","kind":"videoinput","label":"camera2 0, facing back","groupId":"96a68b6d6429786317e3b2c4773082604c5ab9a5cdaaf49c481878e40d67e987"}

Opening video device b46cd34041256d2cf72ed6e8500f71beb698a01b8c47c7c04801c20c47630978 (camera2 1, facing front)
Track capabilities: {"aspectRatio":{"max":3216,"min":0.0004528985507246377},"deviceId":"b46cd34041256d2cf72ed6e8500f71beb698a01b8c47c7c04801c20c47630978","facingMode":["user"],"frameRate":{"max":30,"min":0},"groupId":"500dd57c6795399100a5ca8bf7f0cc4d7ed8b1bcb0877101d1bef7eb74921868","height":{"max":2208,"min":1},"resizeMode":["none","crop-and-scale"],"width":{"max":3216,"min":1}}
Track settings: {"aspectRatio":1.3333333333333333,"deviceId":"b46cd34041256d2cf72ed6e8500f71beb698a01b8c47c7c04801c20c47630978","facingMode":"user","frameRate":30,"groupId":"500dd57c6795399100a5ca8bf7f0cc4d7ed8b1bcb0877101d1bef7eb74921868","height":480,"resizeMode":"none","width":640}

Opening video device 39d63e8a9764261b73785c90beb58399997a5a4de56b3238fff6676c738331a6 (camera2 3, facing front)
Track capabilities: {"aspectRatio":{"max":3968,"min":0.0003654970760233918},"deviceId":"39d63e8a9764261b73785c90beb58399997a5a4de56b3238fff6676c738331a6","facingMode":["user"],"frameRate":{"max":30,"min":0},"groupId":"245693c8d34be77fe2f15be31b6054a19edb8ea9ed4116d966d2a03695bebebe","height":{"max":2736,"min":1},"resizeMode":["none","crop-and-scale"],"width":{"max":3968,"min":1}}
Track settings: {"aspectRatio":1.3333333333333333,"deviceId":"39d63e8a9764261b73785c90beb58399997a5a4de56b3238fff6676c738331a6","facingMode":"user","frameRate":30,"groupId":"245693c8d34be77fe2f15be31b6054a19edb8ea9ed4116d966d2a03695bebebe","height":480,"resizeMode":"none","width":640}

Opening video device 4d5fecf5a3eee5d41812bb6c34efe6d25342af9448628b006561c7385a22ca6c (camera2 2, facing back)
Track capabilities: {"aspectRatio":{"max":4608,"min":0.00028935185185185184},"deviceId":"4d5fecf5a3eee5d41812bb6c34efe6d25342af9448628b006561c7385a22ca6c","facingMode":["environment"],"frameRate":{"max":60,"min":0},"groupId":"c219595df2c2a430aea7007f64e6ce8fbfa783b038cf53069336361cc07e71af","height":{"max":3456,"min":1},"resizeMode":["none","crop-and-scale"],"width":{"max":4608,"min":1}}
Track settings: {"aspectRatio":1.3333333333333333,"deviceId":"4d5fecf5a3eee5d41812bb6c34efe6d25342af9448628b006561c7385a22ca6c","facingMode":"environment","frameRate":60,"groupId":"c219595df2c2a430aea7007f64e6ce8fbfa783b038cf53069336361cc07e71af","height":480,"resizeMode":"none","width":640}

Opening video device 86d4706b0bf160ff12fa75535173edcc68d4fa7ad5e00ec186cb1285ff22869d (camera2 0, facing back)
Track capabilities: {"aspectRatio":{"max":4032,"min":0.00033068783068783067},"deviceId":"86d4706b0bf160ff12fa75535173edcc68d4fa7ad5e00ec186cb1285ff22869d","facingMode":["environment"],"frameRate":{"max":60,"min":0},"groupId":"96a68b6d6429786317e3b2c4773082604c5ab9a5cdaaf49c481878e40d67e987","height":{"max":3024,"min":1},"resizeMode":["none","crop-and-scale"],"width":{"max":4032,"min":1}}
Track settings: {"aspectRatio":1.3333333333333333,"deviceId":"86d4706b0bf160ff12fa75535173edcc68d4fa7ad5e00ec186cb1285ff22869d","facingMode":"environment","frameRate":60,"groupId":"96a68b6d6429786317e3b2c4773082604c5ab9a5cdaaf49c481878e40d67e987","height":480,"resizeMode":"none","width":640}

因此,除了不同的最大可用分辨率外,camera2 0, facing backcamera2 2, facing back之间没有任何区别。
我也试过三星互联网浏览器,它的行为与谷歌Chrome相同。
还有其他想法吗(除了 * 迭代所有的后置摄像头并选择分辨率最低的摄像头 *)?

2w2cym1i

2w2cym1i1#

由于这个问题在今天仍然存在,目前检测“非长焦”/“非广角透镜”相机的最佳方法是IMHO简单地检查torch参数。
(As这是字面上唯一的参数,在一些设备上不同的“标准”相机和其他。)
我是这么做的:

  • 打开默认摄像头流(= facingMode: { ideal: 'environment' },,找出是否存在torch
  • 如果不是,则关闭该相机流并针对每个设备进行迭代;尝试检测torch
  • 如果没有发现,则退回到第一摄像机;或者可能通过其他参数的某种组合而变得更好-例如focusDistance
  • (保存选定的相机ID到例如. cookie,所以下次是为这个用户更快)
nimxete2

nimxete22#

我把所有的摄像头按id排列成这样

navigator.mediaDevices.enumerateDevices()
        .then(function(devices) {
        
        for(;devices[i];){
        if(devices[i].kind == "videoinput"){
            that.aCameras.push(   [devices[i].deviceId , devices[i].label]   )
            j++;            
            }
        i++;
        }
    });

比在事件上翻转相机按下按钮,我这样做:

var defaultsOpts = { audio: false, video: true };
      defaultsOpts.video = { 
              deviceId: that.aCameras[that.currentCamera][0]
      };
      if ( that.aCameras.length-1 != that.currentCamera ){
          that.currentCamera++;
      }
      else{
          that.currentCamera = 0;
      }      
      navigator.mediaDevices.getUserMedia(defaultsOpts)
            .then(function (stream) {
                  vid.srcObject = stream;
                  localstream = stream;
                  vid.play();
               });
          
       });

这样,而不是使用用户/环境,我的问题就解决了。
希望也能帮助你。
你好阿维。

dgjrabp2

dgjrabp23#

我只是天真地选择了最后一个视频输入设备。不知道为什么,手机制造商似乎都把环境(背)正常透镜相机作为最后一个视频输入设备。参见https://www.reddit.com/r/javascript/comments/8eg8w5/choosing_cameras_in_javascript_with_the/

相关问题