為網站進行開發作業時,WebRTC 標準提供 API,用於存取連接至電腦或智慧型手機的相機和麥克風。這些裝置通常稱為媒體裝置,可透過實作 MediaDevices
介面的 navigator.mediaDevices
物件,透過 JavaScript 存取。我們可以透過這個物件列舉所有已連結的裝置、監聽裝置變更 (當裝置連線或中斷連線),以及開啟裝置來擷取媒體串流 (請參閱下方說明)。
最常見的用途是透過 getUserMedia()
函式,此方法會傳回承諾,且該意圖為相符媒體裝置的 MediaStream
。這個函式會採用單一 MediaStreamConstraints
物件來說明我們的需求條件。舉例來說,如要直接開啟預設麥克風和攝影機,我們會執行下列步驟。
使用 Promise
const constraints = {
'video': true,
'audio': true
}
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log('Got MediaStream:', stream);
})
.catch(error => {
console.error('Error accessing media devices.', error);
});
使用 async/await
const openMediaDevices = async (constraints) => {
return await navigator.mediaDevices.getUserMedia(constraints);
}
try {
const stream = openMediaDevices({'video':true,'audio':true});
console.log('Got MediaStream:', stream);
} catch(error) {
console.error('Error accessing media devices.', error);
}
呼叫 getUserMedia()
會觸發權限要求。如果使用者接受權限,就會透過包含視訊和一個音軌的 MediaStream
解決該要求。如果權限遭拒,系統會擲回 PermissionDeniedError
。如果未連上任何相符的裝置,系統會擲回 NotFoundError
。
如需 MediaDevices
介面的完整 API 參考資料,請參閱 MDN 網頁文件。
查詢媒體裝置
在較複雜的應用程式中,我們最有可能檢查所有已連接的攝影機和麥克風,並向使用者提供適當的回饋。方法是呼叫 enumerateDevices()
函式。這會傳回一項承諾,並解析為 MediaDevicesInfo
陣列,用於描述每部已知媒體裝置。我們可以利用這項功能向使用者顯示 UI,讓使用者挑選自己偏好的介面。每個 MediaDevicesInfo
都包含名為 kind
的屬性,值為 audioinput
、audiooutput
或 videoinput
,表示其屬於哪種媒體裝置類型。
使用 Promise
function getConnectedDevices(type, callback) {
navigator.mediaDevices.enumerateDevices()
.then(devices => {
const filtered = devices.filter(device => device.kind === type);
callback(filtered);
});
}
getConnectedDevices('videoinput', cameras => console.log('Cameras found', cameras));
使用 async/await
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
const videoCameras = getConnectedDevices('videoinput');
console.log('Cameras found:', videoCameras);
監聽裝置變更
大多數電腦都支援在執行階段中插入各種裝置。例如透過 USB 連接的網路攝影機、藍牙耳機,或一組外接喇叭。為了正確支援這項功能,網頁應用程式應監聽媒體裝置的變更。只要在 navigator.mediaDevices
為 devicechange
事件新增事件監聽器即可。
// Updates the select element with the provided set of cameras
function updateCameraList(cameras) {
const listElement = document.querySelector('select#availableCameras');
listElement.innerHTML = '';
cameras.map(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
}).forEach(cameraOption => listElement.add(cameraOption));
}
// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Get the initial set of cameras connected
const videoCameras = getConnectedDevices('videoinput');
updateCameraList(videoCameras);
// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', event => {
const newCameraList = getConnectedDevices('video');
updateCameraList(newCameraList);
});
媒體限制
限制條件物件必須實作 MediaStreamConstraints
介面,並以參數形式傳遞至 getUserMedia()
可讓我們開啟符合特定需求的媒體裝置。這項規定可以非常明確定義 (音訊和/或影片),或是非常具體 (最低相機解析度或確切的裝置 ID)。建議使用 getUserMedia()
API 的應用程式先檢查現有裝置,然後使用 deviceId
限制,指定與特定裝置相符的限制。系統也會根據限制條件設定裝置。我們可以啟用麥克風的回音消除功能,也可以為攝影機設定特定的影片寬度或最小寬度。
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Open camera with at least minWidth and minHeight capabilities
async function openCamera(cameraId, minWidth, minHeight) {
const constraints = {
'audio': {'echoCancellation': true},
'video': {
'deviceId': cameraId,
'width': {'min': minWidth},
'height': {'min': minHeight}
}
}
return await navigator.mediaDevices.getUserMedia(constraints);
}
const cameras = getConnectedDevices('videoinput');
if (cameras && cameras.length > 0) {
// Open first available video camera with a resolution of 1280x720 pixels
const stream = openCamera(cameras[0].deviceId, 1280, 720);
}
如需 MediaStreamConstraints
介面的完整說明文件,請參閱 MDN 網頁版文件。
本機播放
當媒體裝置開啟且具有 MediaStream
可用時,我們可以將其指派給影片或音訊元素,以在本機播放串流。
async function playVideoFromCamera() {
try {
const constraints = {'video': true, 'audio': true};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const videoElement = document.querySelector('video#localVideo');
videoElement.srcObject = stream;
} catch(error) {
console.error('Error opening video camera.', error);
}
}
與 getUserMedia()
搭配使用的一般影片元素所需的 HTML 通常包含 autoplay
和 playsinline
屬性。autoplay
屬性會導致指派給元素的新串流自動播放。playsinline
屬性可讓影片在特定行動瀏覽器中以內嵌方式播放,而不只是以全螢幕播放。此外,除非使用者應能夠暫停直播,否則建議您在直播活動中使用 controls="false"
。
<html>
<head><title>Local video playback</title></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>