Bắt đầu với thiết bị truyền thông

Khi phát triển cho web, tiêu chuẩn WebRTC sẽ cung cấp các API để truy cập vào máy ảnh và micrô kết nối với máy tính hoặc điện thoại thông minh. Các thiết bị này thường được gọi là Thiết bị phương tiện và có thể truy cập bằng JavaScript thông qua đối tượng navigator.mediaDevices. Đối tượng này sẽ triển khai giao diện MediaDevices. Từ đối tượng này, chúng ta có thể liệt kê tất cả các thiết bị đã kết nối, theo dõi các thay đổi về thiết bị (khi thiết bị được kết nối hoặc ngắt kết nối) và mở một thiết bị để truy xuất Media Stream (xem bên dưới).

Cách phổ biến nhất để sử dụng tính năng này là thông qua hàm getUserMedia(). Hàm này trả về một lời hứa sẽ phân giải thành MediaStream cho các thiết bị truyền thông phù hợp. Hàm này lấy một đối tượng MediaStreamConstraints duy nhất chỉ định các yêu cầu mà chúng ta có. Ví dụ: để chỉ cần mở micrô và máy ảnh mặc định, chúng ta sẽ làm như sau.

Sử dụng lời hứa

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);
    });

Sử dụng chế độ không đồng bộ/chờ

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);
}

Lệnh gọi đến getUserMedia() sẽ kích hoạt một yêu cầu cấp quyền. Nếu người dùng chấp nhận quyền, lời hứa sẽ được phân giải bằng một MediaStream chứa một video và một bản âm thanh. Nếu quyền bị từ chối, hệ thống sẽ gửi PermissionDeniedError. Trong trường hợp không có thiết bị nào phù hợp được kết nối, NotFoundError sẽ được gửi.

Bạn có thể xem tài liệu tham khảo API đầy đủ cho giao diện MediaDevices tại tài liệu web MDN.

Truy vấn thiết bị truyền thông

Trong một ứng dụng phức tạp hơn, rất có thể chúng ta muốn kiểm tra mọi máy ảnh và micrô đã kết nối rồi cung cấp ý kiến phản hồi thích hợp cho người dùng. Bạn có thể thực hiện việc này bằng cách gọi hàm enumerateDevices(). Thao tác này sẽ trả về lời hứa phân giải một mảng MediaDevicesInfo mô tả từng thiết bị truyền thông đã biết. Chúng ta có thể sử dụng tính năng này để hiển thị giao diện người dùng cho người dùng và cho phép họ chọn giao diện họ muốn. Mỗi MediaDevicesInfo chứa một thuộc tính có tên là kind với giá trị audioinput, audiooutput hoặc videoinput, cho biết đó là loại thiết bị truyền thông nào.

Sử dụng lời hứa

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));

Sử dụng chế độ không đồng bộ/chờ

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);

Nghe các thay đổi về thiết bị

Hầu hết máy tính đều hỗ trợ cắm nhiều thiết bị trong thời gian chạy. Đó có thể là webcam kết nối bằng USB, tai nghe Bluetooth hoặc một nhóm loa ngoài. Để hỗ trợ đúng cách, ứng dụng web nên theo dõi các thay đổi của các thiết bị đa phương tiện. Bạn có thể thực hiện việc này bằng cách thêm trình nghe vào navigator.mediaDevices cho sự kiện 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);
});

Các quy tắc ràng buộc đối với nội dung nghe nhìn

Đối tượng ràng buộc phải triển khai giao diện MediaStreamConstraints mà chúng ta truyền dưới dạng tham số đến getUserMedia(), cho phép chúng ta mở một thiết bị phương tiện phù hợp với một yêu cầu nhất định. Yêu cầu này có thể được xác định rất lỏng lẻo (âm thanh và/hoặc video) hoặc rất cụ thể (độ phân giải máy ảnh tối thiểu hoặc mã thiết bị chính xác). Các ứng dụng sử dụng API getUserMedia() trước tiên nên kiểm tra các thiết bị hiện có, sau đó chỉ định một hạn chế khớp với thiết bị chính xác bằng cách sử dụng quy tắc ràng buộc deviceId. Nếu có thể, thiết bị cũng sẽ được định cấu hình theo các quy tắc ràng buộc. Chúng tôi có thể bật tính năng loại bỏ tiếng vọng trên micrô hoặc đặt chiều rộng và chiều cao cụ thể hoặc tối thiểu của video từ máy ảnh.

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);
}

Bạn có thể xem toàn bộ tài liệu về giao diện MediaStreamConstraints trong tài liệu web MDN.

Phát cục bộ

Sau khi một thiết bị đa phương tiện đã được mở và có MediaStream, chúng ta có thể chỉ định thiết bị đó cho phần tử video hoặc âm thanh để phát luồng cục bộ.

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);
    }
}

HTML cần thiết cho một phần tử video thông thường được dùng với getUserMedia() thường sẽ có các thuộc tính autoplayplaysinline. Thuộc tính autoplay sẽ khiến các luồng mới được gán cho phần tử này tự động phát. Thuộc tính playsinline cho phép video phát tại chỗ, thay vì chỉ phát ở chế độ toàn màn hình, trên một số trình duyệt cho thiết bị di động. Bạn cũng nên sử dụng controls="false" cho các sự kiện phát trực tiếp, trừ khi người dùng có thể tạm dừng chúng.

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