簡介
音訊/視訊擷取功能一直是網站開發人員長久以來的「聖杯」。多年以來,我們都必須依賴瀏覽器外掛程式 (Flash 或 Silverlight) 才能完成工作。Come on!
HTML5 可解決這個問題。雖然不明顯,但 HTML5 的興起也帶來了裝置硬體存取權的激增。地理定位 (GPS)、Orientation API (加速計)、WebGL (GPU) 和 Web Audio API (音訊硬體) 都是很好的例子。這些功能非常強大,可公開高層級 JavaScript API,這些 API 位於系統底層硬體功能之上。
本教學課程將介紹新的 API GetUserMedia,可讓網路應用程式存取使用者的攝影機和麥克風。
使用 getUserMedia() 的路徑
如果您不瞭解其歷史,我們開發 getUserMedia()
API 的過程相當有趣。
過去幾年,我們推出了多個「Media Capture API」變化版本。許多人認為需要在網路上存取原生裝置,但這導致每個人都會編寫新的規格。情況變得如此混亂,以致 W3C 最後決定成立工作小組。單一用途?讓瘋狂變得有意義!Device APIs Policy (DAP) Working Group 的任務是整合並標準化眾多提案。
我來總結一下 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 行動版 10.0
- iOS6 Safari 和 Chrome (部分支援)
第 2 回合:device 元素
許多人認為 HTML Media Capture 的限制太多,因此我們推出了新規格,支援任何類型的 (未來) 裝置。毫無意外,設計會呼叫新的 <device>
元素,這也是 getUserMedia()
的前身。
Opera 是第一個以 <device>
元素初始導入影片擷取功能的瀏覽器。不久之後 (具體來說是同一天),WhatWG 決定捨棄 <device>
標記,改用另一個新興的標記,這次是名為 navigator.getUserMedia()
的 JavaScript API。一週後,Opera 推出了新版本,其中包含支援更新版 getUserMedia()
規格的功能。同年稍晚,微軟也加入了這個行列,發布了支援新規格的 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 (網頁即時通訊) 的規模更大,因此我們更快找到合適的擷取 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>
搭配使用。請注意,我們並未在 <video>
元素上設定 src
屬性或加入 <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);
如需其他設定,請參閱 constraints API。
選取媒體來源
MediaDevices
介面的 enumerateDevices()
方法會要求可用媒體輸入和輸出裝置的清單,例如麥克風、相機、耳機等。系統會使用描述裝置的 MediaDeviceInfo
物件陣列解析傳回的 Promise。
在這個範例中,系統會選取最近找到的麥克風和相機做為媒體串流來源:
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;
}