Recently, due to business reasons, I needed to access and debug various webcams on the Web page, and encountered a lot of strange problems (that is, I could read the brand of the camera, but not the resolution of the camera). So I sorted out this article as a memo, hoping to help similar friends.

Based on the code

navigator.mediaDevices.getUserMedia({ audio: false.video: true }).then(async (stream) => {

    let video = document.getElementById('#video')
	
    // Compatibility monitoring
    if( 'srcObject' in video ) {
        
        video.srcObject = stream
    } else {
        // On browsers that support srcObject, this is no longer supported
        video.src = URL.createObjectURL(stream)
    }
    
    await video.play()
})
Copy the code

compatibility

From the compatibility of Caniuse, the overall compatibility is general, and IE series browsers are not supported at all. IOS not only requires iOS 11 or higher versions, but also cannot be called through API in the embedded page of APP.

Various problems encountered in development

  1. The browser console prompt mediaDevices. GetUserMedia is not a function

    Due to the limited by the browser, the navigator. MediaDevices. GetUserMedia under the HTTPS protocol can be normal use, and under the HTTP protocol allows only localhost / 127.0.0.1 visit these two domain name, so in the development of disaster during processing, Before going online, check whether the production environment uses HTTPS.

    let mediaDevices = navigator.mediaDevices || null
    
    if( mediaDevices === null ) {
        
        console.warn('Please make sure that you are using HTTPS)
        return 
    }
    
    mediaDevices.getUserMedia({ audio: false.video: true }).then(async (stream) => {})
    Copy the code
  2. Obtain the hardware parameters of the camera

    There are two main hardware parameters I need to use in project development: brand and resolution. Access to the brand name of cameras are relatively simple, can be directly through the mediaDevices. EnumerateDevices () to obtain a list of can use computer peripherals, through kind field filtering out of the camera.

    if(! navigator.mediaDevices || ! navigator.mediaDevices.enumerateDevices) {console.log("Browser does not support enumerateDevices attribute")
      return
    }
    
    navigator.mediaDevices.enumerateDevices().then((devices) = > {
        
        let devicesList = devices.filter((device) = > device.kind === 'videoinput')
        
        // devicesList -> [{ kind: 'videoinput', name: 'FaceTime HD Camera (Built-in)', deviceId: xxx }]
        // The deviceId obtained from devicesList can be used to switch the camera
        / / the specific methods: mediaDevices. GetUserMedia ({audio: false, video: {deviceId}})
    })
    Copy the code

    The resolution cannot be obtained directly through the official API. The reason checked from MDN is to protect the personal privacy of users, and the resolution is within the scope of protection. (I’m curious why resolution is private.)

    MDN:

    For privacy reasons, the user’s camera and microphone information cannot be accessed

    However, it is not completely impossible to obtain. Since the content admitted by the camera can be played on the web page through the video label, and the size of the video label will be set to the same size as the camera by default, the resolution of the camera can be obtained by obtaining the size of the video.

    After the test, the value obtained is not affected by the style, so the size of the video can be controlled by the style, but the resolution will not be affected.

    let mediaDevices = navigator.mediaDevices || null
    
    if( mediaDevices === null ) {
        
        console.warn('Please make sure that you are using HTTPS)
        return 
    }
    
    mediaDevices.getUserMedia({ audio: false.video: true }).then(async (stream) => {
        
        let video = document.getElementById('#video')
    
        video.srcObject = stream
    
        await video.play()
        
     	// 1280,720   
        console.log(video.videoWidth, video.videoHeight)
    })
    Copy the code
  3. Troubleshoot errors such as no camera or no user permission

    GetUserMedia itself integrates several common error prompts, such as common no camera, no permission to use, etc., and can handle most of the similar errors through catch.

    let mediaDevices = navigator.mediaDevices || null
    
    if( mediaDevices === null ) {
    
        console.warn('Please make sure that you are using HTTPS)
        return 
    }
    
    mediaDevices.getUserMedia({ audio: false.video: true }).then(async (stream) => {
    
        let video = document.getElementById('#video')
    
        video.srcObject = stream
    
        await video.play()
    
    }).catch((error) = > {
    
        let message = error.message || error,
            response = {
                'permission denied': 'Browser forbids the use of camera on this page, please enable relevant permissions'.'requested device not found': 'No camera detected'
            }
    
        alert(response[ message.toLowerCase() ] || 'Unknown error')})Copy the code
  4. Camera Pull out Check

    Mobile phones do not need to check whether the camera is removed because the camera is built-in. However, when the data cable of the camera is pulled out on the PC, the state of the camera needs to be monitored.

    The initial thought was that getUserMedia might catch an error when the camera was pulled out. However, after many experiments, getUserMedia will not respond to the error that the camera cannot be found when the camera is pulled out. It is not feasible to directly monitor the camera through catch.

    When I was almost out of ideas, I saw this sentence in the getUserMedia document:

    GetUserMedia returns a Promise, and the successful Promise callback takes a MediaStream object as its argument.

    MediaStream is an object that receives a stream of multimedia (including audio and video) content. When printed on the Console of Google Chrome (not tested in other browsers), its property values are as follows:

    Id is the unique identifier of the MediaStream object, active is whether the current content stream is active, and the following fields are hooks provided by Google Chrome.

    As soon as the camera is removed, active changes from True to False and the onInactive hook is triggered, making things much easier with status listening. After testing the code, it is also effective for users to change camera permissions.

    // Check whether the camera is online
    let cameraIsOnline = false
    
    const loadWebCamera = () = > {
        
        let mediaDevices = navigator.mediaDevices || null
    
        if( mediaDevices === null ) {
    
            console.warn('Please make sure that you are using HTTPS)
            return 
        }
    
        mediaDevices.getUserMedia({ audio: false.video: true }).then(async (stream) => {
    
            let video = document.getElementById('#video')
    
            video.srcObject = stream
            
            // Compatibility processing
            if( stream.oninactive === null ) {
                // Listen for flow interruption, and then call itself again for status monitoring
                stream.oninactive = () = > loadWebCamera()
            }
    
            await video.play()
    
            cameraIsOnline = true
    
        }).catch((error) = > {
    
            let message = error.message || error,
                response = {
                    'permission denied': 'Browser forbids the use of camera on this page, please enable relevant permissions'.'requested device not found': 'No camera detected'.'could not start video source': 'Unable to access camera, please reinsert and try again'
                }
    		
            cameraIsOnline = false
            alert(response[ message.toLowerCase() ] || 'Unknown error')})}Copy the code

    However, compatibility is also very urgent, there are a lot of fields are the proposal stage, the development stage is recommended to do compatibility treatment, to prevent production environment problems.