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

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

משוב