تسجيل فيديو من المستخدم

Mat Scales

يمكن الآن للعديد من المتصفّحات الوصول إلى إدخال الفيديو والصوت من العميل. ومع ذلك، استنادًا إلى المتصفّح، قد تكون تجربة كاملة ديناميكية ومضمّنة ، أو قد يتم تفويضها إلى تطبيق آخر على جهاز المستخدم.

أسهل شيء يمكنك القيام به هو أن تطلب من المستخدم تقديم ملف مسجل مسبقًا. يمكنك إجراء ذلك من خلال إنشاء عنصر بسيط لinputملف وإضافة فلتر 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.
  • رسم الإطارات في لوحة وتطبيق فلاتر عليها

على الرغم من انتشار استخدام طريقة عناصر الإدخال للوصول إلى بيانات الفيديو في كل مكان، فهو الخيار الأقل جاذبية. نريد الوصول إلى الكاميرا وتقديم تجربة رائعة مباشرةً في الصفحة.

الوصول إلى الكاميرا بشكل تفاعلي

يمكن للمتصفّحات الحديثة الوصول مباشرةً إلى الكاميرا، ما يتيح لنا إنشاء تجربتَين مدمجتَين بالكامل مع صفحة الويب ولن يضطر المستخدم أبدًا إلى مغادرة المتصفّح.

الحصول على إذن بالوصول إلى الكاميرا

يمكننا الوصول إلى الكاميرا مباشرةً باستخدام واجهة برمجة تطبيقات في مواصفات WebRTC المُسمّاة getUserMedia(). سيطلب تطبيق getUserMedia() من المستخدم منح الإذن بالوصول إلى كاميراته وميكروفوناته المتصلة.

في حال نجاح العملية، ستُرجع واجهة برمجة التطبيقات Stream يحتوي على البيانات من الكاميرا أو الميكروفون، ويمكننا بعد ذلك إرفاقه بعنصر <video> أو إرفاقه ببث WebRTC أو حفظه باستخدام واجهة برمجة التطبيقات MediaRecorder.

للحصول على بيانات من الكاميرا، ما عليك سوى ضبط video: true في قيد العنصر الذي يتم تمريره إلى واجهة برمجة التطبيقات 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');
});

يمكنك بعد ذلك تمرير معرّف الجهاز الذي تريد استخدامه عند الاتصال برقم getUserMedia.

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

لا يُعدّ هذا الإجراء مفيدًا بحد ذاته. كل ما يمكننا فعله هو أخذ بيانات الفيديو وإعادة تشغيلها.

الوصول إلى البيانات الأولية من الكاميرا

للوصول إلى بيانات الفيديو الأوّلية من الكاميرا، يمكنك رسم كل إطار في <canvas> و التلاعب بالبكسل مباشرةً.

للحصول على لوحات ثنائية الأبعاد، يمكنك استخدام طريقة drawImage للسياق لرسم الإطار الحالي لعنصر <video> في اللوحة.

context.drawImage(myVideoElement, 0, 0);

عند استخدام لوحة WebGL في الرسم، يمكنك استخدام عنصر <video> كمصدر للزخرفة.

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

يُرجى العِلم أنّه في كلتا الحالتَين، سيتم استخدام الإطار الحالي للفيديو الذي يتم تشغيله. لمعالجة عدة لقطات، عليك إعادة رسم الفيديو على اللوحة في كل مرة.

يمكنك الاطّلاع على مزيد من المعلومات حول هذا الموضوع في مقالتنا حول تطبيق تأثيرات في الوقت الفعلي على الصور والفيديوهات.

حفظ البيانات من الكاميرا

إنّ أسهل طريقة لحفظ البيانات من الكاميرا هي استخدام واجهة برمجة التطبيقات MediaRecorder.

ستحفظ واجهة برمجة التطبيقات 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 للتحقّق مما إذا كان لديك إذن الوصول

لا تتيح لك واجهة برمجة التطبيقات getUserMedia API معرفة ما إذا كان بإمكانك الوصول إلى الكاميرا. يتسبب ذلك في مشكلة، فلتقديم واجهة مستخدم رائعة لحث المستخدم على منحك إذن الوصول إلى الكاميرا، عليك طلب إذن الوصول إلى الكاميرا.

يمكن حلّ هذه المشكلة في بعض المتصفّحات باستخدام واجهة برمجة التطبيقات Permission API. تسمح لك واجهة برمجة التطبيقات navigator.permission بالاستعلام عن حالة إمكانية الوصول إلى واجهات برمجة تطبيقات معيّنة بدون الحاجة إلى طلب ذلك مرة أخرى.

للاستعلام عما إذا كان بإمكانك الوصول إلى كاميرا المستخدم، يمكنك تمرير {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 () {};
});

ملاحظات