הקלטת סרטון מהמשתמש

Mat Scales

בדפדפנים רבים יש עכשיו אפשרות לגשת לקלט וידאו ואודיו מהמשתמש. עם זאת, בהתאם לדפדפן, יכול להיות שמדובר בחוויה דינמית מלאה בתוך הדף, או שהיא יכולה להיות מועברת לאפליקציה אחרת במכשיר של המשתמש.

הדרך הקלה ביותר היא פשוט לבקש מהמשתמש קובץ שהוקלט מראש. כדי לעשות זאת, יוצרים רכיב פשוט של קלט קובץ ומוסיפים מסנן accept שמציין שאנחנו יכולים לקבל רק קובצי וידאו, ומאפיין capture שמציין שאנחנו רוצים לקבל את הקובץ ישירות מהמצלמה.

<input type="file" accept="video/*" capture />

השיטה הזו פועלת בכל הפלטפורמות. במחשב, המשתמש יתבקש להעלות קובץ ממערכת הקבצים (ללא התחשבות במאפיין capture). ב-Safari ב-iOS, תופעל אפליקציית המצלמה ותוכלו לצלם סרטון ולשלוח אותו בחזרה לדף האינטרנט. ב-Android, המשתמש יוכל לבחור באפליקציה שבה ירצה לצלם את הסרטון לפני שהוא ישלח אותו בחזרה לדף האינטרנט.

במכשירים ניידים רבים יש יותר ממצלמה אחת. אם יש לכם העדפה, תוכלו להגדיר את המאפיין capture לערך user אם אתם רוצים להשתמש במצלמה שמכוונת למשתמש, או לערך environment אם אתם רוצים להשתמש במצלמה שמכוונת החוצה.

<input type="file" accept="video/*" capture="user" />
<input type="file" accept="video/*" capture="environment" />

חשוב לזכור שזו רק הצעה – אם הדפדפן לא תומך באפשרות הזו או שסוג המצלמה שביקשת לא זמין, יכול להיות שהדפדפן יבחר מצלמה אחרת.

אחרי שהמשתמש מסיים את הצילום וחוזרים לאתר, צריך לקבל את נתוני הקובץ בדרך כלשהי. כדי לקבל גישה מהירה, אפשר לצרף אירוע onchange לרכיב הקלט ואז לקרוא את המאפיין files של אובייקט האירוע.

<input type="file" accept="video/*" capture="camera" id="recorder" />
<video id="player" controls></video>
<script>
  var recorder = document.getElementById('recorder');
  var player = document.getElementById('player');

  recorder.addEventListener('change', function (e) {
    var file = e.target.files[0];
    // Do something with the video file.
    player.src = URL.createObjectURL(file);
  });
</script>

אחרי שתקבלו גישה לקובץ, תוכלו לעשות איתו מה שתרצו. לדוגמה, תוכלו:

  • מחברים אותו ישירות לרכיב <video> כדי שתוכלו להפעיל אותו
  • מורידים אותה למכשיר של המשתמש
  • מעלים אותו לשרת על ידי צירוף ל-XMLHttpRequest
  • ציור המסגרות על קנבס והוספת מסננים

השימוש בשיטה של רכיב הקלט כדי לקבל גישה לנתוני הסרטון נפוץ מאוד, אבל זו האפשרות הכי פחות אטרקטיבית. אנחנו רוצים לקבל גישה למצלמה כדי לספק חוויה נעימה ישירות בדף.

גישה למצלמה באופן אינטראקטיבי

לדפדפנים מודרניים יכולה להיות גישה ישירה למצלמה, ומאפשרת לנו ליצור חוויות שמשתלבות באופן מלא בדף האינטרנט, והמשתמש אף פעם לא יוצא מהדפדפן.

קבלת גישה למצלמה

אנחנו יכולים לגשת ישירות למצלמה באמצעות ממשק API במפרט של WebRTC שנקרא getUserMedia(). getUserMedia() יבקש מהמשתמש גישה למיקרופונים ולמצלמות המחוברים.

אם הבקשה תאושר, ה-API יחזיר Stream שיכיל את הנתונים מהמצלמה או מהמיקרופון, ואז נוכל לצרף אותו לרכיב <video>, לצרף אותו לסטרימינג של WebRTC או לשמור אותו באמצעות ה-API של MediaRecorder.

כדי לקבל נתונים מהמצלמה, צריך להגדיר את video: true באובייקט האילוצים שמוענק ל-API‏ getUserMedia().

<video id="player" controls></video>
<script>
  var player = document.getElementById('player');

  var handleSuccess = function (stream) {
    player.srcObject = stream;
  };

  navigator.mediaDevices
    .getUserMedia({audio: true, video: true})
    .then(handleSuccess);
</script>

אם רוצים לבחור מצלמה מסוימת, קודם צריך למנות את המצלמות הזמינות.

navigator.mediaDevices.enumerateDevices().then((devices) => {
  devices = devices.filter((d) => d.kind === 'videoinput');
});

לאחר מכן תוכלו להעביר את deviceId שבו אתם רוצים להשתמש כשאתם קוראים ל-getUserMedia.

navigator.mediaDevices.getUserMedia({
  audio: true,
  video: {
    deviceId: devices[0].deviceId,
  },
});

