攝像頭操作指南

發表於2018-05-08

大多數智慧手機都有前置和後置攝像頭,當你在建立視訊應用時你可能想要選擇或者切換前置、後置攝像頭。

如果你開發的是一款聊天應用,你很可能會想呼叫前置攝像頭,但如果你開發的是一款拍照軟體,那麼你會更傾向於使用後置攝像頭。在這篇文章中我們將探討如何通過 mediaDevices API 和 media constraints (媒體約束) 選擇或者切換攝像頭。

準備工作

要跟著本文一起動手實踐你需要:

  • 一款擁有兩個可供測試的攝像頭的 iOS 或 Android 裝置,如果你的電腦有兩個攝像頭那也可以
  • ngrok 以便你能通過移動裝置輕鬆訪問到你的專案(也因為我覺得 ngrok 炒雞棒
  • 這個 GitHub 庫 的程式碼讓你起步

要獲取程式碼,先把這個專案 clone 下來然後 checkout 到 initial-project tag 下。

這個起步專案已經為你準備好了一些 HTML 和 CSS,所以我們就可以把注意集中到 JavaScript 上了。你可以直接開啟 index.html,但我建議你用一款 webserver 把這些檔案託管起來。我喜歡用 npm 的 serve 模組。我在這個庫裡已經引入了 serve,要使用它你需要先用 npm 安裝依賴然後啟動這個服務。

服務執行起來後,我們要用 ngrok 開啟一條隧道。serve 用 5000 埠託管檔案,要用 ngrok 開隧道通到這個埠,新開一個命令列視窗輸入以下命令:

好了你現在可以公網訪問這個站點了,你可以在移動裝置上開啟這個網站,這樣接下來就可以測試啦。確保你開啟的是 HTTPS 的 URL,因為我們用的 API 只能在安全環境下使用。

ngrok 視窗顯示了兩個你可以用的 URL,選 HTTPS 版本的。

網站看起來像這樣:

網站應該有一個 'Camera fun' 的標題,一個按鈕和一個空的下拉選擇框

獲取 media stream

我們的第一個任務是從任意攝像頭獲取視訊流顯示到螢幕上。完成這個之後我們再調研如何選擇特定攝像頭。開啟 app.js , 我們以從 DOM 中選擇按鈕和 video 元素開始:

當使用者點選或觸控按鈕時,我們要使用 mediaDevices API 請求攝像頭許可權。要這樣做,我們要呼叫 navigator.mediaDevices.getUserMedia ,傳遞 media constraints 物件。讓我們從簡單的 constraints 開始,我們只需要視訊,因此我們把 video 設定為 true,audio 設定為 false。

getUserMedia 會返回一個 promise,當 resolve 的時候我們就可以訪問到攝像頭的媒體流了。把媒體流賦值給 video 元素的 srcObj 屬性,我們就能從螢幕上看到視訊了。

儲存檔案,重新載入頁面然後點選按鈕。你應該能看到一個許可權對話方塊請求訪問你的攝像頭,一旦授權螢幕上就應該會出現視訊。在你的電腦和手機上試一試,我在我的 iPhone 上試了,被選擇的是前置攝像頭。

攝像頭應用, 在之前的空白區域出現了我的臉

如果你用的是一部 iPhone 手機,確認你在 Safari 裡嘗試,因為其他瀏覽器貌似並沒有效果。

可用攝像頭

media Devices API 為我們提供了一種列舉所有可用音訊和視訊輸入裝置的方式。我們要用 enumerateDevices 函式來為<select>框構建選項,這樣我們就能用它來選擇我們想看的攝像頭了。再次開啟 app.js,從 DOM 中選出<select>元素:

enumerateDevices 會返回一個 promise,所以讓我們寫一個用來接受 promise 結果的函式吧。這個函式接收一個 media device 陣列作為引數。

首先要做的是清空<select>現有的任何選項,然後插入一個空的
<option&gt。接著迴圈遍歷所有裝置,過濾掉非 “videoinput”型別的裝置。然後我們建立一個<option&gt元素,用裝置 ID 當作 option value,裝置 label 當作 option text。我們還要處理一種情況,如果一個裝置沒有 label 存在,生成一個簡單的 “Camera n” 作為標籤。

在 app.js 末尾呼叫一下 enumerateDevices。

重新整理頁面,看一下按鈕旁邊的下拉選擇框。如果你用的是 Android ,或者使用 Chrome 或 Firefox,你就能看到可用的攝像頭名稱了。

然而在 iPhone 上,你將看到我們函式生成的通用名字 “Camera 1” 和 “Camera 2”。在 iOS 上只有你授權至少一個攝像頭給網站,你才能看到攝像頭的名字。這讓在我們的介面上選擇攝像頭變得更不方便,因為儘管你能獲取到裝置 ID,你還是不能分辨哪個攝像頭是哪個。

在 iPhone 上你只能看到我們建立的攝像頭標籤, 'Camera 1' and 'Camera 2'.

目前我們還沒有處理下拉選擇框來改變攝像頭。在這之前,讓我們來看另一種能改變哪個攝像頭被使用的方法。

FacingMode

FacingMode 約束是一個可以用來選擇攝像頭的替代方法。這個方法比起通過 enumerateDevices 函式獲取 ID 來說更不那麼精確,但在移動裝置上效果非常好。對於這個約束,一共有四種選項可供你選擇:使用者(user),環境(environment),左(left),右(right)。MDN 上的文件對這個約束做了詳細介紹, 以本文的目的我們將使用使用者和環境模式,在移動裝置上它們正好對應到前置和後置攝像頭。

要使用 facingMode 約束我們需要修改呼叫 getUserMedia 時使用的 constraints 物件。對於 video 我們需要一個物件來控制具體的約束,而不是給一個 true 值。像這樣修改程式碼來使用前置攝像頭:

現在可以用你的手機測試。你應該能看到前置攝像頭被使用。更改 facingMode 為 environment 再試一次, 使用的應該是後置攝像頭。讓我們把這些程式碼和上面通過 enumerateDevices 獲取到的結果放到一塊兒,只要我們獲得了讀取攝像頭資料的許可權,就能構建一個攝像頭切換器了。

切換攝像頭

現在我們有在首次選擇時挑選使用者或環境攝像頭的程式碼了,但如果我們要切換攝像頭那還有一丟丟額外的工作要做。

首先,我們應該保留對當前流的引用,這樣當我們切換到另一個流時就能停止當前流。在 app.js 的最前面新增一個額外的變數和輔助函式來停止流中的軌。

函式 stopMediaTracks 接收一個媒體流,迴圈遍歷流中的每一個媒體軌道,呼叫 stop 方法停止媒體軌。

我們要在點選同一個按鈕時改變攝像頭,所以我們需要更新一下按鈕的事件監聽器了。如果當前有媒體流,我們應該先停止掉它。然後我們要檢查<select>元素看是否選擇了特定的裝置,然後基於此構造 media constraints 物件。

這樣修改按鈕的點選處理函式和 video constraints:

當我們想通過 deviceId 來選擇裝置時,使用 exact 約束。 可是對於 facingMode,我們沒有使用 exact 約束, 否則在一個無法識別有沒有使用者或環境模式的裝置上將會失敗,導致我們什麼媒體裝置也拿不到。

當我們獲得使用視訊的許可權時,在點選處理函式內,我們還要修改一些別的東西。把傳遞給函式的新流賦值給 currentStream 以便後續呼叫 stop,觸發另一次 enumerateDevices 的呼叫。

enumerateDevices 返回一個 promise,所以在我們的 then 函式中可以直接返回它,然後鏈式建立一個新的 then 把結果傳遞給 gotDevices 函式處理。

用以下程式碼替換現有的 getUserMedia 呼叫:

當你新增完所有的程式碼,你的 app.js 應該看起來像這個檔案一樣。重新整理頁面然後你就能愉快地選擇和改變攝像頭了。這個頁面在移動裝置和電腦上都有效。

最後的結果,這個動畫展示你能選擇和修改攝像頭,從檢視後置到前置攝像頭

下一步

我們已經看到如何通過使用 facingMode 和 deviceId 約束來選擇使用者的攝像頭。記住,在你有許可權使用攝像頭之前,facingMode 更可靠,但是選擇 deviceId 更加精確。你可以從 GitHub 倉庫 中得到所有本文中的程式碼,你也可以從這裡嘗試線上版的應用

如果你正在使用 Twilio Video 構建視訊應用,你可以在呼叫 connect 或者 createLocalVideoTrack 的時候使用這些 constraints。

對於視訊聊天來說,選擇和切換攝像頭是非常有用的功能,允許使用者在你的應用介面準確地選擇他們想用的攝像頭,並且還能做到在視訊通話時分享你的螢幕

還有哪些其他你想看到的在視訊聊天中有用的功能?或者對這個功能有什麼疑問?歡迎在評論中留言或者在 Twitter 上 @philnash

相關文章