Giới thiệu
Tính năng quay video/ghi âm đã là "Chén Thánh" của quá trình phát triển web trong một thời gian dài. Trong nhiều năm, chúng ta phải dựa vào các trình bổ trợ của trình duyệt (Flash hoặc Silverlight) để hoàn thành công việc. Nào!
HTML5 là giải pháp cứu cánh. Có thể bạn không nhận thấy, nhưng sự trỗi dậy của HTML5 đã mang lại khả năng truy cập mạnh mẽ vào phần cứng thiết bị. Vị trí địa lý (GPS), Orientation API (gia tốc kế), WebGL (GPU) và Web Audio API (phần cứng âm thanh) là những ví dụ điển hình. Các tính năng này vô cùng mạnh mẽ, hiển thị các API JavaScript cấp cao nằm trên các chức năng phần cứng cơ bản của hệ thống.
Hướng dẫn này giới thiệu một API mới, GetUserMedia, cho phép các ứng dụng web truy cập vào camera và micrô của người dùng.
Đường dẫn đến getUserMedia()
Nếu bạn chưa biết về lịch sử của API getUserMedia(), thì cách chúng tôi tạo ra API này là một câu chuyện thú vị.
Một số biến thể của "API Media Capture" đã phát triển trong vài năm qua. Nhiều người nhận ra nhu cầu truy cập vào các thiết bị gốc trên web, nhưng điều đó khiến mọi người và mẹ của họ phải cùng nhau đưa ra một thông số kỹ thuật mới. Mọi thứ trở nên lộn xộn đến mức W3C cuối cùng đã quyết định thành lập một nhóm làm việc. Mục đích duy nhất của họ là gì? Làm rõ những điều khó hiểu! Nhóm công tác về Chính sách API thiết bị (DAP) đã được giao nhiệm vụ hợp nhất và chuẩn hoá vô số đề xuất.
Tôi sẽ cố gắng tóm tắt những gì đã xảy ra vào năm 2011…
Vòng 1: Chụp ảnh/quay video bằng HTML
HTML Media Capture là nỗ lực đầu tiên của DAP trong việc chuẩn hoá tính năng ghi lại nội dung nghe nhìn trên web. Hàm này hoạt động bằng cách nạp chồng <input type="file"> và thêm các giá trị mới cho tham số accept.
Nếu muốn cho phép người dùng chụp ảnh nhanh bằng webcam, bạn có thể sử dụng capture=camera:
<input type="file" accept="image/*;capture=camera">
Cách quay video hoặc ghi âm cũng tương tự:
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">
Nghe cũng hay đúng không? Tôi đặc biệt thích việc nó sử dụng lại một tệp đầu vào. Về mặt ngữ nghĩa,
điều này rất hợp lý. Điểm yếu của "API" cụ thể này là khả năng tạo hiệu ứng theo thời gian thực (ví dụ: kết xuất dữ liệu webcam trực tiếp thành <canvas> và áp dụng bộ lọc WebGL).
HTML Media Capture chỉ cho phép bạn ghi lại tệp nội dung nghe nhìn hoặc chụp nhanh tại thời điểm đó.
Hỗ trợ:
- Trình duyệt Android 3.0 – một trong những cách triển khai đầu tiên. Hãy xem video này để biết cách hoạt động của tính năng này.
- Chrome dành cho Android (0,16)
- Firefox Mobile 10.0
- iOS6 Safari và Chrome (hỗ trợ một phần)
Vòng 2: Thành phần thiết bị
Nhiều người cho rằng HTML Media Capture quá hạn chế, vì vậy, một thông số kỹ thuật mới đã xuất hiện để hỗ trợ mọi loại thiết bị (trong tương lai). Không có gì ngạc nhiên khi thiết kế này yêu cầu
một phần tử mới, phần tử <device>,
trở thành tiền thân của getUserMedia().
Opera là một trong những trình duyệt đầu tiên tạo ra bản triển khai ban đầu của tính năng quay video dựa trên phần tử <device>. Ngay sau đó (chính xác là cùng ngày), WhatWG đã quyết định loại bỏ thẻ <device> để ủng hộ một thẻ mới nổi khác, lần này là một API JavaScript có tên là navigator.getUserMedia(). Một tuần sau, Opera đã phát hành các bản dựng mới có hỗ trợ thông số kỹ thuật getUserMedia() mới cập nhật. Cuối năm đó, Microsoft cũng tham gia bằng cách phát hành Lab cho IE9 hỗ trợ thông số kỹ thuật mới.
<device> sẽ có dạng như sau:
<device type="media" onchange="update(this.data)"></device>
<video autoplay></video>
<script>
function update(stream) {
document.querySelector('video').src = stream.url;
}
</script>
Hỗ trợ:
Rất tiếc, không có trình duyệt nào được phát hành có chứa <device>.
Tôi đoán là bớt được một API để lo lắng :) <device> có hai điểm tuyệt vời: 1.) nó mang tính ngữ nghĩa và 2.) nó dễ dàng mở rộng để hỗ trợ không chỉ các thiết bị âm thanh/video.
Hãy thư giãn nào. Mọi thứ diễn ra rất nhanh!
Vòng 3: WebRTC
Phần tử <device> cuối cùng đã bị loại bỏ.
Tốc độ tìm API chụp phù hợp đã tăng lên nhờ nỗ lực lớn hơn của WebRTC (Giao tiếp theo thời gian thực trên web). Thông số kỹ thuật đó do Nhóm công tác WebRTC của W3C giám sát. Google, Opera, Mozilla và một số trình duyệt khác đã triển khai tính năng này.
getUserMedia() có liên quan đến WebRTC vì đây là cổng vào tập hợp API đó.
API này cung cấp phương tiện để truy cập vào luồng camera/micrô cục bộ của người dùng.
Hỗ trợ:
getUserMedia() được hỗ trợ kể từ Chrome 21, Opera 18 và Firefox 17.
Bắt đầu
Với navigator.mediaDevices.getUserMedia(), cuối cùng chúng ta cũng có thể khai thác dữ liệu đầu vào của webcam và micrô mà không cần trình bổ trợ.
Giờ đây, bạn có thể truy cập vào camera chỉ bằng một lệnh gọi thay vì phải cài đặt. Tính năng này được tích hợp trực tiếp vào trình duyệt. Bạn đã thấy hào hứng chưa?
Phát hiện tính năng
Phát hiện tính năng là một bước kiểm tra đơn giản về sự tồn tại của navigator.mediaDevices.getUserMedia:
if (navigator.mediaDevices?.getUserMedia) {
// Good to go!
} else {
alert("navigator.mediaDevices.getUserMedia() is not supported");
}
Có quyền truy cập vào thiết bị đầu vào
Để sử dụng webcam hoặc micrô, chúng tôi cần yêu cầu cấp quyền.
Tham số đầu tiên cho navigator.mediaDevices.getUserMedia() là một đối tượng chỉ định thông tin chi tiết và yêu cầu cho từng loại nội dung nghe nhìn mà bạn muốn truy cập. Ví dụ: nếu bạn muốn truy cập vào webcam, thì tham số đầu tiên phải là {video: true}. Để sử dụng cả micrô và camera, hãy truyền {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>
Được rồi. Vậy chuyện gì đang xảy ra ở đây? Ghi lại nội dung nghe nhìn là một ví dụ hoàn hảo về các API HTML5 mới
hoạt động cùng nhau. Thẻ này hoạt động cùng với các thẻ HTML5 khác là <audio> và <video>.
Lưu ý rằng chúng ta không đặt thuộc tính src hoặc thêm các phần tử <source> vào phần tử <video>. Thay vì cung cấp cho video một URL đến tệp nội dung nghe nhìn, chúng ta sẽ đặt srcObject thành đối tượng LocalMediaStream đại diện cho webcam.
Tôi cũng yêu cầu <video> autoplay, nếu không thì video sẽ bị treo ở khung hình đầu tiên. Việc thêm controls cũng hoạt động như bạn mong đợi.
Đặt các quy tắc ràng buộc về nội dung nghe nhìn (độ phân giải, chiều cao, chiều rộng)
Bạn cũng có thể sử dụng tham số đầu tiên cho getUserMedia() để chỉ định thêm yêu cầu (hoặc quy tắc ràng buộc) đối với luồng nội dung nghe nhìn được trả về. Ví dụ: thay vì chỉ cho biết bạn muốn có quyền truy cập cơ bản vào video (ví dụ: {video: true}), bạn có thể yêu cầu thêm luồng phát ở chế độ 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);
Để biết thêm các cấu hình, hãy xem API ràng buộc.
Chọn nguồn nội dung nghe nhìn
Phương thức enumerateDevices() của giao diện MediaDevices yêu cầu danh sách các thiết bị đầu vào và thiết bị đầu ra của nội dung nghe nhìn hiện có, chẳng hạn như micrô, máy ảnh, tai nghe, v.v. Lời hứa được trả về sẽ được giải quyết bằng một mảng các đối tượng MediaDeviceInfo mô tả thiết bị.
Trong ví dụ này, micrô và máy ảnh cuối cùng được tìm thấy sẽ được chọn làm nguồn luồng nội dung nghe nhìn:
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);
}
Hãy xem bản minh hoạ tuyệt vời của Sam Dutton về cách cho phép người dùng chọn nguồn nội dung nghe nhìn.
Bảo mật
Trình duyệt sẽ hiển thị hộp thoại cấp quyền khi gọi navigator.mediaDevices.getUserMedia(), cho phép người dùng cấp hoặc từ chối quyền truy cập vào camera/micrô của họ. Ví dụ: đây là hộp thoại cấp quyền của Chrome:
Cung cấp phương án dự phòng
Đối với những người dùng không được hỗ trợ navigator.mediaDevices.getUserMedia(), bạn có thể quay lại một tệp video hiện có nếu API không được hỗ trợ và/hoặc lệnh gọi không thành công vì lý do nào đó:
if (!navigator.mediaDevices?.getUserMedia) {
video.src = "fallbackvideo.webm";
} else {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
}