תחילת העבודה עם מכשירי מדיה

במהלך הפיתוח לאינטרנט, תקן WebRTC מספק ממשקי API שמאפשרים גישה למצלמות ולמיקרופונים שמחוברים למחשב או לסמארטפון. המכשירים האלה נקראים בדרך כלל מכשירי מדיה, וניתן לגשת אליהם באמצעות JavaScript באמצעות האובייקט navigator.mediaDevices, שמטמיע את הממשק MediaDevices. מהאובייקט הזה אנחנו יכולים לספור את כל המכשירים המחוברים, להאזין לשינויים במכשיר (כשמכשיר מחובר או מנותק) ולפתוח מכשיר כדי לאחזר מדיה שלו (ראו בהמשך).

הדרך הנפוצה ביותר לעשות זאת היא באמצעות הפונקציה getUserMedia(), שמחזירה הבטחה שתסתיים ל-MediaStream עבור מכשירי המדיה התואמים. הפונקציה הזו לוקחת אובייקט 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);
    });

שימוש באסינכרוני/המתנה

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() תפעיל בקשת הרשאה. אם המשתמש יאשר את ההרשאה, ההבטחה תטופל עם MediaStream שמכיל סרטון אחד וטראק אחד של אודיו. אם ההרשאה תידחה, יועתק PermissionDeniedError. אם לא מחוברים מכשירים תואמים, תתבצע התרעה של NotFoundError.

הפניית ה-API המלאה לממשק MediaDevices זמינה במסמכי האינטרנט של MDN.

שליחת שאילתה לגבי מכשירי מדיה

ביישום מורכב יותר, סביר להניח שנרצה לבדוק את כל המצלמות והמיקרופונים המחוברים ולספק למשתמש את המשוב המתאים. כדי לעשות זאת, אפשר לקרוא לפונקציה enumerateDevices(). הפעולה הזו תחזיר הבטחה למערך MediaDevicesInfo שמתאר כל מכשיר מדיה ידוע. נוכל להשתמש בו כדי להציג למשתמש ממשק משתמש שבו הוא יבחר את ממשק המשתמש המועדף עליו. כל MediaDevicesInfo מכיל מאפיין בשם kind עם הערך audioinput, audiooutput או videoinput שמציין מהו סוג מכשיר המדיה.

שימוש בהבטחות

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 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, אוזניות Bluetooth או קבוצה של רמקולים חיצוניים. כדי לתמוך בשידור באופן תקין, אפליקציית אינטרנט צריכה לזהות את השינויים במכשירי מדיה. כדי לעשות זאת, אפשר להוסיף פונקציות listener ל-navigator.mediaDevices עבור האירוע 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);
});

מגבלות מדיה

אובייקט האילוצים, שחייב להטמיע את הממשק MediaStreamConstraints, שאנחנו מעבירים כפרמטר ב-getUserMedia() מאפשר לנו לפתוח מכשיר מדיה שעומד בדרישה מסוימת. אפשר להגדיר את הדרישה הזו בצורה מאוד רפויה (אודיו ו/או וידאו) או ספציפית מאוד (רזולוציה מינימלית של המצלמה או מזהה מכשיר מדויק). מומלץ שאפליקציות שמשתמשות ב-API getUserMedia() יבדקו קודם את המכשירים הקיימים, ולאחר מכן יציינו מגבלות שתואמות למכשיר הספציפי באמצעות האילוץ 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);
    }
}

קוד ה-HTML הנדרש לרכיב וידאו אופייני שנמצא בשימוש עם getUserMedia() יכלול בדרך כלל את המאפיינים autoplay ו-playsinline. המאפיין autoplay יגרום להפעלה אוטומטית של שידורים חדשים שהוקצו לרכיב. המאפיין playsinline מאפשר להפעיל סרטונים בתוך השורה ולא במסך מלא בלבד בדפדפנים מסוימים לנייד. מומלץ להשתמש גם ב-controls="false" לשידורים חיים, אלא אם המשתמשים צריכים להיות מסוגלים להשהות אותם.

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