Web調用網絡攝像頭及各種錯誤處理

最近因爲業務的緣由,須要在Web端頁面接入調試各種的網絡攝像頭,遇到了不少匪夷所思的問題(說的就是讀得出攝像頭的品牌,讀不出攝像頭的分辨率)。因而整理了這篇文章做爲備忘錄,也但願能幫到有相似的小夥伴們。javascript

基礎代碼

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

    let video = document.getElementById('#video')
	
    // 兼容性監測
    if( 'srcObject' in video ) {
        
        video.srcObject = stream
    } else {
        // 在支持srcObject的瀏覽器上,再也不支持使用這種方式
        video.src = URL.createObjectURL(stream)
    }
    
    await video.play()
})

兼容性

getUserMedia兼容性

caniuse的兼容性來看,總體兼容性通常,IE系列瀏覽器徹底不支持,iOS不只須要iOS 11以上的版本,並且在APP的嵌入式頁面也沒法經過api進行調用。java

開發遇到的各類問題

  1. 瀏覽器控制檯提示mediaDevices.getUserMedia is not a functionapi

    因爲受瀏覽器的限制,navigator.mediaDevices.getUserMediahttps協議下是能夠正常使用的,而在http協議下只容許localhost/127.0.0.1這兩個域名訪問,所以在開發時應作好容災處理,上線時則須要確認生產環境是否處於https協議下。瀏覽器

    let mediaDevices = navigator.mediaDevices || null
    
    if( mediaDevices === null ) {
        
        console.warn(`請肯定是否處於https協議環境下`)
        return 
    }
    
    mediaDevices.getUserMedia({ audio: false, video: true }).then(async (stream) => {})
  2. 獲取攝像頭的硬件參數網絡

    我在項目開發中須要用到的硬件參數主要有兩種:品牌,分辨率。獲取攝像頭的品牌名稱相對來講比較簡單,可直接經過mediaDevices.enumerateDevices()獲取電腦上可以使用的外設列表,經過kind字段過濾出攝像頭。async

    if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
        
      console.log("瀏覽器不支持enumerateDevices屬性")
      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 }]
        // 在devicesList獲取到的deviceId能夠用於切換攝像頭
        // 具體方法:mediaDevices.getUserMedia({ audio: false, video: { deviceId } })
    })

    分辨率則不能直接經過官方的api獲取到,從MDN上查到的理由是爲了保護用戶的我的隱私,而分辨率就在保護的範疇內。(我的很是好奇分辨率爲啥是隱私?)ide

    MDN原文(連接):函數

    因爲隱私保護的緣由,沒法訪問用戶的攝像頭和麥克風信息測試

    但也並非徹底沒法獲取到,因爲能夠經過video標籤在網頁上播放攝像頭中所錄取到的內容,而video標籤會默認將大小設置爲與攝像頭相同的大小,所以經過獲取video的大小來獲取攝像頭的分辨率。ui

    通過測試,獲取到的值不受樣式的影響,因此能夠經過樣式控制video的大小,可是不會影響到分辨率。

    let mediaDevices = navigator.mediaDevices || null
    
    if( mediaDevices === null ) {
        
        console.warn(`請肯定是否處於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)
    })
  3. 無攝像頭/無使用權限等錯誤的處理

    getUserMedia自己集成了幾個比較常見的錯誤提示,好比常見的無攝像頭、無使用權限等,經過catch能處理大部分相似的錯誤。

    let mediaDevices = navigator.mediaDevices || null
    
    if( mediaDevices === null ) {
    
        console.warn(`請肯定是否處於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': '瀏覽器禁止本頁面使用攝像頭,請開啓相關的權限',
                'requested device not found': '未檢測到攝像頭'
            }
    
        alert(response[ message.toLowerCase() ] || '未知錯誤')
    })
  4. 攝像頭拔出檢查

    手機端因爲攝像頭是手機自帶的,因此通常不須要對攝像頭是否拔出進行檢查。但在PC上有拔出攝像頭數據線的狀況發生,這種時候就須要對攝像頭的狀態進行監控。

    最開始想到的是,getUserMedia在攝像頭拔出時可能會經過catch報錯。然而通過屢次的實驗,getUserMedia在攝像頭拔出時,不會響應找不到攝像頭的錯誤,想經過catch直接監控這種方法並不可行。

    在幾乎沒有思路的時候,在getUserMedia文檔上看到了這麼一句話:

    getUserMedia返回一個 Promise , 這個Promise成功後的回調函數帶一個 MediaStream 對象做爲其參數。

    MediaStream是接收多媒體(包括音頻、視頻)內容流的一個對象,在谷歌瀏覽器(其餘瀏覽器未測試)的控制檯上打印以後,其屬性值以下:

    id是MediaStream對象的惟一標識符,active是當前內容流是否處於活動狀態,下面幾個字段則是谷歌瀏覽器提供的鉤子。

    MediaStream下的方法

    在攝像頭拔出的一瞬間,active會從true變動爲false,同時觸發oninactive鉤子,有了狀態監聽以後事情就簡單了許多。代碼通過測試後發現,對用戶變動攝像頭權限也有效。

    // 判斷攝像頭是否在線
    let cameraIsOnline = false
    
    const loadWebCamera = () => {
        
        let mediaDevices = navigator.mediaDevices || null
    
        if( mediaDevices === null ) {
    
            console.warn(`請肯定是否處於https協議環境下`)
            return 
        }
    
        mediaDevices.getUserMedia({ audio: false, video: true }).then(async (stream) => {
    
            let video = document.getElementById('#video')
    
            video.srcObject = stream
            
            // 兼容性處理
            if( stream.oninactive === null ) {
                // 監聽流中斷,流中斷後將從新進行調用自身進行狀態監測
                stream.oninactive = () => loadWebCamera()
            }
    
            await video.play()
    
            cameraIsOnline = true
    
        }).catch((error) => {
    
            let message = error.message || error,
                response = {
                    'permission denied': '瀏覽器禁止本頁面使用攝像頭,請開啓相關的權限',
                    'requested device not found': '未檢測到攝像頭',
                    'could not start video source': '沒法訪問到攝像頭,請從新插拔後重試'
                }
    		
            cameraIsOnline = false
            alert(response[ message.toLowerCase() ] || '未知錯誤')
        })
    }

    不過,兼容性也很是地捉急,也有不少字段都是提案階段,開發階段建議作好兼容性處理,防止生產環境出現問題。

相關文章
相關標籤/搜索