MediaDevices: getUserMedia() 方法
getUserMedia() 方法是 MediaDevices 介面的一個方法,它會提示使用者授予許可權以使用媒體輸入裝置,從而生成一個包含所需媒體型別軌道的 MediaStream。
該媒體流可以包含,例如,一個影片軌道(由硬體或虛擬影片源生成,如攝像頭、影片錄製裝置、螢幕共享服務等),一個音訊軌道(同樣,由物理或虛擬音訊源生成,如麥克風、A/D 轉換器等),以及可能其他型別的軌道。
它返回一個 Promise,該 Promise 在成功時解析為一個 MediaStream 物件。如果使用者拒絕許可權,或者沒有可用的匹配媒體,則 Promise 會分別以 NotAllowedError 或 NotFoundError DOMException 拒絕。
注意: 返回的 Promise 既可能不解析也不拒絕,因為使用者不一定需要做出選擇,並且可能會忽略請求。
語法
getUserMedia(constraints)
引數
約束-
一個物件,用於指定請求的媒體型別,以及每種型別的任何要求。
constraints引數是一個包含video和audio兩個成員的物件,用於描述請求的媒體型別。必須指定其中一個或兩個。如果瀏覽器找不到所有符合給定約束條件的指定型別的媒體軌道,則返回的 Promise 會以NotFoundErrorDOMException拒絕。對於
video和audio,其值可以是布林值或物件。預設值為false。- 如果為媒體型別指定
true,則生成的媒體流必須包含該型別的軌道。如果由於任何原因無法包含,返回的 Promise 將被拒絕。 - 如果為媒體型別指定
false,則生成的媒體流不得包含該型別的軌道,否則返回的 Promise 將被拒絕。因為video和audio都預設為false,所以如果constraints物件不包含任何屬性或根本不存在,返回的 Promise 將始終被拒絕。 - 如果為媒體型別指定了一個物件,該物件將被讀取為
MediaTrackConstraints字典。
- 如果為媒體型別指定
返回值
一個 Promise,當成功獲取到請求的媒體時,其完成處理程式會接收一個 MediaStream 物件。
異常
AbortErrorDOMException-
儘管使用者和作業系統都已授予對硬體裝置的訪問許可權,並且沒有發生導致
NotReadableErrorDOMException的硬體問題,但如果發生阻止裝置使用的問題,則丟擲此異常。 InvalidStateErrorDOMException-
如果當前文件未完全啟用,則丟擲此異常。
NotAllowedErrorDOMException-
如果一個或多個請求的源裝置當前無法使用,則丟擲此異常。這會在瀏覽上下文不安全(即頁面使用 HTTP 而非 HTTPS 載入)時發生。如果使用者指定當前瀏覽例項不允許訪問裝置、使用者已拒絕當前會話的訪問許可權,或者使用者已全域性拒絕所有使用者媒體裝置的訪問許可權,也會發生這種情況。在支援使用 Permissions Policy 管理媒體許可權的瀏覽器上,如果 Permissions Policy 未配置為允許訪問輸入源,則返回此錯誤。
注意: 規範的舊版本使用
SecurityError代替此錯誤;SecurityError已具有新含義。 NotFoundErrorDOMException-
如果未找到滿足給定約束條件的指定型別的媒體軌道,則丟擲此異常。
NotReadableErrorDOMException-
如果使用者已授予使用匹配裝置的許可權,但作業系統、瀏覽器或網頁級別發生硬體錯誤,阻止了對裝置的訪問,則丟擲此異常。
OverconstrainedErrorDOMException-
如果指定的約束條件導致沒有符合請求標準的候選裝置,則丟擲此異常。該錯誤是一個
OverconstrainedError型別的物件,並具有一個constraint屬性,其字串值是無法滿足的約束條件的名稱,以及一個message屬性,其中包含解釋問題的可讀字串。注意: 由於此錯誤甚至可能在使用者尚未授予使用底層裝置的許可權時發生,因此它可能被用作 指紋識別 表面。
SecurityErrorDOMException-
如果呼叫
getUserMedia()的Document上停用了使用者媒體支援,則丟擲此異常。啟用和停用使用者媒體支援的機制由各個使用者代理自行決定。 TypeError-
如果指定的約束列表為空,或所有約束都設定為
false,則丟擲此異常。如果在不安全的環境中嘗試呼叫getUserMedia()也可能發生這種情況,因為在不安全的環境中navigator.mediaDevices是undefined。
隱私和安全
作為一項可能涉及重大隱私問題的 API,getUserMedia() 的規範規定了瀏覽器必須滿足的廣泛隱私和安全要求。
getUserMedia() 是一項強大的功能,只能在 安全上下文 中使用;在不安全上下文中,navigator.mediaDevices 是 undefined,從而阻止訪問 getUserMedia()。簡而言之,安全上下文是使用 HTTPS 或 file:/// URL 方案載入的頁面,或從 localhost 載入的頁面。
此外,始終需要使用者許可權才能訪問使用者的音訊和影片輸入。只有有效來源的視窗頂層文件上下文才能請求使用 getUserMedia() 的許可權,除非頂層上下文使用 Permissions Policy 明確授予給定 這樣做許可權。否則,使用者甚至不會被要求授予使用輸入裝置的許可權。
有關這些要求和規則、它們如何反映在您的程式碼執行的上下文中,以及瀏覽器如何管理使用者隱私和安全問題的更多詳細資訊,請繼續閱讀。
使用者隱私
作為一項可能涉及重大隱私問題的 API,getUserMedia() 在規範中對使用者通知和許可權管理有非常具體的要求。首先,getUserMedia() 必須始終在開啟任何媒體採集輸入(例如網路攝像頭或麥克風)之前獲得使用者許可。瀏覽器可能會提供一次性域許可權功能,但它們至少在第一次必須請求,並且如果使用者選擇這樣做,則必須明確授予持續許可權。
同樣重要的是通知規則。瀏覽器必須顯示一個指示器,表明攝像頭或麥克風正在使用中,這超出了任何可能存在的硬體指示器。它們還必須顯示一個指示器,表明已授予使用裝置進行輸入的許可權,即使該裝置當前未 actively 錄製。
例如在 Firefox 中,URL 位址列會顯示一個脈衝紅色圖示,指示正在錄製。如果許可權已到位但當前未進行錄製,則圖示為灰色。裝置的物理燈用於指示錄製是否當前處於活動狀態。如果您已將攝像頭靜音(所謂的“面部靜音”),您的攝像頭的活動指示燈會熄滅,表示攝像頭當前未主動錄製您,但不會放棄在靜音結束後恢復使用攝像頭的許可權。
安全
使用者代理 中的安全管理和控制有多種方式可能導致 getUserMedia() 返回與安全相關的錯誤。
許可權策略
適用於 getUserMedia() 的兩個 Permissions Policy 指令是 camera 和 microphone。
例如,此 HTTP 標頭將允許文件和任何從相同來源載入的嵌入式 元素使用攝像頭。
Permissions-Policy: camera=(self)
這將請求對當前來源和特定來源 https://mdn.club.tw 的麥克風訪問許可權。
Permissions-Policy: microphone=(self "https://mdn.club.tw")
如果您在 中使用 getUserMedia(),您可以僅請求該框架的許可權,這顯然比請求更普遍的許可權更安全。這裡,表示我們需要使用攝像頭和麥克風的能力。
<iframe src="https://mycode.example.net/etc" allow="camera; microphone">
</iframe>
基於加密的安全
getUserMedia() 方法僅在 安全上下文 中可用。安全上下文是瀏覽器合理地確信其中包含安全載入的文件(使用 HTTPS/TLS)並且與不安全上下文的暴露有限的上下文。如果文件未在安全上下文中載入,則 navigator.mediaDevices 屬性是 undefined,從而無法訪問 getUserMedia()。
在這種情況下嘗試訪問 getUserMedia() 將導致 TypeError。
文件源安全
由於 getUserMedia() 如果意外使用或未仔細管理安全性,可能會帶來明顯的安全問題,因此它只能在安全上下文中使用。有許多不安全的載入文件的方式,這些文件反過來可能會嘗試呼叫 getUserMedia()。以下是禁止呼叫 getUserMedia() 的情況示例:
示例
使用 getUserMedia()
通常,您將使用 navigator.mediaDevices 訪問 MediaDevices 單例物件,如下所示:
async function getMedia(constraints) {
let stream = null;
try {
stream = await navigator.mediaDevices.getUserMedia(constraints);
/* use the stream */
} catch (err) {
/* handle the error */
}
}
同樣,直接使用原始 Promise,程式碼如下:
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) => {
/* use the stream */
})
.catch((err) => {
/* handle the error */
});
注意: 如果當前文件未安全載入,navigator.mediaDevices 將是 undefined,您將無法使用 getUserMedia()。有關此問題以及與使用 getUserMedia() 相關的其他安全問題的更多資訊,請參閱安全。
以下是 constraints 引數的一些示例。
以下請求音訊和影片,沒有任何具體要求:
getUserMedia({
audio: true,
video: true,
});
儘管出於隱私原因無法訪問有關使用者攝像頭和麥克風的資訊,但應用程式可以使用額外的約束條件請求其需要和想要的攝像頭和麥克風功能。以下表示對 1280x720 攝像頭解析度的偏好:
getUserMedia({
audio: true,
video: { width: 1280, height: 720 },
});
瀏覽器將嘗試遵循約束,如果底層硬體支援,將返回匹配的軌道。如果不支援,瀏覽器可能會嘗試從底層硬體裁剪和縮小更高解析度的流以匹配約束(如果幀速率也受到約束,也可能會降低幀速率)。可以透過將 resizeMode 約束設定為 crop-and-scale 來強制執行此行為(或透過將其設定為 none 來停用)
getUserMedia({
audio: true,
video: { width: 1280, height: 720, resizeMode: "crop-and-scale" },
});
如果無法獲得精確匹配且源無需縮放,瀏覽器可能會返回其他解析度。
要要求某個功能並在其不可用時失敗,請使用關鍵字 min、max 或 exact(也即 min === max)。以下要求最小解析度為 1280x720:
getUserMedia({
audio: true,
video: {
width: { min: 1280 },
height: { min: 720 },
},
});
如果不存在具有此解析度或更高解析度的攝像頭,則返回的 Promise 將被 OverconstrainedError 拒絕,並且不會提示使用者。
行為差異的原因是關鍵字 min、max 和 exact 本質上是強制性的——而普通值和名為 ideal 的關鍵字則不是。這是一個完整的示例:
getUserMedia({
audio: true,
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 576, ideal: 720, max: 1080 },
},
});
ideal 值在使用時具有“引力”——這意味著瀏覽器將嘗試找到與給定理想值 適應距離 最小的設定(以及攝像頭,如果您有多個)。
普通值本質上是理想的,這意味著我們上面第一個解析度示例可以這樣寫:
getUserMedia({
audio: true,
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
},
});
並非所有約束都是數字。例如,在移動裝置上,以下將優先選擇前置攝像頭(如果可用)而不是後置攝像頭:
getUserMedia({
audio: true,
video: { facingMode: "user" },
});
要要求使用後置攝像頭,請使用:
getUserMedia({
audio: true,
video: {
facingMode: { exact: "environment" },
},
});
另一個非數字約束是 deviceId 約束。如果您有來自 mediaDevices.enumerateDevices() 的 deviceId,您可以使用它來請求特定裝置:
getUserMedia({
video: {
deviceId: myPreferredCameraDeviceId,
},
});
上述程式碼將返回您請求的攝像頭,如果該特定攝像頭不再可用,則返回另一個攝像頭。同樣,要要求使用特定攝像頭,您可以使用:
getUserMedia({
video: {
deviceId: {
exact: myExactCameraOrBustDeviceId,
},
},
});
寬度和高度
此示例優先選擇攝像頭解析度,並將生成的 MediaStream 物件分配給影片元素。
// Prefer camera resolution nearest to 1280x720.
const constraints = {
audio: true,
video: { width: 1280, height: 720 },
};
navigator.mediaDevices
.getUserMedia(constraints)
.then((mediaStream) => {
const video = document.querySelector("video");
video.srcObject = mediaStream;
video.onloadedmetadata = () => {
video.play();
};
})
.catch((err) => {
// always check for errors at the end.
console.error(`${err.name}: ${err.message}`);
});
幀速率
在某些情況下,例如帶有頻寬限制的 WebRTC 傳輸,較低的幀速率可能更可取。
const constraints = {
video: { frameRate: { ideal: 10, max: 15 } },
};
前置和後置攝像頭
在行動電話上。
let front = false;
document.getElementById("flip-button").onclick = () => {
front = !front;
};
const constraints = {
video: { facingMode: front ? "user" : "environment" },
};
注意: 在某些情況下,可能需要先釋放當前攝像頭朝向模式,然後才能切換到不同的模式。為確保攝像頭切換,建議在請求不同的朝向模式之前,透過呼叫軌道的“stop()”方法來釋放媒體資源。
規範
| 規範 |
|---|
| 媒體捕獲和流 # dom-mediadevices-getusermedia |
瀏覽器相容性
載入中…
另見
- 較舊的
Navigator.getUserMedia()傳統 API MediaDevices.enumerateDevices(): 列出可用的媒體裝置- WebRTC API
- 媒體捕獲和流 API
- 螢幕捕獲 API: 將螢幕內容捕獲為
MediaStream MediaDevices.getDisplayMedia(): 獲取包含螢幕內容的媒體流- 拍攝網路攝像頭照片: 一個關於如何使用
getUserMedia()拍攝靜態照片而非影片的教程