소개
오디오/동영상 캡처는 오랫동안 웹 개발의 '성배'였습니다. 수년 동안 이 작업을 수행하기 위해 브라우저 플러그인 (Flash 또는 Silverlight)을 사용해야 했습니다. 어서요!
HTML5가 이 문제를 해결해 줍니다. 명확하지 않을 수 있지만 HTML5의 등장으로 기기 하드웨어에 대한 액세스가 급증했습니다. 위치정보 (GPS), Orientation API (가속도계), WebGL (GPU), Web Audio API (오디오 하드웨어)가 대표적인 예입니다. 이러한 기능은 시스템의 기본 하드웨어 기능 위에 있는 상위 수준의 JavaScript API를 노출하는 매우 강력한 기능입니다.
이 튜토리얼에서는 웹 앱이 사용자의 카메라와 마이크에 액세스할 수 있도록 하는 새로운 API인 GetUserMedia를 소개합니다.
getUserMedia()로 가는 길
getUserMedia() API가 탄생한 배경은 꽤 흥미롭습니다.
지난 몇 년 동안 'Media Capture API'의 여러 변형이 발전해 왔습니다. 많은 사람이 웹에서 네이티브 기기에 액세스할 수 있어야 한다는 필요성을 인식했지만, 이로 인해 모든 사람이 새로운 사양을 만들게 되었습니다. 상황이 너무 복잡해져서 W3C는 마침내 실무 그룹을 구성하기로 결정했습니다. 유일한 목적은 무엇일까요? 혼란을 정리하세요. 기기 API 정책 (DAP) 실무 그룹은 수많은 제안을 통합하고 표준화하는 임무를 맡았습니다.
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용 Chrome (0.16)
- Firefox Mobile 10.0
- iOS6 Safari 및 Chrome (부분 지원)
2라운드: 기기 요소
많은 사람들이 HTML 미디어 캡처가 너무 제한적이라고 생각했기 때문에 모든 유형의 (미래) 기기를 지원하는 새로운 사양이 등장했습니다. 당연히 이 설계에는 새로운 요소인 <device> 요소가 필요했고, 이 요소는 getUserMedia()의 전신이 되었습니다.
Opera는 <device> 요소를 기반으로 동영상 캡처의 초기 구현을 만든 최초의 브라우저 중 하나였습니다. 얼마 지나지 않아(정확히는 같은 날) WhatWG는 <device> 태그를 폐기하고 또 다른 신흥 기술인 navigator.getUserMedia()라는 JavaScript API를 사용하기로 결정했습니다. 일주일 후 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> 요소는 결국 도도새처럼 사라졌습니다.
더 큰 WebRTC (Web Real Time Communications) 노력 덕분에 적합한 캡처 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> 요소를 포함하지 않습니다. 동영상에 미디어 파일의 URL을 제공하는 대신 웹캠을 나타내는 LocalMediaStream 객체에 srcObject를 설정합니다.
또한 <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 객체의 배열로 확인됩니다.
이 예시에서는 마지막으로 발견된 마이크와 카메라가 미디어 스트림 소스로 선택됩니다.
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;
}