Guida introduttiva ai dispositivi multimediali

Nello sviluppo per il web, lo standard WebRTC fornisce API per l'accesso videocamere e microfoni collegati al computer o allo smartphone. Questi dispositivi sono comunemente noti come dispositivi multimediali e sono accessibili con JavaScript tramite l'oggetto navigator.mediaDevices, che implementa l'elemento MediaDevices a riga di comando. Da questo oggetto possiamo enumerare tutti i dispositivi connessi, ascoltare modifiche al dispositivo (quando un dispositivo è connesso o disconnesso) e apri un dispositivo per recuperare uno stream multimediale (vedi di seguito).

Il modo più comune utilizzato per questo scopo è tramite la funzione getUserMedia(), che restituisce una promessa che si risolverà in un MediaStream per i contenuti multimediali corrispondenti dispositivi mobili. Questa funzione prende un singolo oggetto MediaStreamConstraints che specifica i nostri requisiti. Ad esempio, per aprire microfono e fotocamera predefiniti, procederemo nel seguente modo.

Utilizzo delle promesse

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

Utilizzo asincrono/Attendi

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

La chiamata a getUserMedia() attiverà una richiesta di autorizzazione. Se l'utente accetta l'autorizzazione, la promessa viene risolta con un MediaStream contenente un video e una traccia audio. Se l'autorizzazione viene negata, PermissionDeniedError viene lanciata. Nel caso in cui non ci siano dispositivi corrispondenti connesso, verrà restituito un NotFoundError.

Il riferimento completo dell'API per l'interfaccia MediaDevices è disponibile all'indirizzo MDN web documenti.

Esecuzione di query sui dispositivi multimediali

In una richiesta più complessa, probabilmente vorrai controllare tutte le videocamere e microfoni collegati e fornire il feedback appropriato alle utente. A questo scopo, puoi richiamare la funzione enumerateDevices(). In questo modo restituiscono una promessa che si risolve in un array di MediaDevicesInfo che descrive ogni dispositivo multimediale noto. Possiamo usarlo per presentare all'utente una UI che, scegli quello che preferiscono. Ogni MediaDevicesInfo contiene una proprietà denominata kind con il valore audioinput, audiooutput o videoinput, che indica di che tipo di dispositivo multimediale si tratta.

Utilizzo delle promesse

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

Utilizzo asincrono/Attendi

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

Ascolto dei cambiamenti dei dispositivi

La maggior parte dei computer supporta il collegamento di diversi dispositivi in fase di runtime. Potrebbe essere un webcam connessa tramite USB, cuffie Bluetooth o set di altoparlanti esterni. Nella per supportarlo correttamente, un'applicazione web deve rilevare le modifiche dei dispositivi multimediali. A questo scopo, puoi aggiungere un listener navigator.mediaDevices per l'evento 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);
});

Vincoli dei contenuti multimediali

L'oggetto constraints, che deve implementare l'elemento MediaStreamConstraints dell'interfaccia utente, che passiamo come parametro a getUserMedia(), ci consente di aprire dispositivo multimediale che soddisfa un determinato requisito. Questo requisito può essere molto definiti in modo generico (audio e/o video) o molto specifici (numero minimo di videocamere risoluzione massima o un ID dispositivo esatto). È consigliabile che le applicazioni che utilizzano l'API getUserMedia() controlla prima i dispositivi esistenti, quindi specifica vincolo che corrisponde esattamente al dispositivo utilizzando il vincolo deviceId. I dispositivi verranno anche, se possibile, configurati in base ai vincoli. Me puoi attivare la cancellazione dell'eco sui microfoni o impostare una larghezza minima o specifica e l'altezza del video dalla videocamera.

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

È disponibile la documentazione completa per l'interfaccia MediaStreamConstraints sul web MDN documenti.

Riproduzione locale

Una volta aperto un dispositivo multimediale e disponibile un MediaStream, può assegnarlo a un elemento video o audio per riprodurlo localmente.

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

Il codice HTML necessario per un tipico elemento video utilizzato con getUserMedia() di solito hanno gli attributi autoplay e playsinline. autoplay riprodurrà automaticamente i nuovi stream assegnati all'elemento. L'attributo playsinline consente di riprodurre il video in linea, anziché solo per intero. schermo su alcuni browser mobile. È inoltre consigliabile utilizzare controls="false" per i live streaming, a meno che l'utente non possa mettere in pausa che li rappresentano.

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