التقاط الصوت والفيديو بتنسيق HTML5

مقدمة

لطالما كان التقاط الصوت/الفيديو هو "الكأس المقدسة" لتطوير الويب. لسنوات عديدة، كان علينا الاعتماد على المكوّنات الإضافية للمتصفّح (Flash أو Silverlight) لإنجاز المهمة. هيا!

HTML5 to the rescue. قد لا يكون ذلك واضحًا، ولكن ظهور HTML5 أدى إلى زيادة كبيرة في الوصول إلى أجهزة الجهاز. الموقع الجغرافي (GPS) وواجهة برمجة التطبيقات Orientation (مقياس التسارع) وWebGL (وحدة معالجة الرسومات) وواجهة برمجة التطبيقات Web Audio (أجهزة الصوت) هي أمثلة مثالية على ذلك. هذه الميزات قوية بشكل لا يصدق، وتكشف عن واجهات برمجة تطبيقات JavaScript عالية المستوى التي تقع فوق إمكانيات الأجهزة الأساسية للنظام.

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

الطريق إلى getUserMedia()‎

إذا لم تكن على دراية بتاريخها، فإنّ الطريقة التي وصلنا بها إلى واجهة برمجة التطبيقات getUserMedia() هي قصة مثيرة للاهتمام.

تطوّرت عدة صيغ من "واجهات برمجة التطبيقات لالتقاط الوسائط" على مدار السنوات القليلة الماضية. أدرك العديد من الأشخاص الحاجة إلى الوصول إلى الأجهزة الأصلية على الويب، ولكن أدى ذلك إلى قيام الجميع وأمهاتهم بوضع مواصفات جديدة. أصبحت الأمور فوضوية لدرجة أن W3C قررت أخيرًا تشكيل مجموعة عمل. هدفها الوحيد؟ استكشِف عالمًا مليئًا بالغموض. تم تكليف مجموعة عمل سياسة واجهات برمجة التطبيقات للأجهزة (DAP) بدمج وتوحيد العدد الهائل من المقترحات.

سأحاول تلخيص ما حدث في عام 2011…

الجولة الأولى: التقاط الوسائط باستخدام HTML

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">

أمر رائع، أليس كذلك؟ يعجبني بشكل خاص أنّه يعيد استخدام إدخال ملف. من الناحية الدلالية، هذا منطقي جدًا. ما ينقص واجهة برمجة التطبيقات هذه هو القدرة على تنفيذ التأثيرات في الوقت الفعلي (مثل عرض بيانات كاميرا الويب المباشرة على <canvas> وتطبيق فلاتر WebGL). تسمح لك HTML Media Capture فقط بتسجيل ملف وسائط أو التقاط لقطة في الوقت المناسب.

الدعم:

  • متصفّح Android 3.0: أحد أولى عمليات التنفيذ يمكنك مشاهدة هذا الفيديو لمعرفة كيفية تنفيذ ذلك.
  • ‫Chrome لنظام Android (الإصدار 0.16)
  • Firefox Mobile 10.0
  • ‫iOS6 Safari وChrome (دعم جزئي)

الجولة 2: عنصر الجهاز

اعتقد الكثيرون أنّ HTML Media Capture كان محدودًا للغاية، لذا ظهرت مواصفات جديدة تتيح استخدام أي نوع من الأجهزة (المستقبلية). وليس من المستغرب أنّ التصميم تطلّب عنصرًا جديدًا، وهو العنصر <device>، الذي أصبح سلفًا لـ getUserMedia().

كان متصفّح Opera من بين أول المتصفّحات التي أنشأت عمليات تنفيذ أولية لالتقاط الفيديو استنادًا إلى العنصر <device>. بعد فترة وجيزة (في اليوم نفسه على وجه الدقة)، قرّرت مجموعة WhatWG إلغاء علامة <device> لصالح علامة أخرى واعدة، وهذه المرة واجهة برمجة تطبيقات JavaScript تُسمى navigator.getUserMedia(). وبعد أسبوع، أصدرت Opera إصدارات جديدة تتضمّن دعمًا لمواصفات getUserMedia() المعدَّلة. وفي وقت لاحق من ذلك العام، انضمّت Microsoft إلى المجموعة من خلال إصدار Lab لمتصفّح 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>". أعتقد أنّنا تخلّصنا من واجهة برمجة تطبيقات واحدة :) ومع ذلك، كانت واجهة برمجة التطبيقات <device> تتميّز بميزتَين رائعتَين: 1.) كانت دلالية، و2.) كانت قابلة للتوسيع بسهولة لتتوافق مع أكثر من مجرد أجهزة الصوت/الفيديو.

لا داعي لذلك. هذه الأمور تتغير بسرعة!

الجولة الثالثة: WebRTC

في النهاية، تم الاستغناء عن العنصر <device>.

تسارعت وتيرة العثور على واجهة برمجة تطبيقات مناسبة للتسجيل بفضل الجهود الأكبر التي بذلتها WebRTC (الاتصالات في الوقت الفعلي على الويب). ويشرف على هذه المواصفات فريق عمل WebRTC في اتحاد شبكة الويب العالمية (W3C). وتتوفّر عمليات تنفيذ من Google وOpera وMozilla وبعض الجهات الأخرى.

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

الدعم:

يتوفّر 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>

حسنًا. إذًا، ما الذي يحدث هنا؟ يُعدّ التقاط الوسائط مثالاً رائعًا على واجهات برمجة التطبيقات الجديدة في HTML5 التي تعمل معًا. ويعمل هذا العنصر مع عناصر HTML5 الأخرى، مثل <audio> و<video>. يُرجى ملاحظة أنّنا لا نضبط سمة src أو نُدرج عناصر <source> في العنصر <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);

لمزيد من الإعدادات، يُرجى الاطّلاع على واجهة برمجة التطبيقات للقيود.

اختيار مصدر وسائط

تطلب طريقة 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:

مربّع حوار الأذونات في Chrome
مربّع حوار الأذونات في Chrome

توفير عنصر احتياطي

بالنسبة إلى المستخدمين الذين لا تتوفّر لديهم ميزة navigator.mediaDevices.getUserMedia()، يمكنهم استخدام ملف فيديو حالي إذا لم تكن واجهة برمجة التطبيقات متاحة و/أو تعذّر طلب البيانات لسبب ما:

if (!navigator.mediaDevices?.getUserMedia) {
  video.src = "fallbackvideo.webm";
} else {
  const stream = await navigator.mediaDevices.getUserMedia({ video: true });
  video.srcObject = stream;
}