לבד, המידע הזה לא כל כך שימושי. כל מה שאנחנו יכולים לעשות הוא לקחת את נתוני הסרטון ולהפעיל אותם.

גישה לנתונים הגולמיים מהמצלמה

כדי לגשת לנתוני הווידאו הגולמיים מהמצלמה, אפשר לצייר כל פריים ב-<canvas> ולבצע פעולות ישירות על הפיקסלים.

בקנבס דו-מימדי, אפשר להשתמש ב-method drawImage של ההקשר כדי לצייר את המסגרת הנוכחית של רכיב <video> על הקנבס.

context.drawImage(myVideoElement, 0, 0);

בקנבס של WebGL אפשר להשתמש ברכיב <video> כמקור למרקם.

gl.texImage2D(
  gl.TEXTURE_2D,
  0,
  gl.RGBA,
  gl.RGBA,
  gl.UNSIGNED_BYTE,
  myVideoElement,
);

חשוב לזכור שבכל מקרה, המערכת תשתמש בפריים הנוכחי של סרטון שפועל. כדי לעבד כמה פריימים, צריך לצייר מחדש את הסרטון על הלוח בכל פעם.

מידע נוסף זמין במאמר שלנו בנושא החלת אפקטים בזמן אמת על תמונות וסרטונים.

שמירת הנתונים מהמצלמה

הדרך הקלה ביותר לשמור את הנתונים מהמצלמה היא להשתמש ב-MediaRecorder API.

ה-API של MediaRecorder יקבל את המקור שנוצר על ידי getUserMedia, ולאחר מכן ישמור בהדרגה את הנתונים מהמקור ליעד המועדף.

<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
  let shouldStop = false;
  let stopped = false;
  const downloadLink = document.getElementById('download');
  const stopButton = document.getElementById('stop');

  stopButton.addEventListener('click', function() {
    shouldStop = true;
  })

  var handleSuccess = function(stream) {
    const options = {mimeType: 'video/webm'};
    const recordedChunks = [];
    const mediaRecorder = new MediaRecorder(stream, options);

    mediaRecorder.addEventListener('dataavailable', function(e) {
      if (e.data.size > 0) {
        recordedChunks.push(e.data);
      }

      if(shouldStop === true && stopped === false) {
        mediaRecorder.stop();
        stopped = true;
      }
    });

    mediaRecorder.addEventListener('stop', function() {
      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
      downloadLink.download = 'acetest.webm';
    });

    mediaRecorder.start();
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: true })
      .then(handleSuccess);
</script>

במקרה שלנו, אנחנו שומרים את הנתונים ישירות במערך, שאותו נוכל להפוך מאוחר יותר ל-Blob, שאפשר להשתמש בו כדי לשמור בשרת האינטרנט שלנו או ישירות באחסון במכשיר של המשתמש.

לבקש הרשאה לשימוש במצלמה בצורה אחראית

אם המשתמש לא העניק לאתר גישה למצלמה בעבר, ברגע שתפעילו את getUserMedia, הדפדפן יבקש מהמשתמש להעניק לאתר הרשאה למצלמה.

משתמשים לא אוהבים לקבל בקשות גישה למכשירים חזקים במחשב שלהם, ולרוב הם יחסמו את הבקשה או יתעלמו ממנה אם הם לא יבינו את ההקשר שבו היא נוצרה. מומלץ לבקש גישה למצלמה רק כשיש צורך בכך בפעם הראשונה. אחרי שהמשתמש ייתן גישה, לא תתבקשו לעשות זאת שוב. עם זאת, אם הוא ידחה את הגישה, לא תוכלו לקבל גישה שוב כדי לבקש מהמשתמש הרשאה.

איך משתמשים ב-Permissions API כדי לבדוק אם כבר יש לכם גישה

ה-API של getUserMedia לא מאפשר לכם לדעת אם כבר יש לכם גישה למצלמה. הבעיה היא שצריך לבקש גישה למצלמה כדי לספק ממשק משתמש נוח שיגרום למשתמש להעניק לכם גישה למצלמה.

אפשר לפתור את הבעיה הזו בדפדפנים מסוימים באמצעות Permission API. באמצעות API ‏navigator.permission אפשר לשלוח שאילתה לגבי המצב של היכולת לגשת לממשקי API ספציפיים, בלי צורך לבצע שוב את הבקשה.

כדי לבדוק אם יש לכם גישה למצלמה של המשתמש, אתם יכולים להעביר את הערך {name: 'camera'} לשיטת השאילתה, והיא תחזיר את אחת מהתוצאות הבאות:

  • granted – המשתמש העניק לכם גישה למצלמה בעבר.
  • prompt – המשתמש לא העניק לכם גישה, ותופיע בקשה כשתתקשרו אל getUserMedia.
  • denied – המערכת או המשתמש חסמו באופן מפורש את הגישה למצלמה, ולא תוכלו לגשת אליה.

עכשיו תוכלו לבדוק במהירות אם צריך לשנות את ממשק המשתמש כדי להתאים אותו לפעולות שהמשתמשים צריכים לבצע.

navigator.permissions.query({name: 'camera'}).then(function (result) {
  if (result.state == 'granted') {
  } else if (result.state == 'prompt') {
  } else if (result.state == 'denied') {
  }
  result.onchange = function () {};
});

משוב