מבוא
לכידת אודיו/וידאו הייתה ה'גביע הקדוש' של פיתוח אתרים במשך זמן רב. במשך שנים נאלצנו להסתמך על תוספים לדפדפן (Flash או Silverlight) כדי לבצע את העבודה. נו כבר!
HTML5 בא לעזרה. יכול להיות שזה לא ברור, אבל העלייה של HTML5 הביאה לעלייה חדה בגישה לחומרה של המכשיר. דוגמאות מצוינות לכך הן מיקום גיאוגרפי (GPS), Orientation API (מד תאוצה), WebGL (GPU) ו-Web Audio API (חומרת אודיו). התכונות האלה חזקות מאוד, והן חושפות ממשקי API של JavaScript ברמה גבוהה שפועלים על גבי יכולות החומרה הבסיסיות של המערכת.
במדריך הזה מוצג API חדש, GetUserMedia, שמאפשר לאפליקציות אינטרנט לגשת למצלמה ולמיקרופון של המשתמש.
הדרך אל getUserMedia()
אם אתם לא מכירים את ההיסטוריה שלו, כדאי לדעת איך הגענו ל-getUserMedia()
API.
בשנים האחרונות התפתחו כמה וריאציות של Media Capture APIs. הרבה אנשים הבינו שיש צורך בגישה למכשירים מקומיים באינטרנט, אבל זה הוביל לכך שכל אחד ואמו יצרו מפרט חדש. המצב נהיה כל כך מסובך שבסופו של דבר W3C החליטו להקים קבוצת עבודה. מה המטרה היחידה שלהם? להבין את הטירוף! קבוצת העבודה בנושא מדיניות בנושא ממשקי API למכשירים (DAP) קיבלה את המשימה לאחד את ההצעות הרבות ולגבש מהן תקן.
אנסה לסכם את מה שקרה בשנת 2011…
סבב 1: HTML Media Capture
HTML Media Capture היה הניסיון הראשון של ה-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 – אחת ההטמעות הראשונות. בסרטון הזה אפשר לראות איך זה עובד.
- Chrome ל-Android (0.16)
- Firefox Mobile 10.0
- Safari ו-Chrome ל-iOS6 (תמיכה חלקית)
סבב 2: רכיב המכשיר
רבים חשבו שהמפרט HTML Media Capture מגביל מדי, ולכן נוצר מפרט חדש שתומך בכל סוג של מכשיר (עתידי). לא מפתיע שהעיצוב דרש רכיב חדש, <device>
, שהפך לקודם של getUserMedia()
.
דפדפן Opera היה בין הדפדפנים הראשונים שיצרו הטמעות ראשוניות של צילום וידאו שמבוסס על רכיב <device>
. זמן קצר לאחר מכן (באותו היום, ליתר דיוק), קבוצת WhatWG החליטה לבטל את התג <device>
לטובת תג חדש, הפעם API של JavaScript בשם navigator.getUserMedia()
. שבוע לאחר מכן, Opera פרסמה גרסאות חדשות שכללו תמיכה במפרט המעודכן של getUserMedia()
. בהמשך אותה שנה, מיקרוסופט הצטרפה לחגיגה ופרסמה מעבדה ל-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>
נעלם בסופו של דבר.
הקצב למציאת API מתאים לצילום מסך הואץ בזכות המאמץ הגדול יותר של WebRTC (תקשורת בזמן אמת באינטרנט). המפרט הזה נמצא בפיקוח של קבוצת העבודה של W3C WebRTC. ל-Google, ל-Opera, ל-Mozilla ול-כמה חברות נוספות יש יישומים.
getUserMedia()
קשור ל-WebRTC כי הוא השער לסט הזה של ממשקי API.
ההרשאה מאפשרת גישה לשידור המקומי של המצלמה או המיקרופון של המשתמש.
תמיכה:
getUserMedia()
נתמך מגרסה 21 של Chrome, מגרסה 18 של Opera ומגרסה 17 של Firefox.
תחילת העבודה
עם 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>
אישור. אז מה קורה כאן? הדוגמה המושלמת לשימוש משותף בממשקי API חדשים של HTML5 היא לכידת מדיה. הוא פועל בשילוב עם חברים אחרים בפורמט HTML5, <audio>
ו-<video>
.
שימו לב שלא הגדרנו מאפיין src
או הוספנו רכיבי <source>
לרכיב <video>
. במקום להזין ל-video כתובת URL לקובץ מדיה, אנחנו מגדירים את srcObject
לאובייקט LocalMediaStream
שמייצג את מצלמת האינטרנט.
אני גם אומר ל-<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.
בחירת מקור מדיה
השיטה enumerateDevices()
של הממשק MediaDevices
מבקשת רשימה של מכשירי קלט ופלט מדיה זמינים, כמו מיקרופונים, מצלמות, אוזניות וכו'. ה-Promise שמוחזר נפתר עם מערך של אובייקטים מסוג 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);
}
מומלץ לצפות בהדגמה המצוינת של סם דאטון כדי לראות איך מאפשרים למשתמשים לבחור את מקור המדיה.
אבטחה
כשמתקשרים באמצעות 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;
}