簡介
長期以來,音訊/視訊擷取功能一直是網頁開發的「聖杯」。多年來,我們都必須仰賴瀏覽器外掛程式 (Flash 或 Silverlight) 才能完成工作。把握機會!
HTML5 就能派上用場。雖然可能不明顯,但 HTML5 的興起已帶來裝置硬體存取權的激增。地理位置 (GPS)、方向 API (加速度計)、WebGL (GPU) 和 Web Audio API (音訊硬體) 都是絕佳範例。這些功能非常強大,可公開高層級的 JavaScript API,並位於系統的基本硬體功能之上。
本教學課程將介紹新的 GetUserMedia API,讓網路應用程式存取使用者的攝影機和麥克風。
getUserMedia() 的發展歷程
如果您不瞭解這段歷史,我們如何開發出 getUserMedia()
API 的過程相當有趣。
過去幾年來,已演變出多種「媒體擷取 API」。許多人意識到需要能夠在網路上存取原生裝置,但這導致大家紛紛提出新的規格。情況變得非常混亂,因此 W3C 最終決定成立工作組。他們的唯一目的?快來體驗這場瘋狂的賽事!裝置 API 政策 (DAP) 工作團隊的任務是整合並標準化大量提案。
我會盡量總結 2011 年發生的情況…
第 1 輪:HTML 媒體擷取
HTML 媒體擷取是 DAP 首次嘗試在網路上標準化媒體擷取作業。方法是多載 <input type="file">
,並為 accept
參數新增值。
如要讓使用者透過網路攝影機拍攝自己的快照,可以使用 capture=camera
:
<input type="file" accept="image/*;capture=camera">
錄製影片或音訊的步驟類似:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
很棒吧?我特別喜歡它重複使用檔案輸入內容。就語意而言,這很有道理。這個「API」的缺點是無法即時套用效果 (例如將即時網路攝影機資料算繪至 <canvas>
並套用 WebGL 篩選器)。HTML 媒體擷取功能只允許您錄製媒體檔案或拍攝即時快照。
支援:
- Android 3.0 瀏覽器 - 首批實作項目之一。請觀看這部影片,瞭解實際做法。
- Android 版 Google Chrome (0.16)
- Firefox Mobile 10.0
- iOS6 Safari 和 Chrome (部分支援)
第 2 回合:裝置元素
許多人認為 HTML 媒體擷取功能限制太多,因此出現了支援任何類型 (未來) 裝置的新規格。毫不意外,這項設計需要一個新元素,也就是 <device>
元素,而這個元素後來成為 getUserMedia()
的前身。
Opera 是首批根據 <device>
元素建立初始實作的瀏覽器之一,不久後 (確切來說是同一天),WhatWG 決定捨棄 <device>
標記,改用另一個新興技術,這次是名為 navigator.getUserMedia()
的 JavaScript API。一週後,Opera 發布了新版本,支援更新後的 getUserMedia()
規格。同年稍晚,Microsoft 也加入行列,發布了支援新規格的 IE9 實驗室。
<device>
的內容如下所示:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
支援:
很抱歉,沒有任何已發布的瀏覽器包含 <device>
。
我想這樣就少了一個需要擔心的 API :) 不過 <device>
確實有兩項優點:1.) 語意明確,2.) 容易擴充,可支援音訊/視訊裝置以外的裝置。
深呼吸。這個領域的變化速度很快!
第 3 回合:WebRTC
<device>
元素最終也難逃被淘汰的命運。
由於WebRTC (WebRTC) 專案規模擴大,我們加快了尋找合適擷取 API 的速度。該規格由 W3C WebRTC 工作小組監督。Google、Opera、Mozilla 和其他幾家公司都已實作這項功能。
getUserMedia()
與 WebRTC 相關,因為這是該組 API 的閘道。可存取使用者的本機攝影機/麥克風串流。
支援:
getUserMedia()
自 Chrome 21、Opera 18 和 Firefox 17 起支援。
開始使用
有了 navigator.mediaDevices.getUserMedia()
,我們終於可以存取網路攝影機和麥克風輸入內容,不必使用外掛程式。
現在只要撥打電話就能存取攝影機,不必安裝任何應用程式。這項功能直接內建於瀏覽器中,心動了嗎?
特徵偵測
功能偵測是簡單的檢查,用來確認 navigator.mediaDevices.getUserMedia
是否存在:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
取得輸入裝置的存取權
如要使用網路攝影機或麥克風,我們需要要求權限。
navigator.mediaDevices.getUserMedia()
的第一個參數是物件,用於指定要存取每種媒體類型的詳細資料和需求。舉例來說,如要存取網路攝影機,第一個參數應為 {video: true}
。如要同時使用麥克風和攝影機,請傳遞 {video: true, audio: true}
:
<video autoplay></video>
<script>
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((localMediaStream) => {
const video = document.querySelector("video");
video.srcObject = localMediaStream;
})
.catch((error) => {
console.log("Rejected!", error);
});
</script>
好的。這到底是怎麼一回事?媒體擷取是新 HTML5 API 共同運作的絕佳範例。它會與其他 HTML5 好友 <audio>
和 <video>
搭配運作。
請注意,我們並未設定 src
屬性,也沒有在 <video>
元素中加入 <source>
元素。我們不是提供媒體檔案的網址給影片,而是將 srcObject
設為代表網路攝影機的 LocalMediaStream
物件。
我也要告訴 <video>
autoplay
,否則它會停留在第一個影格。新增 controls
的運作方式也符合預期。
設定媒體限制 (解析度、高度、寬度)
getUserMedia()
的第一個參數也可以用來指定傳回媒體串流的更多需求 (或限制)。舉例來說,除了指出您需要影片的基本存取權 (例如 {video: true}
),您還可以要求串流為 HD:
const hdConstraints = {
video: { width: { exact: 1280} , height: { exact: 720 } },
};
const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
const vgaConstraints = {
video: { width: { exact: 640} , height: { exact: 360 } },
};
const stream = await navigator.mediaDevices.getUserMedia(hdConstraints);
如需更多設定,請參閱限制 API。
選取媒體來源
MediaDevices
介面的 enumerateDevices()
方法會要求提供可用媒體輸入和輸出裝置的清單,例如麥克風、攝影機、耳機等。傳回的 Promise 會以描述裝置的 MediaDeviceInfo
物件陣列解析。
在這個範例中,系統會選取最後找到的麥克風和攝影機做為媒體串流來源:
if (!navigator.mediaDevices?.enumerateDevices) {
console.log("enumerateDevices() not supported.");
} else {
// List cameras and microphones.
navigator.mediaDevices
.enumerateDevices()
.then((devices) => {
let audioSource = null;
let videoSource = null;
devices.forEach((device) => {
if (device.kind === "audioinput") {
audioSource = device.deviceId;
} else if (device.kind === "videoinput") {
videoSource = device.deviceId;
}
});
sourceSelected(audioSource, videoSource);
})
.catch((err) => {
console.error(`${err.name}: ${err.message}`);
});
}
async function sourceSelected(audioSource, videoSource) {
const constraints = {
audio: { deviceId: audioSource },
video: { deviceId: videoSource },
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
}
請參閱 Sam Dutton 的精彩示範,瞭解如何讓使用者選取媒體來源。
安全性
呼叫 navigator.mediaDevices.getUserMedia()
時,瀏覽器會顯示權限對話方塊,讓使用者選擇授予或拒絕相機/麥克風存取權。舉例來說,以下是 Chrome 的權限對話方塊:

提供備用項
如果使用者不支援 navigator.mediaDevices.getUserMedia()
,其中一個選項是,如果 API 不受支援和/或呼叫因故失敗,則回退至現有影片檔案:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}