簡介
音訊/視訊擷取一直是網頁開發的「Holy Grail」。多年來,我們一直仰賴瀏覽器外掛程式 (Flash 或 Silverlight) 來完成工作。別擔心!
支援 HTML5。儘管不是顯而易見,但 HTML5 的興起也讓我們開始接觸裝置硬體。Geolocation (GPS)、Orientation API (加速計)、WebGL (GPU) 和 Web Audio API (音訊硬體) 都是完美的範例。這些功能非常強大,可公開位於系統基礎硬體功能之上層的高階 JavaScript API。
本教學課程導入了新的 GetUserMedia API,可讓網頁應用程式存取使用者的相機和麥克風。
getUserMedia() 之路
如果您不知道自己的記錄,我們抵達 getUserMedia()
API 的方式是個有趣的故事。
過去幾年來,我們陸續推出數個「Media Capture API」的變化版本。 許多人都意識到,他們需要存取網路上的原生裝置,但這也導致每個人與其母親共同製定了新的規格。W3C 最終決定組成一個工作團隊,他們的唯一目的?瞭解瘋狂的感受!Device API Policy (DAP) 工作小組任務是整合並標準化提案豐富的提案。
我會總結 2011 年發生的事...
第 1 輪:HTML 媒體擷取
HTML Media Capture 是 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 瀏覽器 - 初次實作之一。請觀看這部影片,瞭解實際運作情形。
- Google Chrome Android 版 (0.16 版)
- Firefox 行動版 10.0
- iOS6 Safari 和 Chrome (部分支援)
第 2 輪:裝置元素
許多人認為 HTML 媒體擷取的限制太多,因此出現了支援任何類型 (日後) 裝置的新規格。也因為如此,呼叫新元素的設計 <device>
元素,成為 getUserMedia()
的前身。
Opera 是第一個根據 <device>
元素建立影片擷取初始實作的瀏覽器。不久之後 (也就是同一天表示精確),WhatWG 決定抓取 <device>
標記,改用另一個更新標記,這次由 JavaScript API 呼叫 navigator.getUserMedia()
。一週後,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>
元素最後是 Dodo 的。
憑藉更大規模的 WebRTC (網路即時通訊) 努力,加速找到合適的擷取 API 加速。該規格由 W3C WebRTC 工作小組所規範。Google、Opera、Mozilla 及其他一些工具已導入相關實作程序。
getUserMedia()
與 WebRTC 有關,因為這是進入該 API 組合的閘道。而是提供存取使用者本機相機/麥克風串流的方法。
支援:
自 Chrome 21、Opera 18 和 Firefox 17 起已支援 getUserMedia()
。
開始使用
透過 navigator.mediaDevices.getUserMedia()
,我們終於可以輕鬆使用網路攝影機和麥克風輸入功能,完全不需外掛程式。
相機存取權現在即可撥打電話,不必安裝。這個 Cookie 會直接內建在瀏覽器中。心動了嗎?
功能偵測
功能偵測是一種簡單的檢查,用來檢查 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);
如需其他設定,請參閱 constraints 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;
}