開始使用媒體裝置

為網站進行開發作業時,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 的屬性,值為 audioinputaudiooutputvideoinput,表示其屬於哪種媒體裝置類型。

使用 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.mediaDevicesdevicechange 事件新增事件監聽器即可。

// 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 通常包含 autoplayplaysinline 屬性。autoplay 屬性會導致指派給元素的新串流自動播放。playsinline 屬性可讓影片在特定行動瀏覽器中以內嵌方式播放,而不只是以全螢幕播放。此外,除非使用者應能夠暫停直播,否則建議您在直播活動中使用 controls="false"

<html>
<head><title>Local video playback</title></head>
<body>
    <video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>