미디어 기기 시작하기

웹용으로 개발할 때 WebRTC 표준은 컴퓨터나 스마트폰에 연결된 카메라와 마이크에 액세스하기 위한 API를 제공합니다. 이러한 기기는 일반적으로 미디어 기기라고 하며 MediaDevices 인터페이스를 구현하는 navigator.mediaDevices 객체를 통해 자바스크립트로 액세스할 수 있습니다. 이 객체에서 연결된 모든 기기를 열거하고 기기 변경사항을 수신 대기하고 (기기가 연결되거나 연결 해제된 경우), 기기를 열어 미디어 스트림을 검색할 수 있습니다 (아래 참고).

가장 일반적인 방법은 일치하는 미디어 기기의 MediaStream로 확인될 프로미스를 반환하는 getUserMedia() 함수를 사용하는 것입니다. 이 함수는 요구사항을 지정하는 단일 MediaStreamConstraints 객체를 사용합니다. 예를 들어 기본 마이크와 카메라를 열기만 하려면 다음을 실행합니다.

프라미스 사용

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() 호출은 권한 요청을 트리거합니다. 사용자가 권한을 수락하면 프로미스는 동영상 1개와 오디오 트랙 1개가 포함된 MediaStream로 확인됩니다. 권한이 거부되면 PermissionDeniedError이 발생합니다. 일치하는 기기가 연결되어 있지 않으면 NotFoundError이 발생합니다.

MediaDevices 인터페이스의 전체 API 참조는 MDN 웹 문서에서 확인할 수 있습니다.

미디어 기기 쿼리

더 복잡한 애플리케이션에서는 연결된 모든 카메라와 마이크를 확인하고 사용자에게 적절한 피드백을 제공하는 것이 좋습니다. enumerateDevices() 함수를 호출하면 됩니다. 그러면 알려진 각 미디어 기기를 설명하는 MediaDevicesInfo 배열로 확인되는 프로미스가 반환됩니다. 이를 사용하여 사용자에게 UI를 표시할 수 있고 사용자가 선호하는 UI를 선택할 수 있습니다. 각 MediaDevicesInfo에는 미디어 기기 유형을 나타내는 audioinput, audiooutput, videoinput 값이 포함된 kind라는 속성이 포함되어 있습니다.

프라미스 사용

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, 블루투스 헤드셋 또는 외부 스피커 세트로 연결된 웹캠일 수 있습니다. 이를 올바르게 지원하려면 웹 애플리케이션이 미디어 기기의 변경사항을 수신 대기해야 합니다. devicechange 이벤트의 navigator.mediaDevices에 리스너를 추가하면 됩니다.

// 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>